Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / cluster.py @ 1c4910f7

History | View | Annotate | Download (136.3 kB)

1 7352d33b Thomas Thrainer
#
2 7352d33b Thomas Thrainer
#
3 7352d33b Thomas Thrainer
4 8a5d326f Jose A. Lopes
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 copy
25 7352d33b Thomas Thrainer
import itertools
26 7352d33b Thomas Thrainer
import logging
27 7352d33b Thomas Thrainer
import operator
28 7352d33b Thomas Thrainer
import os
29 7352d33b Thomas Thrainer
import re
30 7352d33b Thomas Thrainer
import time
31 7352d33b Thomas Thrainer
32 7352d33b Thomas Thrainer
from ganeti import compat
33 7352d33b Thomas Thrainer
from ganeti import constants
34 7352d33b Thomas Thrainer
from ganeti import errors
35 7352d33b Thomas Thrainer
from ganeti import hypervisor
36 7352d33b Thomas Thrainer
from ganeti import locking
37 7352d33b Thomas Thrainer
from ganeti import masterd
38 7352d33b Thomas Thrainer
from ganeti import netutils
39 7352d33b Thomas Thrainer
from ganeti import objects
40 7352d33b Thomas Thrainer
from ganeti import opcodes
41 7352d33b Thomas Thrainer
from ganeti import pathutils
42 7352d33b Thomas Thrainer
from ganeti import query
43 4869595d Petr Pudlak
import ganeti.rpc.node as rpc
44 7352d33b Thomas Thrainer
from ganeti import runtime
45 7352d33b Thomas Thrainer
from ganeti import ssh
46 7352d33b Thomas Thrainer
from ganeti import uidpool
47 7352d33b Thomas Thrainer
from ganeti import utils
48 7352d33b Thomas Thrainer
from ganeti import vcluster
49 7352d33b Thomas Thrainer
50 5eacbcae Thomas Thrainer
from ganeti.cmdlib.base import NoHooksLU, QueryBase, LogicalUnit, \
51 7352d33b Thomas Thrainer
  ResultWithJobs
52 5eacbcae Thomas Thrainer
from ganeti.cmdlib.common import ShareAll, RunPostHook, \
53 5eacbcae Thomas Thrainer
  ComputeAncillaryFiles, RedistributeAncillaryFiles, UploadHelper, \
54 5eacbcae Thomas Thrainer
  GetWantedInstances, MergeAndVerifyHvState, MergeAndVerifyDiskState, \
55 5eacbcae Thomas Thrainer
  GetUpdatedIPolicy, ComputeNewInstanceViolations, GetUpdatedParams, \
56 5eacbcae Thomas Thrainer
  CheckOSParams, CheckHVParams, AdjustCandidatePool, CheckNodePVs, \
57 4e771a95 Helga Velroyen
  ComputeIPolicyInstanceViolation, AnnotateDiskParams, SupportsOob, \
58 294254b1 Raffa Santi
  CheckIpolicyVsDiskTemplates, CheckDiskAccessModeValidity, \
59 2ff6426b Jose A. Lopes
  CheckDiskAccessModeConsistency, CreateNewClientCert, \
60 4b75f8a4 Jose A. Lopes
  AddInstanceCommunicationNetworkOp, ConnectInstanceCommunicationNetworkOp
61 7352d33b Thomas Thrainer
62 7352d33b Thomas Thrainer
import ganeti.masterd.instance
63 7352d33b Thomas Thrainer
64 7352d33b Thomas Thrainer
65 b3cc1646 Helga Velroyen
def _UpdateMasterClientCert(
66 b3cc1646 Helga Velroyen
    lu, master_uuid, cluster, feedback_fn,
67 b3cc1646 Helga Velroyen
    client_cert=pathutils.NODED_CLIENT_CERT_FILE,
68 b3cc1646 Helga Velroyen
    client_cert_tmp=pathutils.NODED_CLIENT_CERT_FILE_TMP):
69 b3cc1646 Helga Velroyen
  """Renews the master's client certificate and propagates the config.
70 b3cc1646 Helga Velroyen

71 b3cc1646 Helga Velroyen
  @type lu: C{LogicalUnit}
72 b3cc1646 Helga Velroyen
  @param lu: the logical unit holding the config
73 b3cc1646 Helga Velroyen
  @type master_uuid: string
74 b3cc1646 Helga Velroyen
  @param master_uuid: the master node's UUID
75 b3cc1646 Helga Velroyen
  @type cluster: C{objects.Cluster}
76 b3cc1646 Helga Velroyen
  @param cluster: the cluster's configuration
77 b3cc1646 Helga Velroyen
  @type feedback_fn: function
78 b3cc1646 Helga Velroyen
  @param feedback_fn: feedback functions for config updates
79 b3cc1646 Helga Velroyen
  @type client_cert: string
80 b3cc1646 Helga Velroyen
  @param client_cert: the path of the client certificate
81 b3cc1646 Helga Velroyen
  @type client_cert_tmp: string
82 b3cc1646 Helga Velroyen
  @param client_cert_tmp: the temporary path of the client certificate
83 b3cc1646 Helga Velroyen
  @rtype: string
84 b3cc1646 Helga Velroyen
  @return: the digest of the newly created client certificate
85 b3cc1646 Helga Velroyen

86 b3cc1646 Helga Velroyen
  """
87 b3cc1646 Helga Velroyen
  client_digest = CreateNewClientCert(lu, master_uuid, filename=client_cert_tmp)
88 b3cc1646 Helga Velroyen
  utils.AddNodeToCandidateCerts(master_uuid, client_digest,
89 b3cc1646 Helga Velroyen
                                cluster.candidate_certs)
90 b3cc1646 Helga Velroyen
  # This triggers an update of the config and distribution of it with the old
91 b3cc1646 Helga Velroyen
  # SSL certificate
92 b3cc1646 Helga Velroyen
  lu.cfg.Update(cluster, feedback_fn)
93 b3cc1646 Helga Velroyen
94 b3cc1646 Helga Velroyen
  utils.RemoveFile(client_cert)
95 b3cc1646 Helga Velroyen
  utils.RenameFile(client_cert_tmp, client_cert)
96 b3cc1646 Helga Velroyen
  return client_digest
97 b3cc1646 Helga Velroyen
98 b3cc1646 Helga Velroyen
99 b3cc1646 Helga Velroyen
class LUClusterRenewCrypto(NoHooksLU):
100 b3cc1646 Helga Velroyen
  """Renew the cluster's crypto tokens.
101 b3cc1646 Helga Velroyen

102 b3cc1646 Helga Velroyen
  Note that most of this operation is done in gnt_cluster.py, this LU only
103 b3cc1646 Helga Velroyen
  takes care of the renewal of the client SSL certificates.
104 b3cc1646 Helga Velroyen

105 b3cc1646 Helga Velroyen
  """
106 b3cc1646 Helga Velroyen
  def Exec(self, feedback_fn):
107 b3cc1646 Helga Velroyen
    master_uuid = self.cfg.GetMasterNode()
108 b3cc1646 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
109 b3cc1646 Helga Velroyen
110 b3cc1646 Helga Velroyen
    server_digest = utils.GetCertificateDigest(
111 b3cc1646 Helga Velroyen
      cert_filename=pathutils.NODED_CERT_FILE)
112 b3cc1646 Helga Velroyen
    utils.AddNodeToCandidateCerts("%s-SERVER" % master_uuid,
113 b3cc1646 Helga Velroyen
                                  server_digest,
114 b3cc1646 Helga Velroyen
                                  cluster.candidate_certs)
115 da27bc7d Helga Velroyen
    try:
116 da27bc7d Helga Velroyen
      old_master_digest = utils.GetCertificateDigest(
117 da27bc7d Helga Velroyen
        cert_filename=pathutils.NODED_CLIENT_CERT_FILE)
118 da27bc7d Helga Velroyen
      utils.AddNodeToCandidateCerts("%s-OLDMASTER" % master_uuid,
119 da27bc7d Helga Velroyen
                                    old_master_digest,
120 da27bc7d Helga Velroyen
                                    cluster.candidate_certs)
121 da27bc7d Helga Velroyen
    except IOError:
122 da27bc7d Helga Velroyen
      logging.info("No old certificate available.")
123 da27bc7d Helga Velroyen
124 b3cc1646 Helga Velroyen
    new_master_digest = _UpdateMasterClientCert(self, master_uuid, cluster,
125 b3cc1646 Helga Velroyen
                                                feedback_fn)
126 b3cc1646 Helga Velroyen
127 3e8a6f39 Helga Velroyen
    utils.AddNodeToCandidateCerts(master_uuid,
128 3e8a6f39 Helga Velroyen
                                  new_master_digest,
129 3e8a6f39 Helga Velroyen
                                  cluster.candidate_certs)
130 b3cc1646 Helga Velroyen
    nodes = self.cfg.GetAllNodesInfo()
131 b3cc1646 Helga Velroyen
    for (node_uuid, node_info) in nodes.items():
132 b3cc1646 Helga Velroyen
      if node_uuid != master_uuid:
133 b3cc1646 Helga Velroyen
        new_digest = CreateNewClientCert(self, node_uuid)
134 b3cc1646 Helga Velroyen
        if node_info.master_candidate:
135 3e8a6f39 Helga Velroyen
          utils.AddNodeToCandidateCerts(node_uuid,
136 3e8a6f39 Helga Velroyen
                                        new_digest,
137 3e8a6f39 Helga Velroyen
                                        cluster.candidate_certs)
138 3e8a6f39 Helga Velroyen
    utils.RemoveNodeFromCandidateCerts("%s-SERVER" % master_uuid,
139 3e8a6f39 Helga Velroyen
                                       cluster.candidate_certs)
140 3e8a6f39 Helga Velroyen
    utils.RemoveNodeFromCandidateCerts("%s-OLDMASTER" % master_uuid,
141 3e8a6f39 Helga Velroyen
                                       cluster.candidate_certs)
142 b3cc1646 Helga Velroyen
    # Trigger another update of the config now with the new master cert
143 b3cc1646 Helga Velroyen
    self.cfg.Update(cluster, feedback_fn)
144 b3cc1646 Helga Velroyen
145 b3cc1646 Helga Velroyen
146 7352d33b Thomas Thrainer
class LUClusterActivateMasterIp(NoHooksLU):
147 7352d33b Thomas Thrainer
  """Activate the master IP on the master node.
148 7352d33b Thomas Thrainer

149 7352d33b Thomas Thrainer
  """
150 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
151 7352d33b Thomas Thrainer
    """Activate the master IP.
152 7352d33b Thomas Thrainer

153 7352d33b Thomas Thrainer
    """
154 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
155 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
156 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_activate_master_ip(master_params.uuid,
157 7352d33b Thomas Thrainer
                                                   master_params, ems)
158 7352d33b Thomas Thrainer
    result.Raise("Could not activate the master IP")
159 7352d33b Thomas Thrainer
160 7352d33b Thomas Thrainer
161 7352d33b Thomas Thrainer
class LUClusterDeactivateMasterIp(NoHooksLU):
162 7352d33b Thomas Thrainer
  """Deactivate the master IP on the master node.
163 7352d33b Thomas Thrainer

164 7352d33b Thomas Thrainer
  """
165 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
166 7352d33b Thomas Thrainer
    """Deactivate the master IP.
167 7352d33b Thomas Thrainer

168 7352d33b Thomas Thrainer
    """
169 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
170 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
171 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
172 7352d33b Thomas Thrainer
                                                     master_params, ems)
173 7352d33b Thomas Thrainer
    result.Raise("Could not deactivate the master IP")
174 7352d33b Thomas Thrainer
175 7352d33b Thomas Thrainer
176 7352d33b Thomas Thrainer
class LUClusterConfigQuery(NoHooksLU):
177 7352d33b Thomas Thrainer
  """Return configuration values.
178 7352d33b Thomas Thrainer

179 7352d33b Thomas Thrainer
  """
180 7352d33b Thomas Thrainer
  REQ_BGL = False
181 7352d33b Thomas Thrainer
182 7352d33b Thomas Thrainer
  def CheckArguments(self):
183 5eacbcae Thomas Thrainer
    self.cq = ClusterQuery(None, self.op.output_fields, False)
184 7352d33b Thomas Thrainer
185 7352d33b Thomas Thrainer
  def ExpandNames(self):
186 7352d33b Thomas Thrainer
    self.cq.ExpandNames(self)
187 7352d33b Thomas Thrainer
188 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
189 7352d33b Thomas Thrainer
    self.cq.DeclareLocks(self, level)
190 7352d33b Thomas Thrainer
191 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
192 7352d33b Thomas Thrainer
    result = self.cq.OldStyleQuery(self)
193 7352d33b Thomas Thrainer
194 7352d33b Thomas Thrainer
    assert len(result) == 1
195 7352d33b Thomas Thrainer
196 7352d33b Thomas Thrainer
    return result[0]
197 7352d33b Thomas Thrainer
198 7352d33b Thomas Thrainer
199 7352d33b Thomas Thrainer
class LUClusterDestroy(LogicalUnit):
200 7352d33b Thomas Thrainer
  """Logical unit for destroying the cluster.
201 7352d33b Thomas Thrainer

202 7352d33b Thomas Thrainer
  """
203 7352d33b Thomas Thrainer
  HPATH = "cluster-destroy"
204 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
205 7352d33b Thomas Thrainer
206 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
207 7352d33b Thomas Thrainer
    """Build hooks env.
208 7352d33b Thomas Thrainer

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

217 7352d33b Thomas Thrainer
    """
218 7352d33b Thomas Thrainer
    return ([], [])
219 7352d33b Thomas Thrainer
220 7352d33b Thomas Thrainer
  def CheckPrereq(self):
221 7352d33b Thomas Thrainer
    """Check prerequisites.
222 7352d33b Thomas Thrainer

223 7352d33b Thomas Thrainer
    This checks whether the cluster is empty.
224 7352d33b Thomas Thrainer

225 7352d33b Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
226 7352d33b Thomas Thrainer

227 7352d33b Thomas Thrainer
    """
228 7352d33b Thomas Thrainer
    master = self.cfg.GetMasterNode()
229 7352d33b Thomas Thrainer
230 7352d33b Thomas Thrainer
    nodelist = self.cfg.GetNodeList()
231 7352d33b Thomas Thrainer
    if len(nodelist) != 1 or nodelist[0] != master:
232 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("There are still %d node(s) in"
233 7352d33b Thomas Thrainer
                                 " this cluster." % (len(nodelist) - 1),
234 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
235 7352d33b Thomas Thrainer
    instancelist = self.cfg.GetInstanceList()
236 7352d33b Thomas Thrainer
    if instancelist:
237 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("There are still %d instance(s) in"
238 7352d33b Thomas Thrainer
                                 " this cluster." % len(instancelist),
239 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
240 7352d33b Thomas Thrainer
241 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
242 7352d33b Thomas Thrainer
    """Destroys the cluster.
243 7352d33b Thomas Thrainer

244 7352d33b Thomas Thrainer
    """
245 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
246 7352d33b Thomas Thrainer
247 7352d33b Thomas Thrainer
    # Run post hooks on master node before it's removed
248 1c3231aa Thomas Thrainer
    RunPostHook(self, self.cfg.GetNodeName(master_params.uuid))
249 7352d33b Thomas Thrainer
250 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
251 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
252 7352d33b Thomas Thrainer
                                                     master_params, ems)
253 c7dd65be Klaus Aehlig
    result.Warn("Error disabling the master IP address", self.LogWarning)
254 1c3231aa Thomas Thrainer
    return master_params.uuid
255 7352d33b Thomas Thrainer
256 7352d33b Thomas Thrainer
257 7352d33b Thomas Thrainer
class LUClusterPostInit(LogicalUnit):
258 7352d33b Thomas Thrainer
  """Logical unit for running hooks after cluster initialization.
259 7352d33b Thomas Thrainer

260 7352d33b Thomas Thrainer
  """
261 7352d33b Thomas Thrainer
  HPATH = "cluster-init"
262 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
263 7352d33b Thomas Thrainer
264 25ea5b98 Sebastian Gebhard
  def CheckArguments(self):
265 25ea5b98 Sebastian Gebhard
    self.master_uuid = self.cfg.GetMasterNode()
266 25ea5b98 Sebastian Gebhard
    self.master_ndparams = self.cfg.GetNdParams(self.cfg.GetMasterNodeInfo())
267 25ea5b98 Sebastian Gebhard
268 25ea5b98 Sebastian Gebhard
    # TODO: When Issue 584 is solved, and None is properly parsed when used
269 25ea5b98 Sebastian Gebhard
    # as a default value, ndparams.get(.., None) can be changed to
270 25ea5b98 Sebastian Gebhard
    # ndparams[..] to access the values directly
271 25ea5b98 Sebastian Gebhard
272 25ea5b98 Sebastian Gebhard
    # OpenvSwitch: Warn user if link is missing
273 25ea5b98 Sebastian Gebhard
    if (self.master_ndparams[constants.ND_OVS] and not
274 25ea5b98 Sebastian Gebhard
        self.master_ndparams.get(constants.ND_OVS_LINK, None)):
275 25ea5b98 Sebastian Gebhard
      self.LogInfo("No physical interface for OpenvSwitch was given."
276 25ea5b98 Sebastian Gebhard
                   " OpenvSwitch will not have an outside connection. This"
277 25ea5b98 Sebastian Gebhard
                   " might not be what you want.")
278 25ea5b98 Sebastian Gebhard
279 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
280 7352d33b Thomas Thrainer
    """Build hooks env.
281 7352d33b Thomas Thrainer

282 7352d33b Thomas Thrainer
    """
283 7352d33b Thomas Thrainer
    return {
284 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
285 7352d33b Thomas Thrainer
      }
286 7352d33b Thomas Thrainer
287 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
288 7352d33b Thomas Thrainer
    """Build hooks nodes.
289 7352d33b Thomas Thrainer

290 7352d33b Thomas Thrainer
    """
291 7352d33b Thomas Thrainer
    return ([], [self.cfg.GetMasterNode()])
292 7352d33b Thomas Thrainer
293 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
294 25ea5b98 Sebastian Gebhard
    """Create and configure Open vSwitch
295 7352d33b Thomas Thrainer

296 7352d33b Thomas Thrainer
    """
297 25ea5b98 Sebastian Gebhard
    if self.master_ndparams[constants.ND_OVS]:
298 25ea5b98 Sebastian Gebhard
      result = self.rpc.call_node_configure_ovs(
299 25ea5b98 Sebastian Gebhard
                 self.master_uuid,
300 25ea5b98 Sebastian Gebhard
                 self.master_ndparams[constants.ND_OVS_NAME],
301 25ea5b98 Sebastian Gebhard
                 self.master_ndparams.get(constants.ND_OVS_LINK, None))
302 25ea5b98 Sebastian Gebhard
      result.Raise("Could not successully configure Open vSwitch")
303 5b6f9e35 Helga Velroyen
304 b3cc1646 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
305 b3cc1646 Helga Velroyen
    _UpdateMasterClientCert(self, self.master_uuid, cluster, feedback_fn)
306 5b6f9e35 Helga Velroyen
307 7352d33b Thomas Thrainer
    return True
308 7352d33b Thomas Thrainer
309 7352d33b Thomas Thrainer
310 5eacbcae Thomas Thrainer
class ClusterQuery(QueryBase):
311 7352d33b Thomas Thrainer
  FIELDS = query.CLUSTER_FIELDS
312 7352d33b Thomas Thrainer
313 7352d33b Thomas Thrainer
  #: Do not sort (there is only one item)
314 7352d33b Thomas Thrainer
  SORT_FIELD = None
315 7352d33b Thomas Thrainer
316 7352d33b Thomas Thrainer
  def ExpandNames(self, lu):
317 7352d33b Thomas Thrainer
    lu.needed_locks = {}
318 7352d33b Thomas Thrainer
319 7352d33b Thomas Thrainer
    # The following variables interact with _QueryBase._GetNames
320 7352d33b Thomas Thrainer
    self.wanted = locking.ALL_SET
321 7352d33b Thomas Thrainer
    self.do_locking = self.use_locking
322 7352d33b Thomas Thrainer
323 7352d33b Thomas Thrainer
    if self.do_locking:
324 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Can not use locking for cluster queries",
325 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
326 7352d33b Thomas Thrainer
327 7352d33b Thomas Thrainer
  def DeclareLocks(self, lu, level):
328 7352d33b Thomas Thrainer
    pass
329 7352d33b Thomas Thrainer
330 7352d33b Thomas Thrainer
  def _GetQueryData(self, lu):
331 7352d33b Thomas Thrainer
    """Computes the list of nodes and their attributes.
332 7352d33b Thomas Thrainer

333 7352d33b Thomas Thrainer
    """
334 7352d33b Thomas Thrainer
    # Locking is not used
335 7352d33b Thomas Thrainer
    assert not (compat.any(lu.glm.is_owned(level)
336 7352d33b Thomas Thrainer
                           for level in locking.LEVELS
337 7352d33b Thomas Thrainer
                           if level != locking.LEVEL_CLUSTER) or
338 7352d33b Thomas Thrainer
                self.do_locking or self.use_locking)
339 7352d33b Thomas Thrainer
340 7352d33b Thomas Thrainer
    if query.CQ_CONFIG in self.requested_data:
341 7352d33b Thomas Thrainer
      cluster = lu.cfg.GetClusterInfo()
342 1c3231aa Thomas Thrainer
      nodes = lu.cfg.GetAllNodesInfo()
343 7352d33b Thomas Thrainer
    else:
344 7352d33b Thomas Thrainer
      cluster = NotImplemented
345 1c3231aa Thomas Thrainer
      nodes = NotImplemented
346 7352d33b Thomas Thrainer
347 7352d33b Thomas Thrainer
    if query.CQ_QUEUE_DRAINED in self.requested_data:
348 7352d33b Thomas Thrainer
      drain_flag = os.path.exists(pathutils.JOB_QUEUE_DRAIN_FILE)
349 7352d33b Thomas Thrainer
    else:
350 7352d33b Thomas Thrainer
      drain_flag = NotImplemented
351 7352d33b Thomas Thrainer
352 7352d33b Thomas Thrainer
    if query.CQ_WATCHER_PAUSE in self.requested_data:
353 1c3231aa Thomas Thrainer
      master_node_uuid = lu.cfg.GetMasterNode()
354 7352d33b Thomas Thrainer
355 1c3231aa Thomas Thrainer
      result = lu.rpc.call_get_watcher_pause(master_node_uuid)
356 7352d33b Thomas Thrainer
      result.Raise("Can't retrieve watcher pause from master node '%s'" %
357 1c3231aa Thomas Thrainer
                   lu.cfg.GetMasterNodeName())
358 7352d33b Thomas Thrainer
359 7352d33b Thomas Thrainer
      watcher_pause = result.payload
360 7352d33b Thomas Thrainer
    else:
361 7352d33b Thomas Thrainer
      watcher_pause = NotImplemented
362 7352d33b Thomas Thrainer
363 1c3231aa Thomas Thrainer
    return query.ClusterQueryData(cluster, nodes, drain_flag, watcher_pause)
364 7352d33b Thomas Thrainer
365 7352d33b Thomas Thrainer
366 7352d33b Thomas Thrainer
class LUClusterQuery(NoHooksLU):
367 7352d33b Thomas Thrainer
  """Query cluster configuration.
368 7352d33b Thomas Thrainer

369 7352d33b Thomas Thrainer
  """
370 7352d33b Thomas Thrainer
  REQ_BGL = False
371 7352d33b Thomas Thrainer
372 7352d33b Thomas Thrainer
  def ExpandNames(self):
373 7352d33b Thomas Thrainer
    self.needed_locks = {}
374 7352d33b Thomas Thrainer
375 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
376 7352d33b Thomas Thrainer
    """Return cluster config.
377 7352d33b Thomas Thrainer

378 7352d33b Thomas Thrainer
    """
379 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
380 7352d33b Thomas Thrainer
    os_hvp = {}
381 7352d33b Thomas Thrainer
382 7352d33b Thomas Thrainer
    # Filter just for enabled hypervisors
383 7352d33b Thomas Thrainer
    for os_name, hv_dict in cluster.os_hvp.items():
384 7352d33b Thomas Thrainer
      os_hvp[os_name] = {}
385 7352d33b Thomas Thrainer
      for hv_name, hv_params in hv_dict.items():
386 7352d33b Thomas Thrainer
        if hv_name in cluster.enabled_hypervisors:
387 7352d33b Thomas Thrainer
          os_hvp[os_name][hv_name] = hv_params
388 7352d33b Thomas Thrainer
389 7352d33b Thomas Thrainer
    # Convert ip_family to ip_version
390 7352d33b Thomas Thrainer
    primary_ip_version = constants.IP4_VERSION
391 7352d33b Thomas Thrainer
    if cluster.primary_ip_family == netutils.IP6Address.family:
392 7352d33b Thomas Thrainer
      primary_ip_version = constants.IP6_VERSION
393 7352d33b Thomas Thrainer
394 7352d33b Thomas Thrainer
    result = {
395 7352d33b Thomas Thrainer
      "software_version": constants.RELEASE_VERSION,
396 7352d33b Thomas Thrainer
      "protocol_version": constants.PROTOCOL_VERSION,
397 7352d33b Thomas Thrainer
      "config_version": constants.CONFIG_VERSION,
398 7352d33b Thomas Thrainer
      "os_api_version": max(constants.OS_API_VERSIONS),
399 7352d33b Thomas Thrainer
      "export_version": constants.EXPORT_VERSION,
400 026f444f Thomas Thrainer
      "vcs_version": constants.VCS_VERSION,
401 7352d33b Thomas Thrainer
      "architecture": runtime.GetArchInfo(),
402 7352d33b Thomas Thrainer
      "name": cluster.cluster_name,
403 1c3231aa Thomas Thrainer
      "master": self.cfg.GetMasterNodeName(),
404 7352d33b Thomas Thrainer
      "default_hypervisor": cluster.primary_hypervisor,
405 7352d33b Thomas Thrainer
      "enabled_hypervisors": cluster.enabled_hypervisors,
406 7352d33b Thomas Thrainer
      "hvparams": dict([(hypervisor_name, cluster.hvparams[hypervisor_name])
407 7352d33b Thomas Thrainer
                        for hypervisor_name in cluster.enabled_hypervisors]),
408 7352d33b Thomas Thrainer
      "os_hvp": os_hvp,
409 7352d33b Thomas Thrainer
      "beparams": cluster.beparams,
410 7352d33b Thomas Thrainer
      "osparams": cluster.osparams,
411 7352d33b Thomas Thrainer
      "ipolicy": cluster.ipolicy,
412 7352d33b Thomas Thrainer
      "nicparams": cluster.nicparams,
413 7352d33b Thomas Thrainer
      "ndparams": cluster.ndparams,
414 7352d33b Thomas Thrainer
      "diskparams": cluster.diskparams,
415 7352d33b Thomas Thrainer
      "candidate_pool_size": cluster.candidate_pool_size,
416 178ad717 Klaus Aehlig
      "max_running_jobs": cluster.max_running_jobs,
417 353bd75b Dimitris Bliablias
      "mac_prefix": cluster.mac_prefix,
418 7352d33b Thomas Thrainer
      "master_netdev": cluster.master_netdev,
419 7352d33b Thomas Thrainer
      "master_netmask": cluster.master_netmask,
420 7352d33b Thomas Thrainer
      "use_external_mip_script": cluster.use_external_mip_script,
421 7352d33b Thomas Thrainer
      "volume_group_name": cluster.volume_group_name,
422 7352d33b Thomas Thrainer
      "drbd_usermode_helper": cluster.drbd_usermode_helper,
423 7352d33b Thomas Thrainer
      "file_storage_dir": cluster.file_storage_dir,
424 7352d33b Thomas Thrainer
      "shared_file_storage_dir": cluster.shared_file_storage_dir,
425 7352d33b Thomas Thrainer
      "maintain_node_health": cluster.maintain_node_health,
426 7352d33b Thomas Thrainer
      "ctime": cluster.ctime,
427 7352d33b Thomas Thrainer
      "mtime": cluster.mtime,
428 7352d33b Thomas Thrainer
      "uuid": cluster.uuid,
429 7352d33b Thomas Thrainer
      "tags": list(cluster.GetTags()),
430 7352d33b Thomas Thrainer
      "uid_pool": cluster.uid_pool,
431 7352d33b Thomas Thrainer
      "default_iallocator": cluster.default_iallocator,
432 0359e5d0 Spyros Trigazis
      "default_iallocator_params": cluster.default_iallocator_params,
433 7352d33b Thomas Thrainer
      "reserved_lvs": cluster.reserved_lvs,
434 7352d33b Thomas Thrainer
      "primary_ip_version": primary_ip_version,
435 7352d33b Thomas Thrainer
      "prealloc_wipe_disks": cluster.prealloc_wipe_disks,
436 7352d33b Thomas Thrainer
      "hidden_os": cluster.hidden_os,
437 7352d33b Thomas Thrainer
      "blacklisted_os": cluster.blacklisted_os,
438 fe782deb Helga Velroyen
      "enabled_disk_templates": cluster.enabled_disk_templates,
439 8a5d326f Jose A. Lopes
      "instance_communication_network": cluster.instance_communication_network,
440 7352d33b Thomas Thrainer
      }
441 7352d33b Thomas Thrainer
442 7352d33b Thomas Thrainer
    return result
443 7352d33b Thomas Thrainer
444 7352d33b Thomas Thrainer
445 7352d33b Thomas Thrainer
class LUClusterRedistConf(NoHooksLU):
446 7352d33b Thomas Thrainer
  """Force the redistribution of cluster configuration.
447 7352d33b Thomas Thrainer

448 7352d33b Thomas Thrainer
  This is a very simple LU.
449 7352d33b Thomas Thrainer

450 7352d33b Thomas Thrainer
  """
451 7352d33b Thomas Thrainer
  REQ_BGL = False
452 7352d33b Thomas Thrainer
453 7352d33b Thomas Thrainer
  def ExpandNames(self):
454 7352d33b Thomas Thrainer
    self.needed_locks = {
455 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
456 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
457 7352d33b Thomas Thrainer
    }
458 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
459 7352d33b Thomas Thrainer
460 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
461 7352d33b Thomas Thrainer
    """Redistribute the configuration.
462 7352d33b Thomas Thrainer

463 7352d33b Thomas Thrainer
    """
464 7352d33b Thomas Thrainer
    self.cfg.Update(self.cfg.GetClusterInfo(), feedback_fn)
465 5eacbcae Thomas Thrainer
    RedistributeAncillaryFiles(self)
466 7352d33b Thomas Thrainer
467 7352d33b Thomas Thrainer
468 7352d33b Thomas Thrainer
class LUClusterRename(LogicalUnit):
469 7352d33b Thomas Thrainer
  """Rename the cluster.
470 7352d33b Thomas Thrainer

471 7352d33b Thomas Thrainer
  """
472 7352d33b Thomas Thrainer
  HPATH = "cluster-rename"
473 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
474 7352d33b Thomas Thrainer
475 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
476 7352d33b Thomas Thrainer
    """Build hooks env.
477 7352d33b Thomas Thrainer

478 7352d33b Thomas Thrainer
    """
479 7352d33b Thomas Thrainer
    return {
480 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
481 7352d33b Thomas Thrainer
      "NEW_NAME": self.op.name,
482 7352d33b Thomas Thrainer
      }
483 7352d33b Thomas Thrainer
484 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
485 7352d33b Thomas Thrainer
    """Build hooks nodes.
486 7352d33b Thomas Thrainer

487 7352d33b Thomas Thrainer
    """
488 7352d33b Thomas Thrainer
    return ([self.cfg.GetMasterNode()], self.cfg.GetNodeList())
489 7352d33b Thomas Thrainer
490 7352d33b Thomas Thrainer
  def CheckPrereq(self):
491 7352d33b Thomas Thrainer
    """Verify that the passed name is a valid one.
492 7352d33b Thomas Thrainer

493 7352d33b Thomas Thrainer
    """
494 7352d33b Thomas Thrainer
    hostname = netutils.GetHostname(name=self.op.name,
495 7352d33b Thomas Thrainer
                                    family=self.cfg.GetPrimaryIPFamily())
496 7352d33b Thomas Thrainer
497 7352d33b Thomas Thrainer
    new_name = hostname.name
498 7352d33b Thomas Thrainer
    self.ip = new_ip = hostname.ip
499 7352d33b Thomas Thrainer
    old_name = self.cfg.GetClusterName()
500 7352d33b Thomas Thrainer
    old_ip = self.cfg.GetMasterIP()
501 7352d33b Thomas Thrainer
    if new_name == old_name and new_ip == old_ip:
502 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Neither the name nor the IP address of the"
503 7352d33b Thomas Thrainer
                                 " cluster has changed",
504 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
505 7352d33b Thomas Thrainer
    if new_ip != old_ip:
506 7352d33b Thomas Thrainer
      if netutils.TcpPing(new_ip, constants.DEFAULT_NODED_PORT):
507 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("The given cluster IP address (%s) is"
508 7352d33b Thomas Thrainer
                                   " reachable on the network" %
509 7352d33b Thomas Thrainer
                                   new_ip, errors.ECODE_NOTUNIQUE)
510 7352d33b Thomas Thrainer
511 7352d33b Thomas Thrainer
    self.op.name = new_name
512 7352d33b Thomas Thrainer
513 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
514 7352d33b Thomas Thrainer
    """Rename the cluster.
515 7352d33b Thomas Thrainer

516 7352d33b Thomas Thrainer
    """
517 7352d33b Thomas Thrainer
    clustername = self.op.name
518 7352d33b Thomas Thrainer
    new_ip = self.ip
519 7352d33b Thomas Thrainer
520 7352d33b Thomas Thrainer
    # shutdown the master IP
521 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
522 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
523 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
524 7352d33b Thomas Thrainer
                                                     master_params, ems)
525 7352d33b Thomas Thrainer
    result.Raise("Could not disable the master role")
526 7352d33b Thomas Thrainer
527 7352d33b Thomas Thrainer
    try:
528 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
529 7352d33b Thomas Thrainer
      cluster.cluster_name = clustername
530 7352d33b Thomas Thrainer
      cluster.master_ip = new_ip
531 7352d33b Thomas Thrainer
      self.cfg.Update(cluster, feedback_fn)
532 7352d33b Thomas Thrainer
533 7352d33b Thomas Thrainer
      # update the known hosts file
534 7352d33b Thomas Thrainer
      ssh.WriteKnownHostsFile(self.cfg, pathutils.SSH_KNOWN_HOSTS_FILE)
535 7352d33b Thomas Thrainer
      node_list = self.cfg.GetOnlineNodeList()
536 7352d33b Thomas Thrainer
      try:
537 1c3231aa Thomas Thrainer
        node_list.remove(master_params.uuid)
538 7352d33b Thomas Thrainer
      except ValueError:
539 7352d33b Thomas Thrainer
        pass
540 5eacbcae Thomas Thrainer
      UploadHelper(self, node_list, pathutils.SSH_KNOWN_HOSTS_FILE)
541 7352d33b Thomas Thrainer
    finally:
542 7352d33b Thomas Thrainer
      master_params.ip = new_ip
543 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
544 7352d33b Thomas Thrainer
                                                     master_params, ems)
545 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master role on the master,"
546 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
547 7352d33b Thomas Thrainer
548 7352d33b Thomas Thrainer
    return clustername
549 7352d33b Thomas Thrainer
550 7352d33b Thomas Thrainer
551 7352d33b Thomas Thrainer
class LUClusterRepairDiskSizes(NoHooksLU):
552 7352d33b Thomas Thrainer
  """Verifies the cluster disks sizes.
553 7352d33b Thomas Thrainer

554 7352d33b Thomas Thrainer
  """
555 7352d33b Thomas Thrainer
  REQ_BGL = False
556 7352d33b Thomas Thrainer
557 7352d33b Thomas Thrainer
  def ExpandNames(self):
558 7352d33b Thomas Thrainer
    if self.op.instances:
559 da4a52a3 Thomas Thrainer
      (_, self.wanted_names) = GetWantedInstances(self, self.op.instances)
560 7352d33b Thomas Thrainer
      # Not getting the node allocation lock as only a specific set of
561 7352d33b Thomas Thrainer
      # instances (and their nodes) is going to be acquired
562 7352d33b Thomas Thrainer
      self.needed_locks = {
563 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_RES: [],
564 7352d33b Thomas Thrainer
        locking.LEVEL_INSTANCE: self.wanted_names,
565 7352d33b Thomas Thrainer
        }
566 7352d33b Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
567 7352d33b Thomas Thrainer
    else:
568 7352d33b Thomas Thrainer
      self.wanted_names = None
569 7352d33b Thomas Thrainer
      self.needed_locks = {
570 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_RES: locking.ALL_SET,
571 7352d33b Thomas Thrainer
        locking.LEVEL_INSTANCE: locking.ALL_SET,
572 7352d33b Thomas Thrainer
573 7352d33b Thomas Thrainer
        # This opcode is acquires the node locks for all instances
574 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
575 7352d33b Thomas Thrainer
        }
576 7352d33b Thomas Thrainer
577 7352d33b Thomas Thrainer
    self.share_locks = {
578 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_RES: 1,
579 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: 0,
580 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: 1,
581 7352d33b Thomas Thrainer
      }
582 7352d33b Thomas Thrainer
583 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
584 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE_RES and self.wanted_names is not None:
585 7352d33b Thomas Thrainer
      self._LockInstancesNodes(primary_only=True, level=level)
586 7352d33b Thomas Thrainer
587 7352d33b Thomas Thrainer
  def CheckPrereq(self):
588 7352d33b Thomas Thrainer
    """Check prerequisites.
589 7352d33b Thomas Thrainer

590 7352d33b Thomas Thrainer
    This only checks the optional instance list against the existing names.
591 7352d33b Thomas Thrainer

592 7352d33b Thomas Thrainer
    """
593 7352d33b Thomas Thrainer
    if self.wanted_names is None:
594 7352d33b Thomas Thrainer
      self.wanted_names = self.owned_locks(locking.LEVEL_INSTANCE)
595 7352d33b Thomas Thrainer
596 7352d33b Thomas Thrainer
    self.wanted_instances = \
597 da4a52a3 Thomas Thrainer
        map(compat.snd, self.cfg.GetMultiInstanceInfoByName(self.wanted_names))
598 7352d33b Thomas Thrainer
599 7352d33b Thomas Thrainer
  def _EnsureChildSizes(self, disk):
600 7352d33b Thomas Thrainer
    """Ensure children of the disk have the needed disk size.
601 7352d33b Thomas Thrainer

602 7352d33b Thomas Thrainer
    This is valid mainly for DRBD8 and fixes an issue where the
603 7352d33b Thomas Thrainer
    children have smaller disk size.
604 7352d33b Thomas Thrainer

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

607 7352d33b Thomas Thrainer
    """
608 cd3b4ff4 Helga Velroyen
    if disk.dev_type == constants.DT_DRBD8:
609 7352d33b Thomas Thrainer
      assert disk.children, "Empty children for DRBD8?"
610 7352d33b Thomas Thrainer
      fchild = disk.children[0]
611 7352d33b Thomas Thrainer
      mismatch = fchild.size < disk.size
612 7352d33b Thomas Thrainer
      if mismatch:
613 7352d33b Thomas Thrainer
        self.LogInfo("Child disk has size %d, parent %d, fixing",
614 7352d33b Thomas Thrainer
                     fchild.size, disk.size)
615 7352d33b Thomas Thrainer
        fchild.size = disk.size
616 7352d33b Thomas Thrainer
617 7352d33b Thomas Thrainer
      # and we recurse on this child only, not on the metadev
618 7352d33b Thomas Thrainer
      return self._EnsureChildSizes(fchild) or mismatch
619 7352d33b Thomas Thrainer
    else:
620 7352d33b Thomas Thrainer
      return False
621 7352d33b Thomas Thrainer
622 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
623 7352d33b Thomas Thrainer
    """Verify the size of cluster disks.
624 7352d33b Thomas Thrainer

625 7352d33b Thomas Thrainer
    """
626 7352d33b Thomas Thrainer
    # TODO: check child disks too
627 7352d33b Thomas Thrainer
    # TODO: check differences in size between primary/secondary nodes
628 7352d33b Thomas Thrainer
    per_node_disks = {}
629 7352d33b Thomas Thrainer
    for instance in self.wanted_instances:
630 7352d33b Thomas Thrainer
      pnode = instance.primary_node
631 7352d33b Thomas Thrainer
      if pnode not in per_node_disks:
632 7352d33b Thomas Thrainer
        per_node_disks[pnode] = []
633 7352d33b Thomas Thrainer
      for idx, disk in enumerate(instance.disks):
634 7352d33b Thomas Thrainer
        per_node_disks[pnode].append((instance, idx, disk))
635 7352d33b Thomas Thrainer
636 7352d33b Thomas Thrainer
    assert not (frozenset(per_node_disks.keys()) -
637 7352d33b Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE_RES)), \
638 7352d33b Thomas Thrainer
      "Not owning correct locks"
639 7352d33b Thomas Thrainer
    assert not self.owned_locks(locking.LEVEL_NODE)
640 7352d33b Thomas Thrainer
641 1c3231aa Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg,
642 1c3231aa Thomas Thrainer
                                               per_node_disks.keys())
643 40d93e3b Bernardo Dal Seno
644 7352d33b Thomas Thrainer
    changed = []
645 1c3231aa Thomas Thrainer
    for node_uuid, dskl in per_node_disks.items():
646 0c3d9c7c Thomas Thrainer
      if not dskl:
647 0c3d9c7c Thomas Thrainer
        # no disks on the node
648 0c3d9c7c Thomas Thrainer
        continue
649 0c3d9c7c Thomas Thrainer
650 d66acf3d Thomas Thrainer
      newl = [([v[2].Copy()], v[0]) for v in dskl]
651 1c3231aa Thomas Thrainer
      node_name = self.cfg.GetNodeName(node_uuid)
652 1c3231aa Thomas Thrainer
      result = self.rpc.call_blockdev_getdimensions(node_uuid, newl)
653 7352d33b Thomas Thrainer
      if result.fail_msg:
654 6ef8077e Bernardo Dal Seno
        self.LogWarning("Failure in blockdev_getdimensions call to node"
655 1c3231aa Thomas Thrainer
                        " %s, ignoring", node_name)
656 7352d33b Thomas Thrainer
        continue
657 7352d33b Thomas Thrainer
      if len(result.payload) != len(dskl):
658 7352d33b Thomas Thrainer
        logging.warning("Invalid result from node %s: len(dksl)=%d,"
659 1c3231aa Thomas Thrainer
                        " result.payload=%s", node_name, len(dskl),
660 1c3231aa Thomas Thrainer
                        result.payload)
661 7352d33b Thomas Thrainer
        self.LogWarning("Invalid result from node %s, ignoring node results",
662 1c3231aa Thomas Thrainer
                        node_name)
663 7352d33b Thomas Thrainer
        continue
664 6ef8077e Bernardo Dal Seno
      for ((instance, idx, disk), dimensions) in zip(dskl, result.payload):
665 6ef8077e Bernardo Dal Seno
        if dimensions is None:
666 7352d33b Thomas Thrainer
          self.LogWarning("Disk %d of instance %s did not return size"
667 7352d33b Thomas Thrainer
                          " information, ignoring", idx, instance.name)
668 7352d33b Thomas Thrainer
          continue
669 6ef8077e Bernardo Dal Seno
        if not isinstance(dimensions, (tuple, list)):
670 6ef8077e Bernardo Dal Seno
          self.LogWarning("Disk %d of instance %s did not return valid"
671 6ef8077e Bernardo Dal Seno
                          " dimension information, ignoring", idx,
672 6ef8077e Bernardo Dal Seno
                          instance.name)
673 6ef8077e Bernardo Dal Seno
          continue
674 40d93e3b Bernardo Dal Seno
        (size, spindles) = dimensions
675 7352d33b Thomas Thrainer
        if not isinstance(size, (int, long)):
676 7352d33b Thomas Thrainer
          self.LogWarning("Disk %d of instance %s did not return valid"
677 7352d33b Thomas Thrainer
                          " size information, ignoring", idx, instance.name)
678 7352d33b Thomas Thrainer
          continue
679 7352d33b Thomas Thrainer
        size = size >> 20
680 7352d33b Thomas Thrainer
        if size != disk.size:
681 7352d33b Thomas Thrainer
          self.LogInfo("Disk %d of instance %s has mismatched size,"
682 7352d33b Thomas Thrainer
                       " correcting: recorded %d, actual %d", idx,
683 7352d33b Thomas Thrainer
                       instance.name, disk.size, size)
684 7352d33b Thomas Thrainer
          disk.size = size
685 7352d33b Thomas Thrainer
          self.cfg.Update(instance, feedback_fn)
686 40d93e3b Bernardo Dal Seno
          changed.append((instance.name, idx, "size", size))
687 1c3231aa Thomas Thrainer
        if es_flags[node_uuid]:
688 40d93e3b Bernardo Dal Seno
          if spindles is None:
689 40d93e3b Bernardo Dal Seno
            self.LogWarning("Disk %d of instance %s did not return valid"
690 40d93e3b Bernardo Dal Seno
                            " spindles information, ignoring", idx,
691 40d93e3b Bernardo Dal Seno
                            instance.name)
692 40d93e3b Bernardo Dal Seno
          elif disk.spindles is None or disk.spindles != spindles:
693 40d93e3b Bernardo Dal Seno
            self.LogInfo("Disk %d of instance %s has mismatched spindles,"
694 40d93e3b Bernardo Dal Seno
                         " correcting: recorded %s, actual %s",
695 40d93e3b Bernardo Dal Seno
                         idx, instance.name, disk.spindles, spindles)
696 40d93e3b Bernardo Dal Seno
            disk.spindles = spindles
697 40d93e3b Bernardo Dal Seno
            self.cfg.Update(instance, feedback_fn)
698 40d93e3b Bernardo Dal Seno
            changed.append((instance.name, idx, "spindles", disk.spindles))
699 7352d33b Thomas Thrainer
        if self._EnsureChildSizes(disk):
700 7352d33b Thomas Thrainer
          self.cfg.Update(instance, feedback_fn)
701 40d93e3b Bernardo Dal Seno
          changed.append((instance.name, idx, "size", disk.size))
702 7352d33b Thomas Thrainer
    return changed
703 7352d33b Thomas Thrainer
704 7352d33b Thomas Thrainer
705 7352d33b Thomas Thrainer
def _ValidateNetmask(cfg, netmask):
706 7352d33b Thomas Thrainer
  """Checks if a netmask is valid.
707 7352d33b Thomas Thrainer

708 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
709 9808764a Jose A. Lopes
  @param cfg: cluster configuration
710 7352d33b Thomas Thrainer
  @type netmask: int
711 9808764a Jose A. Lopes
  @param netmask: netmask to be verified
712 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the validation fails
713 7352d33b Thomas Thrainer

714 7352d33b Thomas Thrainer
  """
715 7352d33b Thomas Thrainer
  ip_family = cfg.GetPrimaryIPFamily()
716 7352d33b Thomas Thrainer
  try:
717 7352d33b Thomas Thrainer
    ipcls = netutils.IPAddress.GetClassFromIpFamily(ip_family)
718 7352d33b Thomas Thrainer
  except errors.ProgrammerError:
719 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("Invalid primary ip family: %s." %
720 7352d33b Thomas Thrainer
                               ip_family, errors.ECODE_INVAL)
721 7352d33b Thomas Thrainer
  if not ipcls.ValidateNetmask(netmask):
722 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("CIDR netmask (%s) not valid" %
723 7352d33b Thomas Thrainer
                               (netmask), errors.ECODE_INVAL)
724 7352d33b Thomas Thrainer
725 7352d33b Thomas Thrainer
726 e8b5640e Helga Velroyen
def CheckFileBasedStoragePathVsEnabledDiskTemplates(
727 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates,
728 e8b5640e Helga Velroyen
    file_disk_template):
729 e8b5640e Helga Velroyen
  """Checks whether the given file-based storage directory is acceptable.
730 e8b5640e Helga Velroyen

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

733 3039e2dc Helga Velroyen
  @type logging_warn_fn: function
734 3039e2dc Helga Velroyen
  @param logging_warn_fn: function which accepts a string and logs it
735 3039e2dc Helga Velroyen
  @type file_storage_dir: string
736 3039e2dc Helga Velroyen
  @param file_storage_dir: the directory to be used for file-based instances
737 3039e2dc Helga Velroyen
  @type enabled_disk_templates: list of string
738 3039e2dc Helga Velroyen
  @param enabled_disk_templates: the list of enabled disk templates
739 e8b5640e Helga Velroyen
  @type file_disk_template: string
740 e8b5640e Helga Velroyen
  @param file_disk_template: the file-based disk template for which the
741 e8b5640e Helga Velroyen
      path should be checked
742 3039e2dc Helga Velroyen

743 3039e2dc Helga Velroyen
  """
744 5a904197 Santi Raffa
  assert (file_disk_template in utils.storage.GetDiskTemplatesOfStorageTypes(
745 5a904197 Santi Raffa
            constants.ST_FILE, constants.ST_SHARED_FILE
746 5a904197 Santi Raffa
         ))
747 e8b5640e Helga Velroyen
  file_storage_enabled = file_disk_template in enabled_disk_templates
748 3039e2dc Helga Velroyen
  if file_storage_dir is not None:
749 3039e2dc Helga Velroyen
    if file_storage_dir == "":
750 3039e2dc Helga Velroyen
      if file_storage_enabled:
751 e8b5640e Helga Velroyen
        raise errors.OpPrereqError(
752 e8b5640e Helga Velroyen
            "Unsetting the '%s' storage directory while having '%s' storage"
753 e8b5640e Helga Velroyen
            " enabled is not permitted." %
754 e8b5640e Helga Velroyen
            (file_disk_template, file_disk_template))
755 3039e2dc Helga Velroyen
    else:
756 3039e2dc Helga Velroyen
      if not file_storage_enabled:
757 e8b5640e Helga Velroyen
        logging_warn_fn(
758 e8b5640e Helga Velroyen
            "Specified a %s storage directory, although %s storage is not"
759 e8b5640e Helga Velroyen
            " enabled." % (file_disk_template, file_disk_template))
760 3039e2dc Helga Velroyen
  else:
761 e8b5640e Helga Velroyen
    raise errors.ProgrammerError("Received %s storage dir with value"
762 e8b5640e Helga Velroyen
                                 " 'None'." % file_disk_template)
763 e8b5640e Helga Velroyen
764 e8b5640e Helga Velroyen
765 e8b5640e Helga Velroyen
def CheckFileStoragePathVsEnabledDiskTemplates(
766 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates):
767 e8b5640e Helga Velroyen
  """Checks whether the given file storage directory is acceptable.
768 e8b5640e Helga Velroyen

769 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
770 e8b5640e Helga Velroyen

771 e8b5640e Helga Velroyen
  """
772 e8b5640e Helga Velroyen
  CheckFileBasedStoragePathVsEnabledDiskTemplates(
773 e8b5640e Helga Velroyen
      logging_warn_fn, file_storage_dir, enabled_disk_templates,
774 e8b5640e Helga Velroyen
      constants.DT_FILE)
775 e8b5640e Helga Velroyen
776 e8b5640e Helga Velroyen
777 e8b5640e Helga Velroyen
def CheckSharedFileStoragePathVsEnabledDiskTemplates(
778 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates):
779 e8b5640e Helga Velroyen
  """Checks whether the given shared file storage directory is acceptable.
780 e8b5640e Helga Velroyen

781 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
782 e8b5640e Helga Velroyen

783 e8b5640e Helga Velroyen
  """
784 e8b5640e Helga Velroyen
  CheckFileBasedStoragePathVsEnabledDiskTemplates(
785 e8b5640e Helga Velroyen
      logging_warn_fn, file_storage_dir, enabled_disk_templates,
786 e8b5640e Helga Velroyen
      constants.DT_SHARED_FILE)
787 3039e2dc Helga Velroyen
788 3039e2dc Helga Velroyen
789 7352d33b Thomas Thrainer
class LUClusterSetParams(LogicalUnit):
790 7352d33b Thomas Thrainer
  """Change the parameters of the cluster.
791 7352d33b Thomas Thrainer

792 7352d33b Thomas Thrainer
  """
793 7352d33b Thomas Thrainer
  HPATH = "cluster-modify"
794 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
795 7352d33b Thomas Thrainer
  REQ_BGL = False
796 7352d33b Thomas Thrainer
797 7352d33b Thomas Thrainer
  def CheckArguments(self):
798 7352d33b Thomas Thrainer
    """Check parameters
799 7352d33b Thomas Thrainer

800 7352d33b Thomas Thrainer
    """
801 7352d33b Thomas Thrainer
    if self.op.uid_pool:
802 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.uid_pool)
803 7352d33b Thomas Thrainer
804 7352d33b Thomas Thrainer
    if self.op.add_uids:
805 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.add_uids)
806 7352d33b Thomas Thrainer
807 7352d33b Thomas Thrainer
    if self.op.remove_uids:
808 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.remove_uids)
809 7352d33b Thomas Thrainer
810 0cffcdb1 Dimitris Bliablias
    if self.op.mac_prefix:
811 0cffcdb1 Dimitris Bliablias
      self.op.mac_prefix = \
812 0cffcdb1 Dimitris Bliablias
          utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
813 0cffcdb1 Dimitris Bliablias
814 7352d33b Thomas Thrainer
    if self.op.master_netmask is not None:
815 7352d33b Thomas Thrainer
      _ValidateNetmask(self.cfg, self.op.master_netmask)
816 7352d33b Thomas Thrainer
817 7352d33b Thomas Thrainer
    if self.op.diskparams:
818 7352d33b Thomas Thrainer
      for dt_params in self.op.diskparams.values():
819 7352d33b Thomas Thrainer
        utils.ForceDictType(dt_params, constants.DISK_DT_TYPES)
820 7352d33b Thomas Thrainer
      try:
821 7352d33b Thomas Thrainer
        utils.VerifyDictOptions(self.op.diskparams, constants.DISK_DT_DEFAULTS)
822 294254b1 Raffa Santi
        CheckDiskAccessModeValidity(self.op.diskparams)
823 7352d33b Thomas Thrainer
      except errors.OpPrereqError, err:
824 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
825 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
826 7352d33b Thomas Thrainer
827 7352d33b Thomas Thrainer
  def ExpandNames(self):
828 7352d33b Thomas Thrainer
    # FIXME: in the future maybe other cluster params won't require checking on
829 7352d33b Thomas Thrainer
    # all nodes to be modified.
830 7352d33b Thomas Thrainer
    # FIXME: This opcode changes cluster-wide settings. Is acquiring all
831 7352d33b Thomas Thrainer
    # resource locks the right thing, shouldn't it be the BGL instead?
832 7352d33b Thomas Thrainer
    self.needed_locks = {
833 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
834 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: locking.ALL_SET,
835 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
836 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
837 7352d33b Thomas Thrainer
    }
838 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
839 7352d33b Thomas Thrainer
840 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
841 7352d33b Thomas Thrainer
    """Build hooks env.
842 7352d33b Thomas Thrainer

843 7352d33b Thomas Thrainer
    """
844 7352d33b Thomas Thrainer
    return {
845 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
846 7352d33b Thomas Thrainer
      "NEW_VG_NAME": self.op.vg_name,
847 7352d33b Thomas Thrainer
      }
848 7352d33b Thomas Thrainer
849 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
850 7352d33b Thomas Thrainer
    """Build hooks nodes.
851 7352d33b Thomas Thrainer

852 7352d33b Thomas Thrainer
    """
853 7352d33b Thomas Thrainer
    mn = self.cfg.GetMasterNode()
854 7352d33b Thomas Thrainer
    return ([mn], [mn])
855 7352d33b Thomas Thrainer
856 1c3231aa Thomas Thrainer
  def _CheckVgName(self, node_uuids, enabled_disk_templates,
857 1bb99a33 Bernardo Dal Seno
                   new_enabled_disk_templates):
858 1bb99a33 Bernardo Dal Seno
    """Check the consistency of the vg name on all nodes and in case it gets
859 1bb99a33 Bernardo Dal Seno
       unset whether there are instances still using it.
860 7352d33b Thomas Thrainer

861 7352d33b Thomas Thrainer
    """
862 c89eb67d Helga Velroyen
    lvm_is_enabled = utils.IsLvmEnabled(enabled_disk_templates)
863 c89eb67d Helga Velroyen
    lvm_gets_enabled = utils.LvmGetsEnabled(enabled_disk_templates,
864 c89eb67d Helga Velroyen
                                            new_enabled_disk_templates)
865 c89eb67d Helga Velroyen
    current_vg_name = self.cfg.GetVGName()
866 c89eb67d Helga Velroyen
867 c89eb67d Helga Velroyen
    if self.op.vg_name == '':
868 c89eb67d Helga Velroyen
      if lvm_is_enabled:
869 c89eb67d Helga Velroyen
        raise errors.OpPrereqError("Cannot unset volume group if lvm-based"
870 c89eb67d Helga Velroyen
                                   " disk templates are or get enabled.")
871 c89eb67d Helga Velroyen
872 c89eb67d Helga Velroyen
    if self.op.vg_name is None:
873 c89eb67d Helga Velroyen
      if current_vg_name is None and lvm_is_enabled:
874 c89eb67d Helga Velroyen
        raise errors.OpPrereqError("Please specify a volume group when"
875 c89eb67d Helga Velroyen
                                   " enabling lvm-based disk-templates.")
876 c89eb67d Helga Velroyen
877 7352d33b Thomas Thrainer
    if self.op.vg_name is not None and not self.op.vg_name:
878 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_PLAIN):
879 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable lvm storage while lvm-based"
880 7352d33b Thomas Thrainer
                                   " instances exist", errors.ECODE_INVAL)
881 7352d33b Thomas Thrainer
882 c89eb67d Helga Velroyen
    if (self.op.vg_name is not None and lvm_is_enabled) or \
883 c89eb67d Helga Velroyen
        (self.cfg.GetVGName() is not None and lvm_gets_enabled):
884 1c3231aa Thomas Thrainer
      self._CheckVgNameOnNodes(node_uuids)
885 1bb99a33 Bernardo Dal Seno
886 1c3231aa Thomas Thrainer
  def _CheckVgNameOnNodes(self, node_uuids):
887 1bb99a33 Bernardo Dal Seno
    """Check the status of the volume group on each node.
888 1bb99a33 Bernardo Dal Seno

889 1bb99a33 Bernardo Dal Seno
    """
890 1c3231aa Thomas Thrainer
    vglist = self.rpc.call_vg_list(node_uuids)
891 1c3231aa Thomas Thrainer
    for node_uuid in node_uuids:
892 1c3231aa Thomas Thrainer
      msg = vglist[node_uuid].fail_msg
893 1bb99a33 Bernardo Dal Seno
      if msg:
894 1bb99a33 Bernardo Dal Seno
        # ignoring down node
895 1bb99a33 Bernardo Dal Seno
        self.LogWarning("Error while gathering data on node %s"
896 1c3231aa Thomas Thrainer
                        " (ignoring node): %s",
897 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
898 1bb99a33 Bernardo Dal Seno
        continue
899 1c3231aa Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist[node_uuid].payload,
900 1bb99a33 Bernardo Dal Seno
                                            self.op.vg_name,
901 1bb99a33 Bernardo Dal Seno
                                            constants.MIN_VG_SIZE)
902 1bb99a33 Bernardo Dal Seno
      if vgstatus:
903 1bb99a33 Bernardo Dal Seno
        raise errors.OpPrereqError("Error on node '%s': %s" %
904 1c3231aa Thomas Thrainer
                                   (self.cfg.GetNodeName(node_uuid), vgstatus),
905 1c3231aa Thomas Thrainer
                                   errors.ECODE_ENVIRON)
906 1bb99a33 Bernardo Dal Seno
907 c89eb67d Helga Velroyen
  @staticmethod
908 87e23f2d Helga Velroyen
  def _GetDiskTemplateSetsInner(op_enabled_disk_templates,
909 87e23f2d Helga Velroyen
                                old_enabled_disk_templates):
910 6e513917 Helga Velroyen
    """Computes three sets of disk templates.
911 6e513917 Helga Velroyen

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

914 1bb99a33 Bernardo Dal Seno
    """
915 1bb99a33 Bernardo Dal Seno
    enabled_disk_templates = None
916 1bb99a33 Bernardo Dal Seno
    new_enabled_disk_templates = []
917 6e513917 Helga Velroyen
    disabled_disk_templates = []
918 c89eb67d Helga Velroyen
    if op_enabled_disk_templates:
919 c89eb67d Helga Velroyen
      enabled_disk_templates = op_enabled_disk_templates
920 1bb99a33 Bernardo Dal Seno
      new_enabled_disk_templates = \
921 5808df30 Helga Velroyen
        list(set(enabled_disk_templates)
922 5808df30 Helga Velroyen
             - set(old_enabled_disk_templates))
923 6e513917 Helga Velroyen
      disabled_disk_templates = \
924 5808df30 Helga Velroyen
        list(set(old_enabled_disk_templates)
925 5808df30 Helga Velroyen
             - set(enabled_disk_templates))
926 1bb99a33 Bernardo Dal Seno
    else:
927 c89eb67d Helga Velroyen
      enabled_disk_templates = old_enabled_disk_templates
928 6e513917 Helga Velroyen
    return (enabled_disk_templates, new_enabled_disk_templates,
929 6e513917 Helga Velroyen
            disabled_disk_templates)
930 1bb99a33 Bernardo Dal Seno
931 87e23f2d Helga Velroyen
  def _GetDiskTemplateSets(self, cluster):
932 6e513917 Helga Velroyen
    """Computes three sets of disk templates.
933 6e513917 Helga Velroyen

934 6e513917 Helga Velroyen
    The three sets are:
935 6e513917 Helga Velroyen
      - disk templates that will be enabled after this operation (no matter if
936 6e513917 Helga Velroyen
        they were enabled before or not)
937 6e513917 Helga Velroyen
      - disk templates that get enabled by this operation (thus haven't been
938 6e513917 Helga Velroyen
        enabled before.)
939 6e513917 Helga Velroyen
      - disk templates that get disabled by this operation
940 c89eb67d Helga Velroyen

941 c89eb67d Helga Velroyen
    """
942 87e23f2d Helga Velroyen
    return self._GetDiskTemplateSetsInner(self.op.enabled_disk_templates,
943 87e23f2d Helga Velroyen
                                          cluster.enabled_disk_templates)
944 c89eb67d Helga Velroyen
945 33a6464e Helga Velroyen
  def _CheckIpolicy(self, cluster, enabled_disk_templates):
946 1532b078 Helga Velroyen
    """Checks the ipolicy.
947 1532b078 Helga Velroyen

948 1532b078 Helga Velroyen
    @type cluster: C{objects.Cluster}
949 1532b078 Helga Velroyen
    @param cluster: the cluster's configuration
950 33a6464e Helga Velroyen
    @type enabled_disk_templates: list of string
951 33a6464e Helga Velroyen
    @param enabled_disk_templates: list of (possibly newly) enabled disk
952 33a6464e Helga Velroyen
      templates
953 1532b078 Helga Velroyen

954 1532b078 Helga Velroyen
    """
955 33a6464e Helga Velroyen
    # FIXME: write unit tests for this
956 1532b078 Helga Velroyen
    if self.op.ipolicy:
957 1532b078 Helga Velroyen
      self.new_ipolicy = GetUpdatedIPolicy(cluster.ipolicy, self.op.ipolicy,
958 1532b078 Helga Velroyen
                                           group_policy=False)
959 1532b078 Helga Velroyen
960 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(self.new_ipolicy,
961 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
962 33a6464e Helga Velroyen
963 1532b078 Helga Velroyen
      all_instances = self.cfg.GetAllInstancesInfo().values()
964 1532b078 Helga Velroyen
      violations = set()
965 1532b078 Helga Velroyen
      for group in self.cfg.GetAllNodeGroupsInfo().values():
966 1532b078 Helga Velroyen
        instances = frozenset([inst for inst in all_instances
967 1532b078 Helga Velroyen
                               if compat.any(nuuid in group.members
968 1532b078 Helga Velroyen
                                             for nuuid in inst.all_nodes)])
969 1532b078 Helga Velroyen
        new_ipolicy = objects.FillIPolicy(self.new_ipolicy, group.ipolicy)
970 1532b078 Helga Velroyen
        ipol = masterd.instance.CalculateGroupIPolicy(cluster, group)
971 1532b078 Helga Velroyen
        new = ComputeNewInstanceViolations(ipol, new_ipolicy, instances,
972 1532b078 Helga Velroyen
                                           self.cfg)
973 1532b078 Helga Velroyen
        if new:
974 1532b078 Helga Velroyen
          violations.update(new)
975 1532b078 Helga Velroyen
976 1532b078 Helga Velroyen
      if violations:
977 1532b078 Helga Velroyen
        self.LogWarning("After the ipolicy change the following instances"
978 1532b078 Helga Velroyen
                        " violate them: %s",
979 1532b078 Helga Velroyen
                        utils.CommaJoin(utils.NiceSort(violations)))
980 33a6464e Helga Velroyen
    else:
981 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(cluster.ipolicy,
982 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
983 1532b078 Helga Velroyen
984 31ccfc0e Helga Velroyen
  def _CheckDrbdHelperOnNodes(self, drbd_helper, node_uuids):
985 31ccfc0e Helga Velroyen
    """Checks whether the set DRBD helper actually exists on the nodes.
986 31ccfc0e Helga Velroyen

987 31ccfc0e Helga Velroyen
    @type drbd_helper: string
988 31ccfc0e Helga Velroyen
    @param drbd_helper: path of the drbd usermode helper binary
989 31ccfc0e Helga Velroyen
    @type node_uuids: list of strings
990 31ccfc0e Helga Velroyen
    @param node_uuids: list of node UUIDs to check for the helper
991 31ccfc0e Helga Velroyen

992 31ccfc0e Helga Velroyen
    """
993 31ccfc0e Helga Velroyen
    # checks given drbd helper on all nodes
994 31ccfc0e Helga Velroyen
    helpers = self.rpc.call_drbd_helper(node_uuids)
995 31ccfc0e Helga Velroyen
    for (_, ninfo) in self.cfg.GetMultiNodeInfo(node_uuids):
996 31ccfc0e Helga Velroyen
      if ninfo.offline:
997 31ccfc0e Helga Velroyen
        self.LogInfo("Not checking drbd helper on offline node %s",
998 31ccfc0e Helga Velroyen
                     ninfo.name)
999 31ccfc0e Helga Velroyen
        continue
1000 31ccfc0e Helga Velroyen
      msg = helpers[ninfo.uuid].fail_msg
1001 31ccfc0e Helga Velroyen
      if msg:
1002 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Error checking drbd helper on node"
1003 31ccfc0e Helga Velroyen
                                   " '%s': %s" % (ninfo.name, msg),
1004 31ccfc0e Helga Velroyen
                                   errors.ECODE_ENVIRON)
1005 31ccfc0e Helga Velroyen
      node_helper = helpers[ninfo.uuid].payload
1006 31ccfc0e Helga Velroyen
      if node_helper != drbd_helper:
1007 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Error on node '%s': drbd helper is %s" %
1008 31ccfc0e Helga Velroyen
                                   (ninfo.name, node_helper),
1009 31ccfc0e Helga Velroyen
                                   errors.ECODE_ENVIRON)
1010 31ccfc0e Helga Velroyen
1011 31ccfc0e Helga Velroyen
  def _CheckDrbdHelper(self, node_uuids, drbd_enabled, drbd_gets_enabled):
1012 7c577910 Helga Velroyen
    """Check the DRBD usermode helper.
1013 1bb99a33 Bernardo Dal Seno

1014 7c577910 Helga Velroyen
    @type node_uuids: list of strings
1015 7c577910 Helga Velroyen
    @param node_uuids: a list of nodes' UUIDs
1016 31ccfc0e Helga Velroyen
    @type drbd_enabled: boolean
1017 31ccfc0e Helga Velroyen
    @param drbd_enabled: whether DRBD will be enabled after this operation
1018 31ccfc0e Helga Velroyen
      (no matter if it was disabled before or not)
1019 31ccfc0e Helga Velroyen
    @type drbd_gets_enabled: boolen
1020 31ccfc0e Helga Velroyen
    @param drbd_gets_enabled: true if DRBD was disabled before this
1021 31ccfc0e Helga Velroyen
      operation, but will be enabled afterwards
1022 1bb99a33 Bernardo Dal Seno

1023 1bb99a33 Bernardo Dal Seno
    """
1024 31ccfc0e Helga Velroyen
    if self.op.drbd_helper == '':
1025 31ccfc0e Helga Velroyen
      if drbd_enabled:
1026 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Cannot disable drbd helper while"
1027 31ccfc0e Helga Velroyen
                                   " DRBD is enabled.")
1028 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_DRBD8):
1029 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable drbd helper while"
1030 7352d33b Thomas Thrainer
                                   " drbd-based instances exist",
1031 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
1032 7352d33b Thomas Thrainer
1033 31ccfc0e Helga Velroyen
    else:
1034 31ccfc0e Helga Velroyen
      if self.op.drbd_helper is not None and drbd_enabled:
1035 31ccfc0e Helga Velroyen
        self._CheckDrbdHelperOnNodes(self.op.drbd_helper, node_uuids)
1036 31ccfc0e Helga Velroyen
      else:
1037 31ccfc0e Helga Velroyen
        if drbd_gets_enabled:
1038 31ccfc0e Helga Velroyen
          current_drbd_helper = self.cfg.GetClusterInfo().drbd_usermode_helper
1039 31ccfc0e Helga Velroyen
          if current_drbd_helper is not None:
1040 31ccfc0e Helga Velroyen
            self._CheckDrbdHelperOnNodes(current_drbd_helper, node_uuids)
1041 31ccfc0e Helga Velroyen
          else:
1042 31ccfc0e Helga Velroyen
            raise errors.OpPrereqError("Cannot enable DRBD without a"
1043 31ccfc0e Helga Velroyen
                                       " DRBD usermode helper set.")
1044 7c577910 Helga Velroyen
1045 c2e984e2 Helga Velroyen
  def _CheckInstancesOfDisabledDiskTemplates(
1046 c2e984e2 Helga Velroyen
      self, disabled_disk_templates):
1047 5808df30 Helga Velroyen
    """Check whether we try to disable a disk template that is in use.
1048 c2e984e2 Helga Velroyen

1049 c2e984e2 Helga Velroyen
    @type disabled_disk_templates: list of string
1050 c2e984e2 Helga Velroyen
    @param disabled_disk_templates: list of disk templates that are going to
1051 c2e984e2 Helga Velroyen
      be disabled by this operation
1052 c2e984e2 Helga Velroyen

1053 c2e984e2 Helga Velroyen
    """
1054 c2e984e2 Helga Velroyen
    for disk_template in disabled_disk_templates:
1055 c2e984e2 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(disk_template):
1056 c2e984e2 Helga Velroyen
        raise errors.OpPrereqError(
1057 c2e984e2 Helga Velroyen
            "Cannot disable disk template '%s', because there is at least one"
1058 c2e984e2 Helga Velroyen
            " instance using it." % disk_template)
1059 c2e984e2 Helga Velroyen
1060 11eeb1b9 Jose A. Lopes
  @staticmethod
1061 11eeb1b9 Jose A. Lopes
  def _CheckInstanceCommunicationNetwork(network, warning_fn):
1062 11eeb1b9 Jose A. Lopes
    """Check whether an existing network is configured for instance
1063 11eeb1b9 Jose A. Lopes
    communication.
1064 11eeb1b9 Jose A. Lopes

1065 11eeb1b9 Jose A. Lopes
    Checks whether an existing network is configured with the
1066 11eeb1b9 Jose A. Lopes
    parameters that are advisable for instance communication, and
1067 11eeb1b9 Jose A. Lopes
    otherwise issue security warnings.
1068 11eeb1b9 Jose A. Lopes

1069 11eeb1b9 Jose A. Lopes
    @type network: L{ganeti.objects.Network}
1070 11eeb1b9 Jose A. Lopes
    @param network: L{ganeti.objects.Network} object whose
1071 11eeb1b9 Jose A. Lopes
                    configuration is being checked
1072 11eeb1b9 Jose A. Lopes
    @type warning_fn: function
1073 11eeb1b9 Jose A. Lopes
    @param warning_fn: function used to print warnings
1074 11eeb1b9 Jose A. Lopes
    @rtype: None
1075 11eeb1b9 Jose A. Lopes
    @return: None
1076 11eeb1b9 Jose A. Lopes

1077 11eeb1b9 Jose A. Lopes
    """
1078 11eeb1b9 Jose A. Lopes
    def _MaybeWarn(err, val, default):
1079 11eeb1b9 Jose A. Lopes
      if val != default:
1080 11eeb1b9 Jose A. Lopes
        warning_fn("Supplied instance communication network '%s' %s '%s',"
1081 11eeb1b9 Jose A. Lopes
                   " this might pose a security risk (default is '%s').",
1082 11eeb1b9 Jose A. Lopes
                   network.name, err, val, default)
1083 11eeb1b9 Jose A. Lopes
1084 11eeb1b9 Jose A. Lopes
    if network.network is None:
1085 11eeb1b9 Jose A. Lopes
      raise errors.OpPrereqError("Supplied instance communication network '%s'"
1086 11eeb1b9 Jose A. Lopes
                                 " must have an IPv4 network address.",
1087 11eeb1b9 Jose A. Lopes
                                 network.name)
1088 11eeb1b9 Jose A. Lopes
1089 11eeb1b9 Jose A. Lopes
    _MaybeWarn("has an IPv4 gateway", network.gateway, None)
1090 11eeb1b9 Jose A. Lopes
    _MaybeWarn("has a non-standard IPv4 network address", network.network,
1091 11eeb1b9 Jose A. Lopes
               constants.INSTANCE_COMMUNICATION_NETWORK4)
1092 11eeb1b9 Jose A. Lopes
    _MaybeWarn("has an IPv6 gateway", network.gateway6, None)
1093 11eeb1b9 Jose A. Lopes
    _MaybeWarn("has a non-standard IPv6 network address", network.network6,
1094 11eeb1b9 Jose A. Lopes
               constants.INSTANCE_COMMUNICATION_NETWORK6)
1095 11eeb1b9 Jose A. Lopes
    _MaybeWarn("has a non-standard MAC prefix", network.mac_prefix,
1096 11eeb1b9 Jose A. Lopes
               constants.INSTANCE_COMMUNICATION_MAC_PREFIX)
1097 11eeb1b9 Jose A. Lopes
1098 7c577910 Helga Velroyen
  def CheckPrereq(self):
1099 7c577910 Helga Velroyen
    """Check prerequisites.
1100 7c577910 Helga Velroyen

1101 7c577910 Helga Velroyen
    This checks whether the given params don't conflict and
1102 7c577910 Helga Velroyen
    if the given volume group is valid.
1103 7c577910 Helga Velroyen

1104 7c577910 Helga Velroyen
    """
1105 1c3231aa Thomas Thrainer
    node_uuids = self.owned_locks(locking.LEVEL_NODE)
1106 1bb99a33 Bernardo Dal Seno
    self.cluster = cluster = self.cfg.GetClusterInfo()
1107 7352d33b Thomas Thrainer
1108 1c3231aa Thomas Thrainer
    vm_capable_node_uuids = [node.uuid
1109 1c3231aa Thomas Thrainer
                             for node in self.cfg.GetAllNodesInfo().values()
1110 1c3231aa Thomas Thrainer
                             if node.uuid in node_uuids and node.vm_capable]
1111 7352d33b Thomas Thrainer
1112 6e513917 Helga Velroyen
    (enabled_disk_templates, new_enabled_disk_templates,
1113 5808df30 Helga Velroyen
      disabled_disk_templates) = self._GetDiskTemplateSets(cluster)
1114 6e513917 Helga Velroyen
    self._CheckInstancesOfDisabledDiskTemplates(disabled_disk_templates)
1115 1bb99a33 Bernardo Dal Seno
1116 1c3231aa Thomas Thrainer
    self._CheckVgName(vm_capable_node_uuids, enabled_disk_templates,
1117 1bb99a33 Bernardo Dal Seno
                      new_enabled_disk_templates)
1118 7352d33b Thomas Thrainer
1119 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
1120 3039e2dc Helga Velroyen
      CheckFileStoragePathVsEnabledDiskTemplates(
1121 3039e2dc Helga Velroyen
          self.LogWarning, self.op.file_storage_dir, enabled_disk_templates)
1122 3039e2dc Helga Velroyen
1123 4e6cfd11 Helga Velroyen
    if self.op.shared_file_storage_dir is not None:
1124 4e6cfd11 Helga Velroyen
      CheckSharedFileStoragePathVsEnabledDiskTemplates(
1125 4e6cfd11 Helga Velroyen
          self.LogWarning, self.op.shared_file_storage_dir,
1126 4e6cfd11 Helga Velroyen
          enabled_disk_templates)
1127 4e6cfd11 Helga Velroyen
1128 31ccfc0e Helga Velroyen
    drbd_enabled = constants.DT_DRBD8 in enabled_disk_templates
1129 31ccfc0e Helga Velroyen
    drbd_gets_enabled = constants.DT_DRBD8 in new_enabled_disk_templates
1130 31ccfc0e Helga Velroyen
    self._CheckDrbdHelper(node_uuids, drbd_enabled, drbd_gets_enabled)
1131 7352d33b Thomas Thrainer
1132 7352d33b Thomas Thrainer
    # validate params changes
1133 7352d33b Thomas Thrainer
    if self.op.beparams:
1134 7352d33b Thomas Thrainer
      objects.UpgradeBeParams(self.op.beparams)
1135 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
1136 7352d33b Thomas Thrainer
      self.new_beparams = cluster.SimpleFillBE(self.op.beparams)
1137 7352d33b Thomas Thrainer
1138 7352d33b Thomas Thrainer
    if self.op.ndparams:
1139 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
1140 7352d33b Thomas Thrainer
      self.new_ndparams = cluster.SimpleFillND(self.op.ndparams)
1141 7352d33b Thomas Thrainer
1142 7352d33b Thomas Thrainer
      # TODO: we need a more general way to handle resetting
1143 7352d33b Thomas Thrainer
      # cluster-level parameters to default values
1144 7352d33b Thomas Thrainer
      if self.new_ndparams["oob_program"] == "":
1145 7352d33b Thomas Thrainer
        self.new_ndparams["oob_program"] = \
1146 7352d33b Thomas Thrainer
            constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM]
1147 7352d33b Thomas Thrainer
1148 7352d33b Thomas Thrainer
    if self.op.hv_state:
1149 5eacbcae Thomas Thrainer
      new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
1150 5eacbcae Thomas Thrainer
                                           self.cluster.hv_state_static)
1151 7352d33b Thomas Thrainer
      self.new_hv_state = dict((hv, cluster.SimpleFillHvState(values))
1152 7352d33b Thomas Thrainer
                               for hv, values in new_hv_state.items())
1153 7352d33b Thomas Thrainer
1154 7352d33b Thomas Thrainer
    if self.op.disk_state:
1155 5eacbcae Thomas Thrainer
      new_disk_state = MergeAndVerifyDiskState(self.op.disk_state,
1156 5eacbcae Thomas Thrainer
                                               self.cluster.disk_state_static)
1157 7352d33b Thomas Thrainer
      self.new_disk_state = \
1158 7352d33b Thomas Thrainer
        dict((storage, dict((name, cluster.SimpleFillDiskState(values))
1159 7352d33b Thomas Thrainer
                            for name, values in svalues.items()))
1160 7352d33b Thomas Thrainer
             for storage, svalues in new_disk_state.items())
1161 7352d33b Thomas Thrainer
1162 33a6464e Helga Velroyen
    self._CheckIpolicy(cluster, enabled_disk_templates)
1163 7352d33b Thomas Thrainer
1164 7352d33b Thomas Thrainer
    if self.op.nicparams:
1165 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
1166 7352d33b Thomas Thrainer
      self.new_nicparams = cluster.SimpleFillNIC(self.op.nicparams)
1167 7352d33b Thomas Thrainer
      objects.NIC.CheckParameterSyntax(self.new_nicparams)
1168 7352d33b Thomas Thrainer
      nic_errors = []
1169 7352d33b Thomas Thrainer
1170 7352d33b Thomas Thrainer
      # check all instances for consistency
1171 7352d33b Thomas Thrainer
      for instance in self.cfg.GetAllInstancesInfo().values():
1172 7352d33b Thomas Thrainer
        for nic_idx, nic in enumerate(instance.nics):
1173 7352d33b Thomas Thrainer
          params_copy = copy.deepcopy(nic.nicparams)
1174 7352d33b Thomas Thrainer
          params_filled = objects.FillDict(self.new_nicparams, params_copy)
1175 7352d33b Thomas Thrainer
1176 7352d33b Thomas Thrainer
          # check parameter syntax
1177 7352d33b Thomas Thrainer
          try:
1178 7352d33b Thomas Thrainer
            objects.NIC.CheckParameterSyntax(params_filled)
1179 7352d33b Thomas Thrainer
          except errors.ConfigurationError, err:
1180 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: %s" %
1181 7352d33b Thomas Thrainer
                              (instance.name, nic_idx, err))
1182 7352d33b Thomas Thrainer
1183 7352d33b Thomas Thrainer
          # if we're moving instances to routed, check that they have an ip
1184 7352d33b Thomas Thrainer
          target_mode = params_filled[constants.NIC_MODE]
1185 7352d33b Thomas Thrainer
          if target_mode == constants.NIC_MODE_ROUTED and not nic.ip:
1186 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: routed NIC with no ip"
1187 7352d33b Thomas Thrainer
                              " address" % (instance.name, nic_idx))
1188 7352d33b Thomas Thrainer
      if nic_errors:
1189 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot apply the change, errors:\n%s" %
1190 7352d33b Thomas Thrainer
                                   "\n".join(nic_errors), errors.ECODE_INVAL)
1191 7352d33b Thomas Thrainer
1192 7352d33b Thomas Thrainer
    # hypervisor list/parameters
1193 7352d33b Thomas Thrainer
    self.new_hvparams = new_hvp = objects.FillDict(cluster.hvparams, {})
1194 7352d33b Thomas Thrainer
    if self.op.hvparams:
1195 7352d33b Thomas Thrainer
      for hv_name, hv_dict in self.op.hvparams.items():
1196 7352d33b Thomas Thrainer
        if hv_name not in self.new_hvparams:
1197 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name] = hv_dict
1198 7352d33b Thomas Thrainer
        else:
1199 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name].update(hv_dict)
1200 7352d33b Thomas Thrainer
1201 7352d33b Thomas Thrainer
    # disk template parameters
1202 7352d33b Thomas Thrainer
    self.new_diskparams = objects.FillDict(cluster.diskparams, {})
1203 7352d33b Thomas Thrainer
    if self.op.diskparams:
1204 7352d33b Thomas Thrainer
      for dt_name, dt_params in self.op.diskparams.items():
1205 f06af3ca Thomas Thrainer
        if dt_name not in self.new_diskparams:
1206 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name] = dt_params
1207 7352d33b Thomas Thrainer
        else:
1208 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name].update(dt_params)
1209 294254b1 Raffa Santi
      CheckDiskAccessModeConsistency(self.op.diskparams, self.cfg)
1210 7352d33b Thomas Thrainer
1211 7352d33b Thomas Thrainer
    # os hypervisor parameters
1212 7352d33b Thomas Thrainer
    self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
1213 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1214 7352d33b Thomas Thrainer
      for os_name, hvs in self.op.os_hvp.items():
1215 7352d33b Thomas Thrainer
        if os_name not in self.new_os_hvp:
1216 7352d33b Thomas Thrainer
          self.new_os_hvp[os_name] = hvs
1217 7352d33b Thomas Thrainer
        else:
1218 7352d33b Thomas Thrainer
          for hv_name, hv_dict in hvs.items():
1219 7352d33b Thomas Thrainer
            if hv_dict is None:
1220 7352d33b Thomas Thrainer
              # Delete if it exists
1221 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name].pop(hv_name, None)
1222 7352d33b Thomas Thrainer
            elif hv_name not in self.new_os_hvp[os_name]:
1223 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name] = hv_dict
1224 7352d33b Thomas Thrainer
            else:
1225 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name].update(hv_dict)
1226 7352d33b Thomas Thrainer
1227 7352d33b Thomas Thrainer
    # os parameters
1228 07e3c124 Santi Raffa
    self._BuildOSParams(cluster)
1229 7352d33b Thomas Thrainer
1230 7352d33b Thomas Thrainer
    # changes to the hypervisor list
1231 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1232 7352d33b Thomas Thrainer
      self.hv_list = self.op.enabled_hypervisors
1233 7352d33b Thomas Thrainer
      for hv in self.hv_list:
1234 7352d33b Thomas Thrainer
        # if the hypervisor doesn't already exist in the cluster
1235 7352d33b Thomas Thrainer
        # hvparams, we initialize it to empty, and then (in both
1236 7352d33b Thomas Thrainer
        # cases) we make sure to fill the defaults, as we might not
1237 7352d33b Thomas Thrainer
        # have a complete defaults list if the hypervisor wasn't
1238 7352d33b Thomas Thrainer
        # enabled before
1239 7352d33b Thomas Thrainer
        if hv not in new_hvp:
1240 7352d33b Thomas Thrainer
          new_hvp[hv] = {}
1241 7352d33b Thomas Thrainer
        new_hvp[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], new_hvp[hv])
1242 7352d33b Thomas Thrainer
        utils.ForceDictType(new_hvp[hv], constants.HVS_PARAMETER_TYPES)
1243 7352d33b Thomas Thrainer
    else:
1244 7352d33b Thomas Thrainer
      self.hv_list = cluster.enabled_hypervisors
1245 7352d33b Thomas Thrainer
1246 7352d33b Thomas Thrainer
    if self.op.hvparams or self.op.enabled_hypervisors is not None:
1247 7352d33b Thomas Thrainer
      # either the enabled list has changed, or the parameters have, validate
1248 7352d33b Thomas Thrainer
      for hv_name, hv_params in self.new_hvparams.items():
1249 7352d33b Thomas Thrainer
        if ((self.op.hvparams and hv_name in self.op.hvparams) or
1250 7352d33b Thomas Thrainer
            (self.op.enabled_hypervisors and
1251 7352d33b Thomas Thrainer
             hv_name in self.op.enabled_hypervisors)):
1252 7352d33b Thomas Thrainer
          # either this is a new hypervisor, or its parameters have changed
1253 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
1254 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1255 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(hv_params)
1256 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, hv_params)
1257 7352d33b Thomas Thrainer
1258 7352d33b Thomas Thrainer
    self._CheckDiskTemplateConsistency()
1259 7352d33b Thomas Thrainer
1260 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1261 7352d33b Thomas Thrainer
      # no need to check any newly-enabled hypervisors, since the
1262 7352d33b Thomas Thrainer
      # defaults have already been checked in the above code-block
1263 7352d33b Thomas Thrainer
      for os_name, os_hvp in self.new_os_hvp.items():
1264 7352d33b Thomas Thrainer
        for hv_name, hv_params in os_hvp.items():
1265 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1266 7352d33b Thomas Thrainer
          # we need to fill in the new os_hvp on top of the actual hv_p
1267 7352d33b Thomas Thrainer
          cluster_defaults = self.new_hvparams.get(hv_name, {})
1268 7352d33b Thomas Thrainer
          new_osp = objects.FillDict(cluster_defaults, hv_params)
1269 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
1270 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(new_osp)
1271 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, new_osp)
1272 7352d33b Thomas Thrainer
1273 7352d33b Thomas Thrainer
    if self.op.default_iallocator:
1274 7352d33b Thomas Thrainer
      alloc_script = utils.FindFile(self.op.default_iallocator,
1275 7352d33b Thomas Thrainer
                                    constants.IALLOCATOR_SEARCH_PATH,
1276 7352d33b Thomas Thrainer
                                    os.path.isfile)
1277 7352d33b Thomas Thrainer
      if alloc_script is None:
1278 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Invalid default iallocator script '%s'"
1279 7352d33b Thomas Thrainer
                                   " specified" % self.op.default_iallocator,
1280 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
1281 7352d33b Thomas Thrainer
1282 11eeb1b9 Jose A. Lopes
    if self.op.instance_communication_network:
1283 11eeb1b9 Jose A. Lopes
      network_name = self.op.instance_communication_network
1284 11eeb1b9 Jose A. Lopes
1285 11eeb1b9 Jose A. Lopes
      try:
1286 11eeb1b9 Jose A. Lopes
        network_uuid = self.cfg.LookupNetwork(network_name)
1287 11eeb1b9 Jose A. Lopes
      except errors.OpPrereqError:
1288 11eeb1b9 Jose A. Lopes
        network_uuid = None
1289 11eeb1b9 Jose A. Lopes
1290 11eeb1b9 Jose A. Lopes
      if network_uuid is not None:
1291 11eeb1b9 Jose A. Lopes
        network = self.cfg.GetNetwork(network_uuid)
1292 11eeb1b9 Jose A. Lopes
        self._CheckInstanceCommunicationNetwork(network, self.LogWarning)
1293 11eeb1b9 Jose A. Lopes
1294 07e3c124 Santi Raffa
  def _BuildOSParams(self, cluster):
1295 07e3c124 Santi Raffa
    "Calculate the new OS parameters for this operation."
1296 07e3c124 Santi Raffa
1297 07e3c124 Santi Raffa
    def _GetNewParams(source, new_params):
1298 07e3c124 Santi Raffa
      "Wrapper around GetUpdatedParams."
1299 07e3c124 Santi Raffa
      if new_params is None:
1300 07e3c124 Santi Raffa
        return source
1301 07e3c124 Santi Raffa
      result = objects.FillDict(source, {}) # deep copy of source
1302 07e3c124 Santi Raffa
      for os_name in new_params:
1303 07e3c124 Santi Raffa
        result[os_name] = GetUpdatedParams(result.get(os_name, {}),
1304 07e3c124 Santi Raffa
                                           new_params[os_name],
1305 07e3c124 Santi Raffa
                                           use_none=True)
1306 07e3c124 Santi Raffa
        if not result[os_name]:
1307 07e3c124 Santi Raffa
          del result[os_name] # we removed all parameters
1308 07e3c124 Santi Raffa
      return result
1309 07e3c124 Santi Raffa
1310 07e3c124 Santi Raffa
    self.new_osp = _GetNewParams(cluster.osparams,
1311 07e3c124 Santi Raffa
                                 self.op.osparams)
1312 07e3c124 Santi Raffa
    self.new_osp_private = _GetNewParams(cluster.osparams_private_cluster,
1313 07e3c124 Santi Raffa
                                         self.op.osparams_private_cluster)
1314 07e3c124 Santi Raffa
1315 07e3c124 Santi Raffa
    # Remove os validity check
1316 07e3c124 Santi Raffa
    changed_oses = (set(self.new_osp.keys()) | set(self.new_osp_private.keys()))
1317 07e3c124 Santi Raffa
    for os_name in changed_oses:
1318 07e3c124 Santi Raffa
      os_params = cluster.SimpleFillOS(
1319 07e3c124 Santi Raffa
        os_name,
1320 07e3c124 Santi Raffa
        self.new_osp.get(os_name, {}),
1321 07e3c124 Santi Raffa
        os_params_private=self.new_osp_private.get(os_name, {})
1322 07e3c124 Santi Raffa
      )
1323 07e3c124 Santi Raffa
      # check the parameter validity (remote check)
1324 07e3c124 Santi Raffa
      CheckOSParams(self, False, [self.cfg.GetMasterNode()],
1325 07e3c124 Santi Raffa
                    os_name, os_params)
1326 07e3c124 Santi Raffa
1327 7352d33b Thomas Thrainer
  def _CheckDiskTemplateConsistency(self):
1328 7352d33b Thomas Thrainer
    """Check whether the disk templates that are going to be disabled
1329 7352d33b Thomas Thrainer
       are still in use by some instances.
1330 7352d33b Thomas Thrainer

1331 7352d33b Thomas Thrainer
    """
1332 7352d33b Thomas Thrainer
    if self.op.enabled_disk_templates:
1333 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
1334 7352d33b Thomas Thrainer
      instances = self.cfg.GetAllInstancesInfo()
1335 7352d33b Thomas Thrainer
1336 7352d33b Thomas Thrainer
      disk_templates_to_remove = set(cluster.enabled_disk_templates) \
1337 7352d33b Thomas Thrainer
        - set(self.op.enabled_disk_templates)
1338 7352d33b Thomas Thrainer
      for instance in instances.itervalues():
1339 7352d33b Thomas Thrainer
        if instance.disk_template in disk_templates_to_remove:
1340 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Cannot disable disk template '%s',"
1341 7352d33b Thomas Thrainer
                                     " because instance '%s' is using it." %
1342 7352d33b Thomas Thrainer
                                     (instance.disk_template, instance.name))
1343 7352d33b Thomas Thrainer
1344 1bb99a33 Bernardo Dal Seno
  def _SetVgName(self, feedback_fn):
1345 1bb99a33 Bernardo Dal Seno
    """Determines and sets the new volume group name.
1346 7352d33b Thomas Thrainer

1347 7352d33b Thomas Thrainer
    """
1348 7352d33b Thomas Thrainer
    if self.op.vg_name is not None:
1349 7352d33b Thomas Thrainer
      new_volume = self.op.vg_name
1350 7352d33b Thomas Thrainer
      if not new_volume:
1351 7352d33b Thomas Thrainer
        new_volume = None
1352 7352d33b Thomas Thrainer
      if new_volume != self.cfg.GetVGName():
1353 7352d33b Thomas Thrainer
        self.cfg.SetVGName(new_volume)
1354 7352d33b Thomas Thrainer
      else:
1355 7352d33b Thomas Thrainer
        feedback_fn("Cluster LVM configuration already in desired"
1356 7352d33b Thomas Thrainer
                    " state, not changing")
1357 1bb99a33 Bernardo Dal Seno
1358 3039e2dc Helga Velroyen
  def _SetFileStorageDir(self, feedback_fn):
1359 3039e2dc Helga Velroyen
    """Set the file storage directory.
1360 3039e2dc Helga Velroyen

1361 3039e2dc Helga Velroyen
    """
1362 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
1363 3039e2dc Helga Velroyen
      if self.cluster.file_storage_dir == self.op.file_storage_dir:
1364 3039e2dc Helga Velroyen
        feedback_fn("Global file storage dir already set to value '%s'"
1365 3039e2dc Helga Velroyen
                    % self.cluster.file_storage_dir)
1366 3039e2dc Helga Velroyen
      else:
1367 3039e2dc Helga Velroyen
        self.cluster.file_storage_dir = self.op.file_storage_dir
1368 3039e2dc Helga Velroyen
1369 7c577910 Helga Velroyen
  def _SetDrbdHelper(self, feedback_fn):
1370 7c577910 Helga Velroyen
    """Set the DRBD usermode helper.
1371 1bb99a33 Bernardo Dal Seno

1372 1bb99a33 Bernardo Dal Seno
    """
1373 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None:
1374 1bb99a33 Bernardo Dal Seno
      if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates:
1375 a794b8d7 Thomas Thrainer
        feedback_fn("Note that you specified a drbd user helper, but did not"
1376 a794b8d7 Thomas Thrainer
                    " enable the drbd disk template.")
1377 7352d33b Thomas Thrainer
      new_helper = self.op.drbd_helper
1378 7352d33b Thomas Thrainer
      if not new_helper:
1379 7352d33b Thomas Thrainer
        new_helper = None
1380 7352d33b Thomas Thrainer
      if new_helper != self.cfg.GetDRBDHelper():
1381 7352d33b Thomas Thrainer
        self.cfg.SetDRBDHelper(new_helper)
1382 7352d33b Thomas Thrainer
      else:
1383 7352d33b Thomas Thrainer
        feedback_fn("Cluster DRBD helper already in desired state,"
1384 7352d33b Thomas Thrainer
                    " not changing")
1385 7c577910 Helga Velroyen
1386 d6a7518a Jose A. Lopes
  @staticmethod
1387 d6a7518a Jose A. Lopes
  def _EnsureInstanceCommunicationNetwork(cfg, network_name):
1388 d6a7518a Jose A. Lopes
    """Ensure that the instance communication network exists and is
1389 d6a7518a Jose A. Lopes
    connected to all groups.
1390 d6a7518a Jose A. Lopes

1391 d6a7518a Jose A. Lopes
    The instance communication network given by L{network_name} it is
1392 d6a7518a Jose A. Lopes
    created, if necessary, via the opcode 'OpNetworkAdd'.  Also, the
1393 d6a7518a Jose A. Lopes
    instance communication network is connected to all existing node
1394 d6a7518a Jose A. Lopes
    groups, if necessary, via the opcode 'OpNetworkConnect'.
1395 d6a7518a Jose A. Lopes

1396 9808764a Jose A. Lopes
    @type cfg: L{config.ConfigWriter}
1397 9808764a Jose A. Lopes
    @param cfg: cluster configuration
1398 d6a7518a Jose A. Lopes

1399 d6a7518a Jose A. Lopes
    @type network_name: string
1400 d6a7518a Jose A. Lopes
    @param network_name: instance communication network name
1401 d6a7518a Jose A. Lopes

1402 d6a7518a Jose A. Lopes
    @rtype: L{ganeti.cmdlib.ResultWithJobs} or L{None}
1403 d6a7518a Jose A. Lopes
    @return: L{ganeti.cmdlib.ResultWithJobs} if the instance
1404 d6a7518a Jose A. Lopes
             communication needs to be created or it needs to be
1405 d6a7518a Jose A. Lopes
             connected to a group, otherwise L{None}
1406 d6a7518a Jose A. Lopes

1407 d6a7518a Jose A. Lopes
    """
1408 d6a7518a Jose A. Lopes
    jobs = []
1409 d6a7518a Jose A. Lopes
1410 d6a7518a Jose A. Lopes
    try:
1411 d6a7518a Jose A. Lopes
      network_uuid = cfg.LookupNetwork(network_name)
1412 d6a7518a Jose A. Lopes
      network_exists = True
1413 d6a7518a Jose A. Lopes
    except errors.OpPrereqError:
1414 d6a7518a Jose A. Lopes
      network_exists = False
1415 d6a7518a Jose A. Lopes
1416 d6a7518a Jose A. Lopes
    if not network_exists:
1417 4b75f8a4 Jose A. Lopes
      jobs.append(AddInstanceCommunicationNetworkOp(network_name))
1418 d6a7518a Jose A. Lopes
1419 d6a7518a Jose A. Lopes
    for group_uuid in cfg.GetNodeGroupList():
1420 d6a7518a Jose A. Lopes
      group = cfg.GetNodeGroup(group_uuid)
1421 d6a7518a Jose A. Lopes
1422 d6a7518a Jose A. Lopes
      if network_exists:
1423 d6a7518a Jose A. Lopes
        network_connected = network_uuid in group.networks
1424 d6a7518a Jose A. Lopes
      else:
1425 d6a7518a Jose A. Lopes
        # The network was created asynchronously by the previous
1426 d6a7518a Jose A. Lopes
        # opcode and, therefore, we don't have access to its
1427 d6a7518a Jose A. Lopes
        # network_uuid.  As a result, we assume that the network is
1428 d6a7518a Jose A. Lopes
        # not connected to any group yet.
1429 d6a7518a Jose A. Lopes
        network_connected = False
1430 d6a7518a Jose A. Lopes
1431 d6a7518a Jose A. Lopes
      if not network_connected:
1432 4b75f8a4 Jose A. Lopes
        op = ConnectInstanceCommunicationNetworkOp(group_uuid, network_name)
1433 d6a7518a Jose A. Lopes
        jobs.append(op)
1434 d6a7518a Jose A. Lopes
1435 d6a7518a Jose A. Lopes
    if jobs:
1436 d6a7518a Jose A. Lopes
      return ResultWithJobs([jobs])
1437 d6a7518a Jose A. Lopes
    else:
1438 d6a7518a Jose A. Lopes
      return None
1439 d6a7518a Jose A. Lopes
1440 d6a7518a Jose A. Lopes
  @staticmethod
1441 d6a7518a Jose A. Lopes
  def _ModifyInstanceCommunicationNetwork(cfg, cluster, network_name,
1442 d6a7518a Jose A. Lopes
                                          feedback_fn):
1443 d6a7518a Jose A. Lopes
    """Update the instance communication network stored in the cluster
1444 d6a7518a Jose A. Lopes
    configuration.
1445 d6a7518a Jose A. Lopes

1446 d6a7518a Jose A. Lopes
    Compares the user-supplied instance communication network against
1447 d6a7518a Jose A. Lopes
    the one stored in the Ganeti cluster configuration.  If there is a
1448 d6a7518a Jose A. Lopes
    change, the instance communication network may be possibly created
1449 d6a7518a Jose A. Lopes
    and connected to all groups (see
1450 d6a7518a Jose A. Lopes
    L{LUClusterSetParams._EnsureInstanceCommunicationNetwork}).
1451 d6a7518a Jose A. Lopes

1452 9808764a Jose A. Lopes
    @type cfg: L{config.ConfigWriter}
1453 9808764a Jose A. Lopes
    @param cfg: cluster configuration
1454 d6a7518a Jose A. Lopes

1455 d6a7518a Jose A. Lopes
    @type cluster: L{ganeti.objects.Cluster}
1456 d6a7518a Jose A. Lopes
    @param cluster: Ganeti cluster
1457 d6a7518a Jose A. Lopes

1458 d6a7518a Jose A. Lopes
    @type network_name: string
1459 d6a7518a Jose A. Lopes
    @param network_name: instance communication network name
1460 d6a7518a Jose A. Lopes

1461 d6a7518a Jose A. Lopes
    @type feedback_fn: function
1462 d6a7518a Jose A. Lopes
    @param feedback_fn: see L{ganeti.cmdlist.base.LogicalUnit}
1463 d6a7518a Jose A. Lopes

1464 d6a7518a Jose A. Lopes
    @rtype: L{LUClusterSetParams._EnsureInstanceCommunicationNetwork} or L{None}
1465 d6a7518a Jose A. Lopes
    @return: see L{LUClusterSetParams._EnsureInstanceCommunicationNetwork}
1466 d6a7518a Jose A. Lopes

1467 d6a7518a Jose A. Lopes
    """
1468 d6a7518a Jose A. Lopes
    config_network_name = cfg.GetInstanceCommunicationNetwork()
1469 d6a7518a Jose A. Lopes
1470 d6a7518a Jose A. Lopes
    if network_name == config_network_name:
1471 d6a7518a Jose A. Lopes
      feedback_fn("Instance communication network already is '%s', nothing to"
1472 d6a7518a Jose A. Lopes
                  " do." % network_name)
1473 d6a7518a Jose A. Lopes
    else:
1474 d6a7518a Jose A. Lopes
      try:
1475 d6a7518a Jose A. Lopes
        cfg.LookupNetwork(config_network_name)
1476 d6a7518a Jose A. Lopes
        feedback_fn("Previous instance communication network '%s'"
1477 d6a7518a Jose A. Lopes
                    " should be removed manually." % config_network_name)
1478 d6a7518a Jose A. Lopes
      except errors.OpPrereqError:
1479 d6a7518a Jose A. Lopes
        pass
1480 d6a7518a Jose A. Lopes
1481 d6a7518a Jose A. Lopes
      if network_name:
1482 d6a7518a Jose A. Lopes
        feedback_fn("Changing instance communication network to '%s', only new"
1483 d6a7518a Jose A. Lopes
                    " instances will be affected."
1484 d6a7518a Jose A. Lopes
                    % network_name)
1485 d6a7518a Jose A. Lopes
      else:
1486 d6a7518a Jose A. Lopes
        feedback_fn("Disabling instance communication network, only new"
1487 d6a7518a Jose A. Lopes
                    " instances will be affected.")
1488 d6a7518a Jose A. Lopes
1489 d6a7518a Jose A. Lopes
      cluster.instance_communication_network = network_name
1490 d6a7518a Jose A. Lopes
1491 d6a7518a Jose A. Lopes
      if network_name:
1492 d6a7518a Jose A. Lopes
        return LUClusterSetParams._EnsureInstanceCommunicationNetwork(
1493 d6a7518a Jose A. Lopes
          cfg,
1494 d6a7518a Jose A. Lopes
          network_name)
1495 d6a7518a Jose A. Lopes
      else:
1496 d6a7518a Jose A. Lopes
        return None
1497 d6a7518a Jose A. Lopes
1498 7c577910 Helga Velroyen
  def Exec(self, feedback_fn):
1499 7c577910 Helga Velroyen
    """Change the parameters of the cluster.
1500 7c577910 Helga Velroyen

1501 7c577910 Helga Velroyen
    """
1502 7c577910 Helga Velroyen
    if self.op.enabled_disk_templates:
1503 7c577910 Helga Velroyen
      self.cluster.enabled_disk_templates = \
1504 8b95dfdc Helga Velroyen
        list(self.op.enabled_disk_templates)
1505 7c577910 Helga Velroyen
1506 7c577910 Helga Velroyen
    self._SetVgName(feedback_fn)
1507 7c577910 Helga Velroyen
    self._SetFileStorageDir(feedback_fn)
1508 7c577910 Helga Velroyen
    self._SetDrbdHelper(feedback_fn)
1509 7c577910 Helga Velroyen
1510 7352d33b Thomas Thrainer
    if self.op.hvparams:
1511 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1512 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1513 7352d33b Thomas Thrainer
      self.cluster.os_hvp = self.new_os_hvp
1514 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1515 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1516 7352d33b Thomas Thrainer
      self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
1517 7352d33b Thomas Thrainer
    if self.op.beparams:
1518 7352d33b Thomas Thrainer
      self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
1519 7352d33b Thomas Thrainer
    if self.op.nicparams:
1520 7352d33b Thomas Thrainer
      self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
1521 7352d33b Thomas Thrainer
    if self.op.ipolicy:
1522 7352d33b Thomas Thrainer
      self.cluster.ipolicy = self.new_ipolicy
1523 7352d33b Thomas Thrainer
    if self.op.osparams:
1524 7352d33b Thomas Thrainer
      self.cluster.osparams = self.new_osp
1525 07e3c124 Santi Raffa
    if self.op.osparams_private_cluster:
1526 07e3c124 Santi Raffa
      self.cluster.osparams_private_cluster = self.new_osp_private
1527 7352d33b Thomas Thrainer
    if self.op.ndparams:
1528 7352d33b Thomas Thrainer
      self.cluster.ndparams = self.new_ndparams
1529 7352d33b Thomas Thrainer
    if self.op.diskparams:
1530 7352d33b Thomas Thrainer
      self.cluster.diskparams = self.new_diskparams
1531 7352d33b Thomas Thrainer
    if self.op.hv_state:
1532 7352d33b Thomas Thrainer
      self.cluster.hv_state_static = self.new_hv_state
1533 7352d33b Thomas Thrainer
    if self.op.disk_state:
1534 7352d33b Thomas Thrainer
      self.cluster.disk_state_static = self.new_disk_state
1535 7352d33b Thomas Thrainer
1536 7352d33b Thomas Thrainer
    if self.op.candidate_pool_size is not None:
1537 7352d33b Thomas Thrainer
      self.cluster.candidate_pool_size = self.op.candidate_pool_size
1538 7352d33b Thomas Thrainer
      # we need to update the pool size here, otherwise the save will fail
1539 c1410048 Helga Velroyen
      AdjustCandidatePool(self, [], feedback_fn)
1540 7352d33b Thomas Thrainer
1541 ad756c77 Klaus Aehlig
    if self.op.max_running_jobs is not None:
1542 ad756c77 Klaus Aehlig
      self.cluster.max_running_jobs = self.op.max_running_jobs
1543 ad756c77 Klaus Aehlig
1544 7352d33b Thomas Thrainer
    if self.op.maintain_node_health is not None:
1545 7352d33b Thomas Thrainer
      if self.op.maintain_node_health and not constants.ENABLE_CONFD:
1546 7352d33b Thomas Thrainer
        feedback_fn("Note: CONFD was disabled at build time, node health"
1547 7352d33b Thomas Thrainer
                    " maintenance is not useful (still enabling it)")
1548 7352d33b Thomas Thrainer
      self.cluster.maintain_node_health = self.op.maintain_node_health
1549 7352d33b Thomas Thrainer
1550 75f2ff7d Michele Tartara
    if self.op.modify_etc_hosts is not None:
1551 75f2ff7d Michele Tartara
      self.cluster.modify_etc_hosts = self.op.modify_etc_hosts
1552 75f2ff7d Michele Tartara
1553 7352d33b Thomas Thrainer
    if self.op.prealloc_wipe_disks is not None:
1554 7352d33b Thomas Thrainer
      self.cluster.prealloc_wipe_disks = self.op.prealloc_wipe_disks
1555 7352d33b Thomas Thrainer
1556 7352d33b Thomas Thrainer
    if self.op.add_uids is not None:
1557 7352d33b Thomas Thrainer
      uidpool.AddToUidPool(self.cluster.uid_pool, self.op.add_uids)
1558 7352d33b Thomas Thrainer
1559 7352d33b Thomas Thrainer
    if self.op.remove_uids is not None:
1560 7352d33b Thomas Thrainer
      uidpool.RemoveFromUidPool(self.cluster.uid_pool, self.op.remove_uids)
1561 7352d33b Thomas Thrainer
1562 7352d33b Thomas Thrainer
    if self.op.uid_pool is not None:
1563 7352d33b Thomas Thrainer
      self.cluster.uid_pool = self.op.uid_pool
1564 7352d33b Thomas Thrainer
1565 7352d33b Thomas Thrainer
    if self.op.default_iallocator is not None:
1566 7352d33b Thomas Thrainer
      self.cluster.default_iallocator = self.op.default_iallocator
1567 7352d33b Thomas Thrainer
1568 0359e5d0 Spyros Trigazis
    if self.op.default_iallocator_params is not None:
1569 0359e5d0 Spyros Trigazis
      self.cluster.default_iallocator_params = self.op.default_iallocator_params
1570 0359e5d0 Spyros Trigazis
1571 7352d33b Thomas Thrainer
    if self.op.reserved_lvs is not None:
1572 7352d33b Thomas Thrainer
      self.cluster.reserved_lvs = self.op.reserved_lvs
1573 7352d33b Thomas Thrainer
1574 7352d33b Thomas Thrainer
    if self.op.use_external_mip_script is not None:
1575 7352d33b Thomas Thrainer
      self.cluster.use_external_mip_script = self.op.use_external_mip_script
1576 7352d33b Thomas Thrainer
1577 7352d33b Thomas Thrainer
    def helper_os(aname, mods, desc):
1578 7352d33b Thomas Thrainer
      desc += " OS list"
1579 7352d33b Thomas Thrainer
      lst = getattr(self.cluster, aname)
1580 7352d33b Thomas Thrainer
      for key, val in mods:
1581 7352d33b Thomas Thrainer
        if key == constants.DDM_ADD:
1582 7352d33b Thomas Thrainer
          if val in lst:
1583 7352d33b Thomas Thrainer
            feedback_fn("OS %s already in %s, ignoring" % (val, desc))
1584 7352d33b Thomas Thrainer
          else:
1585 7352d33b Thomas Thrainer
            lst.append(val)
1586 7352d33b Thomas Thrainer
        elif key == constants.DDM_REMOVE:
1587 7352d33b Thomas Thrainer
          if val in lst:
1588 7352d33b Thomas Thrainer
            lst.remove(val)
1589 7352d33b Thomas Thrainer
          else:
1590 7352d33b Thomas Thrainer
            feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
1591 7352d33b Thomas Thrainer
        else:
1592 7352d33b Thomas Thrainer
          raise errors.ProgrammerError("Invalid modification '%s'" % key)
1593 7352d33b Thomas Thrainer
1594 7352d33b Thomas Thrainer
    if self.op.hidden_os:
1595 7352d33b Thomas Thrainer
      helper_os("hidden_os", self.op.hidden_os, "hidden")
1596 7352d33b Thomas Thrainer
1597 7352d33b Thomas Thrainer
    if self.op.blacklisted_os:
1598 7352d33b Thomas Thrainer
      helper_os("blacklisted_os", self.op.blacklisted_os, "blacklisted")
1599 7352d33b Thomas Thrainer
1600 0cffcdb1 Dimitris Bliablias
    if self.op.mac_prefix:
1601 0cffcdb1 Dimitris Bliablias
      self.cluster.mac_prefix = self.op.mac_prefix
1602 0cffcdb1 Dimitris Bliablias
1603 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1604 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1605 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1606 7352d33b Thomas Thrainer
      feedback_fn("Shutting down master ip on the current netdev (%s)" %
1607 7352d33b Thomas Thrainer
                  self.cluster.master_netdev)
1608 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
1609 7352d33b Thomas Thrainer
                                                       master_params, ems)
1610 e5c92cfb Klaus Aehlig
      if not self.op.force:
1611 e5c92cfb Klaus Aehlig
        result.Raise("Could not disable the master ip")
1612 e5c92cfb Klaus Aehlig
      else:
1613 e5c92cfb Klaus Aehlig
        if result.fail_msg:
1614 e5c92cfb Klaus Aehlig
          msg = ("Could not disable the master ip (continuing anyway): %s" %
1615 e5c92cfb Klaus Aehlig
                 result.fail_msg)
1616 e5c92cfb Klaus Aehlig
          feedback_fn(msg)
1617 7352d33b Thomas Thrainer
      feedback_fn("Changing master_netdev from %s to %s" %
1618 7352d33b Thomas Thrainer
                  (master_params.netdev, self.op.master_netdev))
1619 7352d33b Thomas Thrainer
      self.cluster.master_netdev = self.op.master_netdev
1620 7352d33b Thomas Thrainer
1621 7352d33b Thomas Thrainer
    if self.op.master_netmask:
1622 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1623 7352d33b Thomas Thrainer
      feedback_fn("Changing master IP netmask to %s" % self.op.master_netmask)
1624 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_change_master_netmask(
1625 1c3231aa Thomas Thrainer
                 master_params.uuid, master_params.netmask,
1626 1c3231aa Thomas Thrainer
                 self.op.master_netmask, master_params.ip,
1627 1c3231aa Thomas Thrainer
                 master_params.netdev)
1628 c7dd65be Klaus Aehlig
      result.Warn("Could not change the master IP netmask", feedback_fn)
1629 7352d33b Thomas Thrainer
      self.cluster.master_netmask = self.op.master_netmask
1630 7352d33b Thomas Thrainer
1631 7352d33b Thomas Thrainer
    self.cfg.Update(self.cluster, feedback_fn)
1632 7352d33b Thomas Thrainer
1633 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1634 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1635 7352d33b Thomas Thrainer
      feedback_fn("Starting the master ip on the new master netdev (%s)" %
1636 7352d33b Thomas Thrainer
                  self.op.master_netdev)
1637 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1638 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
1639 7352d33b Thomas Thrainer
                                                     master_params, ems)
1640 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master ip on the master,"
1641 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
1642 7352d33b Thomas Thrainer
1643 d6a7518a Jose A. Lopes
    network_name = self.op.instance_communication_network
1644 d6a7518a Jose A. Lopes
    if network_name is not None:
1645 d6a7518a Jose A. Lopes
      return self._ModifyInstanceCommunicationNetwork(self.cfg, self.cluster,
1646 d6a7518a Jose A. Lopes
                                                      network_name, feedback_fn)
1647 d6a7518a Jose A. Lopes
    else:
1648 d6a7518a Jose A. Lopes
      return None
1649 d6a7518a Jose A. Lopes
1650 7352d33b Thomas Thrainer
1651 7352d33b Thomas Thrainer
class LUClusterVerify(NoHooksLU):
1652 7352d33b Thomas Thrainer
  """Submits all jobs necessary to verify the cluster.
1653 7352d33b Thomas Thrainer

1654 7352d33b Thomas Thrainer
  """
1655 7352d33b Thomas Thrainer
  REQ_BGL = False
1656 7352d33b Thomas Thrainer
1657 7352d33b Thomas Thrainer
  def ExpandNames(self):
1658 7352d33b Thomas Thrainer
    self.needed_locks = {}
1659 7352d33b Thomas Thrainer
1660 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1661 7352d33b Thomas Thrainer
    jobs = []
1662 7352d33b Thomas Thrainer
1663 7352d33b Thomas Thrainer
    if self.op.group_name:
1664 7352d33b Thomas Thrainer
      groups = [self.op.group_name]
1665 7352d33b Thomas Thrainer
      depends_fn = lambda: None
1666 7352d33b Thomas Thrainer
    else:
1667 7352d33b Thomas Thrainer
      groups = self.cfg.GetNodeGroupList()
1668 7352d33b Thomas Thrainer
1669 7352d33b Thomas Thrainer
      # Verify global configuration
1670 7352d33b Thomas Thrainer
      jobs.append([
1671 7352d33b Thomas Thrainer
        opcodes.OpClusterVerifyConfig(ignore_errors=self.op.ignore_errors),
1672 7352d33b Thomas Thrainer
        ])
1673 7352d33b Thomas Thrainer
1674 7352d33b Thomas Thrainer
      # Always depend on global verification
1675 7352d33b Thomas Thrainer
      depends_fn = lambda: [(-len(jobs), [])]
1676 7352d33b Thomas Thrainer
1677 7352d33b Thomas Thrainer
    jobs.extend(
1678 7352d33b Thomas Thrainer
      [opcodes.OpClusterVerifyGroup(group_name=group,
1679 7352d33b Thomas Thrainer
                                    ignore_errors=self.op.ignore_errors,
1680 7352d33b Thomas Thrainer
                                    depends=depends_fn())]
1681 7352d33b Thomas Thrainer
      for group in groups)
1682 7352d33b Thomas Thrainer
1683 7352d33b Thomas Thrainer
    # Fix up all parameters
1684 7352d33b Thomas Thrainer
    for op in itertools.chain(*jobs): # pylint: disable=W0142
1685 7352d33b Thomas Thrainer
      op.debug_simulate_errors = self.op.debug_simulate_errors
1686 7352d33b Thomas Thrainer
      op.verbose = self.op.verbose
1687 7352d33b Thomas Thrainer
      op.error_codes = self.op.error_codes
1688 7352d33b Thomas Thrainer
      try:
1689 7352d33b Thomas Thrainer
        op.skip_checks = self.op.skip_checks
1690 7352d33b Thomas Thrainer
      except AttributeError:
1691 7352d33b Thomas Thrainer
        assert not isinstance(op, opcodes.OpClusterVerifyGroup)
1692 7352d33b Thomas Thrainer
1693 7352d33b Thomas Thrainer
    return ResultWithJobs(jobs)
1694 7352d33b Thomas Thrainer
1695 7352d33b Thomas Thrainer
1696 7352d33b Thomas Thrainer
class _VerifyErrors(object):
1697 7352d33b Thomas Thrainer
  """Mix-in for cluster/group verify LUs.
1698 7352d33b Thomas Thrainer

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

1702 7352d33b Thomas Thrainer
  """
1703 7352d33b Thomas Thrainer
1704 7352d33b Thomas Thrainer
  ETYPE_FIELD = "code"
1705 a6c43c02 Helga Velroyen
  ETYPE_ERROR = constants.CV_ERROR
1706 a6c43c02 Helga Velroyen
  ETYPE_WARNING = constants.CV_WARNING
1707 7352d33b Thomas Thrainer
1708 7352d33b Thomas Thrainer
  def _Error(self, ecode, item, msg, *args, **kwargs):
1709 7352d33b Thomas Thrainer
    """Format an error message.
1710 7352d33b Thomas Thrainer

1711 7352d33b Thomas Thrainer
    Based on the opcode's error_codes parameter, either format a
1712 7352d33b Thomas Thrainer
    parseable error code, or a simpler error string.
1713 7352d33b Thomas Thrainer

1714 7352d33b Thomas Thrainer
    This must be called only from Exec and functions called from Exec.
1715 7352d33b Thomas Thrainer

1716 7352d33b Thomas Thrainer
    """
1717 7352d33b Thomas Thrainer
    ltype = kwargs.get(self.ETYPE_FIELD, self.ETYPE_ERROR)
1718 7352d33b Thomas Thrainer
    itype, etxt, _ = ecode
1719 7352d33b Thomas Thrainer
    # If the error code is in the list of ignored errors, demote the error to a
1720 7352d33b Thomas Thrainer
    # warning
1721 7352d33b Thomas Thrainer
    if etxt in self.op.ignore_errors:     # pylint: disable=E1101
1722 7352d33b Thomas Thrainer
      ltype = self.ETYPE_WARNING
1723 7352d33b Thomas Thrainer
    # first complete the msg
1724 7352d33b Thomas Thrainer
    if args:
1725 7352d33b Thomas Thrainer
      msg = msg % args
1726 7352d33b Thomas Thrainer
    # then format the whole message
1727 7352d33b Thomas Thrainer
    if self.op.error_codes: # This is a mix-in. pylint: disable=E1101
1728 7352d33b Thomas Thrainer
      msg = "%s:%s:%s:%s:%s" % (ltype, etxt, itype, item, msg)
1729 7352d33b Thomas Thrainer
    else:
1730 7352d33b Thomas Thrainer
      if item:
1731 7352d33b Thomas Thrainer
        item = " " + item
1732 7352d33b Thomas Thrainer
      else:
1733 7352d33b Thomas Thrainer
        item = ""
1734 7352d33b Thomas Thrainer
      msg = "%s: %s%s: %s" % (ltype, itype, item, msg)
1735 7352d33b Thomas Thrainer
    # and finally report it via the feedback_fn
1736 7352d33b Thomas Thrainer
    self._feedback_fn("  - %s" % msg) # Mix-in. pylint: disable=E1101
1737 7352d33b Thomas Thrainer
    # do not mark the operation as failed for WARN cases only
1738 7352d33b Thomas Thrainer
    if ltype == self.ETYPE_ERROR:
1739 7352d33b Thomas Thrainer
      self.bad = True
1740 7352d33b Thomas Thrainer
1741 7352d33b Thomas Thrainer
  def _ErrorIf(self, cond, *args, **kwargs):
1742 7352d33b Thomas Thrainer
    """Log an error message if the passed condition is True.
1743 7352d33b Thomas Thrainer

1744 7352d33b Thomas Thrainer
    """
1745 7352d33b Thomas Thrainer
    if (bool(cond)
1746 7352d33b Thomas Thrainer
        or self.op.debug_simulate_errors): # pylint: disable=E1101
1747 7352d33b Thomas Thrainer
      self._Error(*args, **kwargs)
1748 7352d33b Thomas Thrainer
1749 7352d33b Thomas Thrainer
1750 7352d33b Thomas Thrainer
def _GetAllHypervisorParameters(cluster, instances):
1751 7352d33b Thomas Thrainer
  """Compute the set of all hypervisor parameters.
1752 7352d33b Thomas Thrainer

1753 7352d33b Thomas Thrainer
  @type cluster: L{objects.Cluster}
1754 7352d33b Thomas Thrainer
  @param cluster: the cluster object
1755 7352d33b Thomas Thrainer
  @param instances: list of L{objects.Instance}
1756 7352d33b Thomas Thrainer
  @param instances: additional instances from which to obtain parameters
1757 7352d33b Thomas Thrainer
  @rtype: list of (origin, hypervisor, parameters)
1758 7352d33b Thomas Thrainer
  @return: a list with all parameters found, indicating the hypervisor they
1759 7352d33b Thomas Thrainer
       apply to, and the origin (can be "cluster", "os X", or "instance Y")
1760 7352d33b Thomas Thrainer

1761 7352d33b Thomas Thrainer
  """
1762 7352d33b Thomas Thrainer
  hvp_data = []
1763 7352d33b Thomas Thrainer
1764 7352d33b Thomas Thrainer
  for hv_name in cluster.enabled_hypervisors:
1765 7352d33b Thomas Thrainer
    hvp_data.append(("cluster", hv_name, cluster.GetHVDefaults(hv_name)))
1766 7352d33b Thomas Thrainer
1767 7352d33b Thomas Thrainer
  for os_name, os_hvp in cluster.os_hvp.items():
1768 7352d33b Thomas Thrainer
    for hv_name, hv_params in os_hvp.items():
1769 7352d33b Thomas Thrainer
      if hv_params:
1770 7352d33b Thomas Thrainer
        full_params = cluster.GetHVDefaults(hv_name, os_name=os_name)
1771 7352d33b Thomas Thrainer
        hvp_data.append(("os %s" % os_name, hv_name, full_params))
1772 7352d33b Thomas Thrainer
1773 7352d33b Thomas Thrainer
  # TODO: collapse identical parameter values in a single one
1774 7352d33b Thomas Thrainer
  for instance in instances:
1775 7352d33b Thomas Thrainer
    if instance.hvparams:
1776 7352d33b Thomas Thrainer
      hvp_data.append(("instance %s" % instance.name, instance.hypervisor,
1777 7352d33b Thomas Thrainer
                       cluster.FillHV(instance)))
1778 7352d33b Thomas Thrainer
1779 7352d33b Thomas Thrainer
  return hvp_data
1780 7352d33b Thomas Thrainer
1781 7352d33b Thomas Thrainer
1782 7352d33b Thomas Thrainer
class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors):
1783 7352d33b Thomas Thrainer
  """Verifies the cluster config.
1784 7352d33b Thomas Thrainer

1785 7352d33b Thomas Thrainer
  """
1786 7352d33b Thomas Thrainer
  REQ_BGL = False
1787 7352d33b Thomas Thrainer
1788 7352d33b Thomas Thrainer
  def _VerifyHVP(self, hvp_data):
1789 7352d33b Thomas Thrainer
    """Verifies locally the syntax of the hypervisor parameters.
1790 7352d33b Thomas Thrainer

1791 7352d33b Thomas Thrainer
    """
1792 7352d33b Thomas Thrainer
    for item, hv_name, hv_params in hvp_data:
1793 7352d33b Thomas Thrainer
      msg = ("hypervisor %s parameters syntax check (source %s): %%s" %
1794 7352d33b Thomas Thrainer
             (item, hv_name))
1795 7352d33b Thomas Thrainer
      try:
1796 7352d33b Thomas Thrainer
        hv_class = hypervisor.GetHypervisorClass(hv_name)
1797 7352d33b Thomas Thrainer
        utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1798 7352d33b Thomas Thrainer
        hv_class.CheckParameterSyntax(hv_params)
1799 7352d33b Thomas Thrainer
      except errors.GenericError, err:
1800 7352d33b Thomas Thrainer
        self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg % str(err))
1801 7352d33b Thomas Thrainer
1802 7352d33b Thomas Thrainer
  def ExpandNames(self):
1803 7352d33b Thomas Thrainer
    self.needed_locks = dict.fromkeys(locking.LEVELS, locking.ALL_SET)
1804 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1805 7352d33b Thomas Thrainer
1806 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1807 7352d33b Thomas Thrainer
    """Check prerequisites.
1808 7352d33b Thomas Thrainer

1809 7352d33b Thomas Thrainer
    """
1810 7352d33b Thomas Thrainer
    # Retrieve all information
1811 7352d33b Thomas Thrainer
    self.all_group_info = self.cfg.GetAllNodeGroupsInfo()
1812 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1813 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1814 7352d33b Thomas Thrainer
1815 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1816 7352d33b Thomas Thrainer
    """Verify integrity of cluster, performing various test on nodes.
1817 7352d33b Thomas Thrainer

1818 7352d33b Thomas Thrainer
    """
1819 7352d33b Thomas Thrainer
    self.bad = False
1820 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
1821 7352d33b Thomas Thrainer
1822 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster config")
1823 7352d33b Thomas Thrainer
1824 7352d33b Thomas Thrainer
    for msg in self.cfg.VerifyConfig():
1825 7352d33b Thomas Thrainer
      self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg)
1826 7352d33b Thomas Thrainer
1827 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster certificate files")
1828 7352d33b Thomas Thrainer
1829 7352d33b Thomas Thrainer
    for cert_filename in pathutils.ALL_CERT_FILES:
1830 a6c43c02 Helga Velroyen
      (errcode, msg) = utils.VerifyCertificate(cert_filename)
1831 7352d33b Thomas Thrainer
      self._ErrorIf(errcode, constants.CV_ECLUSTERCERT, None, msg, code=errcode)
1832 7352d33b Thomas Thrainer
1833 a5b9e2f2 Thomas Thrainer
    self._ErrorIf(not utils.CanRead(constants.LUXID_USER,
1834 69ac3b74 Michele Tartara
                                    pathutils.NODED_CERT_FILE),
1835 69ac3b74 Michele Tartara
                  constants.CV_ECLUSTERCERT,
1836 69ac3b74 Michele Tartara
                  None,
1837 69ac3b74 Michele Tartara
                  pathutils.NODED_CERT_FILE + " must be accessible by the " +
1838 a5b9e2f2 Thomas Thrainer
                    constants.LUXID_USER + " user")
1839 69ac3b74 Michele Tartara
1840 7352d33b Thomas Thrainer
    feedback_fn("* Verifying hypervisor parameters")
1841 7352d33b Thomas Thrainer
1842 7352d33b Thomas Thrainer
    self._VerifyHVP(_GetAllHypervisorParameters(self.cfg.GetClusterInfo(),
1843 7352d33b Thomas Thrainer
                                                self.all_inst_info.values()))
1844 7352d33b Thomas Thrainer
1845 7352d33b Thomas Thrainer
    feedback_fn("* Verifying all nodes belong to an existing group")
1846 7352d33b Thomas Thrainer
1847 7352d33b Thomas Thrainer
    # We do this verification here because, should this bogus circumstance
1848 7352d33b Thomas Thrainer
    # occur, it would never be caught by VerifyGroup, which only acts on
1849 7352d33b Thomas Thrainer
    # nodes/instances reachable from existing node groups.
1850 7352d33b Thomas Thrainer
1851 1c3231aa Thomas Thrainer
    dangling_nodes = set(node for node in self.all_node_info.values()
1852 7352d33b Thomas Thrainer
                         if node.group not in self.all_group_info)
1853 7352d33b Thomas Thrainer
1854 7352d33b Thomas Thrainer
    dangling_instances = {}
1855 7352d33b Thomas Thrainer
    no_node_instances = []
1856 7352d33b Thomas Thrainer
1857 7352d33b Thomas Thrainer
    for inst in self.all_inst_info.values():
1858 1c3231aa Thomas Thrainer
      if inst.primary_node in [node.uuid for node in dangling_nodes]:
1859 da4a52a3 Thomas Thrainer
        dangling_instances.setdefault(inst.primary_node, []).append(inst)
1860 7352d33b Thomas Thrainer
      elif inst.primary_node not in self.all_node_info:
1861 da4a52a3 Thomas Thrainer
        no_node_instances.append(inst)
1862 7352d33b Thomas Thrainer
1863 7352d33b Thomas Thrainer
    pretty_dangling = [
1864 7352d33b Thomas Thrainer
        "%s (%s)" %
1865 7352d33b Thomas Thrainer
        (node.name,
1866 24e96ef6 Thomas Thrainer
         utils.CommaJoin(inst.name for
1867 24e96ef6 Thomas Thrainer
                         inst in dangling_instances.get(node.uuid, [])))
1868 7352d33b Thomas Thrainer
        for node in dangling_nodes]
1869 7352d33b Thomas Thrainer
1870 7352d33b Thomas Thrainer
    self._ErrorIf(bool(dangling_nodes), constants.CV_ECLUSTERDANGLINGNODES,
1871 7352d33b Thomas Thrainer
                  None,
1872 7352d33b Thomas Thrainer
                  "the following nodes (and their instances) belong to a non"
1873 7352d33b Thomas Thrainer
                  " existing group: %s", utils.CommaJoin(pretty_dangling))
1874 7352d33b Thomas Thrainer
1875 7352d33b Thomas Thrainer
    self._ErrorIf(bool(no_node_instances), constants.CV_ECLUSTERDANGLINGINST,
1876 7352d33b Thomas Thrainer
                  None,
1877 7352d33b Thomas Thrainer
                  "the following instances have a non-existing primary-node:"
1878 24e96ef6 Thomas Thrainer
                  " %s", utils.CommaJoin(inst.name for
1879 24e96ef6 Thomas Thrainer
                                         inst in no_node_instances))
1880 7352d33b Thomas Thrainer
1881 7352d33b Thomas Thrainer
    return not self.bad
1882 7352d33b Thomas Thrainer
1883 7352d33b Thomas Thrainer
1884 7352d33b Thomas Thrainer
class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
1885 7352d33b Thomas Thrainer
  """Verifies the status of a node group.
1886 7352d33b Thomas Thrainer

1887 7352d33b Thomas Thrainer
  """
1888 7352d33b Thomas Thrainer
  HPATH = "cluster-verify"
1889 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
1890 7352d33b Thomas Thrainer
  REQ_BGL = False
1891 7352d33b Thomas Thrainer
1892 7352d33b Thomas Thrainer
  _HOOKS_INDENT_RE = re.compile("^", re.M)
1893 7352d33b Thomas Thrainer
1894 7352d33b Thomas Thrainer
  class NodeImage(object):
1895 7352d33b Thomas Thrainer
    """A class representing the logical and physical status of a node.
1896 7352d33b Thomas Thrainer

1897 1c3231aa Thomas Thrainer
    @type uuid: string
1898 1c3231aa Thomas Thrainer
    @ivar uuid: the node UUID to which this object refers
1899 7352d33b Thomas Thrainer
    @ivar volumes: a structure as returned from
1900 7352d33b Thomas Thrainer
        L{ganeti.backend.GetVolumeList} (runtime)
1901 7352d33b Thomas Thrainer
    @ivar instances: a list of running instances (runtime)
1902 7352d33b Thomas Thrainer
    @ivar pinst: list of configured primary instances (config)
1903 7352d33b Thomas Thrainer
    @ivar sinst: list of configured secondary instances (config)
1904 7352d33b Thomas Thrainer
    @ivar sbp: dictionary of {primary-node: list of instances} for all
1905 7352d33b Thomas Thrainer
        instances for which this node is secondary (config)
1906 7352d33b Thomas Thrainer
    @ivar mfree: free memory, as reported by hypervisor (runtime)
1907 7352d33b Thomas Thrainer
    @ivar dfree: free disk, as reported by the node (runtime)
1908 7352d33b Thomas Thrainer
    @ivar offline: the offline status (config)
1909 7352d33b Thomas Thrainer
    @type rpc_fail: boolean
1910 7352d33b Thomas Thrainer
    @ivar rpc_fail: whether the RPC verify call was successfull (overall,
1911 7352d33b Thomas Thrainer
        not whether the individual keys were correct) (runtime)
1912 7352d33b Thomas Thrainer
    @type lvm_fail: boolean
1913 7352d33b Thomas Thrainer
    @ivar lvm_fail: whether the RPC call didn't return valid LVM data
1914 7352d33b Thomas Thrainer
    @type hyp_fail: boolean
1915 7352d33b Thomas Thrainer
    @ivar hyp_fail: whether the RPC call didn't return the instance list
1916 7352d33b Thomas Thrainer
    @type ghost: boolean
1917 7352d33b Thomas Thrainer
    @ivar ghost: whether this is a known node or not (config)
1918 7352d33b Thomas Thrainer
    @type os_fail: boolean
1919 7352d33b Thomas Thrainer
    @ivar os_fail: whether the RPC call didn't return valid OS data
1920 7352d33b Thomas Thrainer
    @type oslist: list
1921 7352d33b Thomas Thrainer
    @ivar oslist: list of OSes as diagnosed by DiagnoseOS
1922 7352d33b Thomas Thrainer
    @type vm_capable: boolean
1923 7352d33b Thomas Thrainer
    @ivar vm_capable: whether the node can host instances
1924 7352d33b Thomas Thrainer
    @type pv_min: float
1925 7352d33b Thomas Thrainer
    @ivar pv_min: size in MiB of the smallest PVs
1926 7352d33b Thomas Thrainer
    @type pv_max: float
1927 7352d33b Thomas Thrainer
    @ivar pv_max: size in MiB of the biggest PVs
1928 7352d33b Thomas Thrainer

1929 7352d33b Thomas Thrainer
    """
1930 1c3231aa Thomas Thrainer
    def __init__(self, offline=False, uuid=None, vm_capable=True):
1931 1c3231aa Thomas Thrainer
      self.uuid = uuid
1932 7352d33b Thomas Thrainer
      self.volumes = {}
1933 7352d33b Thomas Thrainer
      self.instances = []
1934 7352d33b Thomas Thrainer
      self.pinst = []
1935 7352d33b Thomas Thrainer
      self.sinst = []
1936 7352d33b Thomas Thrainer
      self.sbp = {}
1937 7352d33b Thomas Thrainer
      self.mfree = 0
1938 7352d33b Thomas Thrainer
      self.dfree = 0
1939 7352d33b Thomas Thrainer
      self.offline = offline
1940 7352d33b Thomas Thrainer
      self.vm_capable = vm_capable
1941 7352d33b Thomas Thrainer
      self.rpc_fail = False
1942 7352d33b Thomas Thrainer
      self.lvm_fail = False
1943 7352d33b Thomas Thrainer
      self.hyp_fail = False
1944 7352d33b Thomas Thrainer
      self.ghost = False
1945 7352d33b Thomas Thrainer
      self.os_fail = False
1946 7352d33b Thomas Thrainer
      self.oslist = {}
1947 7352d33b Thomas Thrainer
      self.pv_min = None
1948 7352d33b Thomas Thrainer
      self.pv_max = None
1949 7352d33b Thomas Thrainer
1950 7352d33b Thomas Thrainer
  def ExpandNames(self):
1951 7352d33b Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
1952 7352d33b Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
1953 7352d33b Thomas Thrainer
1954 7352d33b Thomas Thrainer
    # Get instances in node group; this is unsafe and needs verification later
1955 da4a52a3 Thomas Thrainer
    inst_uuids = \
1956 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1957 7352d33b Thomas Thrainer
1958 7352d33b Thomas Thrainer
    self.needed_locks = {
1959 da4a52a3 Thomas Thrainer
      locking.LEVEL_INSTANCE: self.cfg.GetInstanceNames(inst_uuids),
1960 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
1961 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: [],
1962 7352d33b Thomas Thrainer
1963 7352d33b Thomas Thrainer
      # This opcode is run by watcher every five minutes and acquires all nodes
1964 7352d33b Thomas Thrainer
      # for a group. It doesn't run for a long time, so it's better to acquire
1965 7352d33b Thomas Thrainer
      # the node allocation lock as well.
1966 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1967 7352d33b Thomas Thrainer
      }
1968 7352d33b Thomas Thrainer
1969 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1970 7352d33b Thomas Thrainer
1971 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
1972 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE:
1973 7352d33b Thomas Thrainer
      # Get members of node group; this is unsafe and needs verification later
1974 7352d33b Thomas Thrainer
      nodes = set(self.cfg.GetNodeGroup(self.group_uuid).members)
1975 7352d33b Thomas Thrainer
1976 7352d33b Thomas Thrainer
      # In Exec(), we warn about mirrored instances that have primary and
1977 7352d33b Thomas Thrainer
      # secondary living in separate node groups. To fully verify that
1978 7352d33b Thomas Thrainer
      # volumes for these instances are healthy, we will need to do an
1979 7352d33b Thomas Thrainer
      # extra call to their secondaries. We ensure here those nodes will
1980 7352d33b Thomas Thrainer
      # be locked.
1981 da4a52a3 Thomas Thrainer
      for inst_name in self.owned_locks(locking.LEVEL_INSTANCE):
1982 7352d33b Thomas Thrainer
        # Important: access only the instances whose lock is owned
1983 da4a52a3 Thomas Thrainer
        instance = self.cfg.GetInstanceInfoByName(inst_name)
1984 da4a52a3 Thomas Thrainer
        if instance.disk_template in constants.DTS_INT_MIRROR:
1985 da4a52a3 Thomas Thrainer
          nodes.update(instance.secondary_nodes)
1986 7352d33b Thomas Thrainer
1987 7352d33b Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodes
1988 7352d33b Thomas Thrainer
1989 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1990 7352d33b Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
1991 7352d33b Thomas Thrainer
    self.group_info = self.cfg.GetNodeGroup(self.group_uuid)
1992 7352d33b Thomas Thrainer
1993 1c3231aa Thomas Thrainer
    group_node_uuids = set(self.group_info.members)
1994 da4a52a3 Thomas Thrainer
    group_inst_uuids = \
1995 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1996 7352d33b Thomas Thrainer
1997 1c3231aa Thomas Thrainer
    unlocked_node_uuids = \
1998 1c3231aa Thomas Thrainer
        group_node_uuids.difference(self.owned_locks(locking.LEVEL_NODE))
1999 7352d33b Thomas Thrainer
2000 da4a52a3 Thomas Thrainer
    unlocked_inst_uuids = \
2001 da4a52a3 Thomas Thrainer
        group_inst_uuids.difference(
2002 da4a52a3 Thomas Thrainer
          [self.cfg.GetInstanceInfoByName(name).uuid
2003 da4a52a3 Thomas Thrainer
           for name in self.owned_locks(locking.LEVEL_INSTANCE)])
2004 7352d33b Thomas Thrainer
2005 1c3231aa Thomas Thrainer
    if unlocked_node_uuids:
2006 1c3231aa Thomas Thrainer
      raise errors.OpPrereqError(
2007 1c3231aa Thomas Thrainer
        "Missing lock for nodes: %s" %
2008 1c3231aa Thomas Thrainer
        utils.CommaJoin(self.cfg.GetNodeNames(unlocked_node_uuids)),
2009 1c3231aa Thomas Thrainer
        errors.ECODE_STATE)
2010 7352d33b Thomas Thrainer
2011 da4a52a3 Thomas Thrainer
    if unlocked_inst_uuids:
2012 da4a52a3 Thomas Thrainer
      raise errors.OpPrereqError(
2013 da4a52a3 Thomas Thrainer
        "Missing lock for instances: %s" %
2014 da4a52a3 Thomas Thrainer
        utils.CommaJoin(self.cfg.GetInstanceNames(unlocked_inst_uuids)),
2015 da4a52a3 Thomas Thrainer
        errors.ECODE_STATE)
2016 7352d33b Thomas Thrainer
2017 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
2018 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
2019 7352d33b Thomas Thrainer
2020 1c3231aa Thomas Thrainer
    self.my_node_uuids = group_node_uuids
2021 1c3231aa Thomas Thrainer
    self.my_node_info = dict((node_uuid, self.all_node_info[node_uuid])
2022 1c3231aa Thomas Thrainer
                             for node_uuid in group_node_uuids)
2023 7352d33b Thomas Thrainer
2024 da4a52a3 Thomas Thrainer
    self.my_inst_uuids = group_inst_uuids
2025 da4a52a3 Thomas Thrainer
    self.my_inst_info = dict((inst_uuid, self.all_inst_info[inst_uuid])
2026 da4a52a3 Thomas Thrainer
                             for inst_uuid in group_inst_uuids)
2027 7352d33b Thomas Thrainer
2028 7352d33b Thomas Thrainer
    # We detect here the nodes that will need the extra RPC calls for verifying
2029 7352d33b Thomas Thrainer
    # split LV volumes; they should be locked.
2030 7352d33b Thomas Thrainer
    extra_lv_nodes = set()
2031 7352d33b Thomas Thrainer
2032 7352d33b Thomas Thrainer
    for inst in self.my_inst_info.values():
2033 7352d33b Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
2034 1c3231aa Thomas Thrainer
        for nuuid in inst.all_nodes:
2035 1c3231aa Thomas Thrainer
          if self.all_node_info[nuuid].group != self.group_uuid:
2036 1c3231aa Thomas Thrainer
            extra_lv_nodes.add(nuuid)
2037 7352d33b Thomas Thrainer
2038 7352d33b Thomas Thrainer
    unlocked_lv_nodes = \
2039 7352d33b Thomas Thrainer
        extra_lv_nodes.difference(self.owned_locks(locking.LEVEL_NODE))
2040 7352d33b Thomas Thrainer
2041 7352d33b Thomas Thrainer
    if unlocked_lv_nodes:
2042 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Missing node locks for LV check: %s" %
2043 7352d33b Thomas Thrainer
                                 utils.CommaJoin(unlocked_lv_nodes),
2044 7352d33b Thomas Thrainer
                                 errors.ECODE_STATE)
2045 7352d33b Thomas Thrainer
    self.extra_lv_nodes = list(extra_lv_nodes)
2046 7352d33b Thomas Thrainer
2047 7352d33b Thomas Thrainer
  def _VerifyNode(self, ninfo, nresult):
2048 7352d33b Thomas Thrainer
    """Perform some basic validation on data returned from a node.
2049 7352d33b Thomas Thrainer

2050 7352d33b Thomas Thrainer
      - check the result data structure is well formed and has all the
2051 7352d33b Thomas Thrainer
        mandatory fields
2052 7352d33b Thomas Thrainer
      - check ganeti version
2053 7352d33b Thomas Thrainer

2054 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2055 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2056 7352d33b Thomas Thrainer
    @param nresult: the results from the node
2057 7352d33b Thomas Thrainer
    @rtype: boolean
2058 7352d33b Thomas Thrainer
    @return: whether overall this call was successful (and we can expect
2059 7352d33b Thomas Thrainer
         reasonable values in the respose)
2060 7352d33b Thomas Thrainer

2061 7352d33b Thomas Thrainer
    """
2062 7352d33b Thomas Thrainer
    # main result, nresult should be a non-empty dict
2063 7352d33b Thomas Thrainer
    test = not nresult or not isinstance(nresult, dict)
2064 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
2065 7352d33b Thomas Thrainer
                  "unable to verify node: no data returned")
2066 7352d33b Thomas Thrainer
    if test:
2067 7352d33b Thomas Thrainer
      return False
2068 7352d33b Thomas Thrainer
2069 7352d33b Thomas Thrainer
    # compares ganeti version
2070 7352d33b Thomas Thrainer
    local_version = constants.PROTOCOL_VERSION
2071 7352d33b Thomas Thrainer
    remote_version = nresult.get("version", None)
2072 7352d33b Thomas Thrainer
    test = not (remote_version and
2073 7352d33b Thomas Thrainer
                isinstance(remote_version, (list, tuple)) and
2074 7352d33b Thomas Thrainer
                len(remote_version) == 2)
2075 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
2076 d0d7d7cf Thomas Thrainer
                  "connection to node returned invalid data")
2077 7352d33b Thomas Thrainer
    if test:
2078 7352d33b Thomas Thrainer
      return False
2079 7352d33b Thomas Thrainer
2080 7352d33b Thomas Thrainer
    test = local_version != remote_version[0]
2081 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEVERSION, ninfo.name,
2082 d0d7d7cf Thomas Thrainer
                  "incompatible protocol versions: master %s,"
2083 d0d7d7cf Thomas Thrainer
                  " node %s", local_version, remote_version[0])
2084 7352d33b Thomas Thrainer
    if test:
2085 7352d33b Thomas Thrainer
      return False
2086 7352d33b Thomas Thrainer
2087 7352d33b Thomas Thrainer
    # node seems compatible, we can actually try to look into its results
2088 7352d33b Thomas Thrainer
2089 7352d33b Thomas Thrainer
    # full package version
2090 7352d33b Thomas Thrainer
    self._ErrorIf(constants.RELEASE_VERSION != remote_version[1],
2091 d0d7d7cf Thomas Thrainer
                  constants.CV_ENODEVERSION, ninfo.name,
2092 7352d33b Thomas Thrainer
                  "software version mismatch: master %s, node %s",
2093 7352d33b Thomas Thrainer
                  constants.RELEASE_VERSION, remote_version[1],
2094 7352d33b Thomas Thrainer
                  code=self.ETYPE_WARNING)
2095 7352d33b Thomas Thrainer
2096 7352d33b Thomas Thrainer
    hyp_result = nresult.get(constants.NV_HYPERVISOR, None)
2097 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hyp_result, dict):
2098 7352d33b Thomas Thrainer
      for hv_name, hv_result in hyp_result.iteritems():
2099 7352d33b Thomas Thrainer
        test = hv_result is not None
2100 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2101 d0d7d7cf Thomas Thrainer
                      "hypervisor %s verify failure: '%s'", hv_name, hv_result)
2102 7352d33b Thomas Thrainer
2103 7352d33b Thomas Thrainer
    hvp_result = nresult.get(constants.NV_HVPARAMS, None)
2104 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hvp_result, list):
2105 7352d33b Thomas Thrainer
      for item, hv_name, hv_result in hvp_result:
2106 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEHV, ninfo.name,
2107 d0d7d7cf Thomas Thrainer
                      "hypervisor %s parameter verify failure (source %s): %s",
2108 d0d7d7cf Thomas Thrainer
                      hv_name, item, hv_result)
2109 7352d33b Thomas Thrainer
2110 7352d33b Thomas Thrainer
    test = nresult.get(constants.NV_NODESETUP,
2111 7352d33b Thomas Thrainer
                       ["Missing NODESETUP results"])
2112 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESETUP, ninfo.name,
2113 d0d7d7cf Thomas Thrainer
                  "node setup error: %s", "; ".join(test))
2114 7352d33b Thomas Thrainer
2115 7352d33b Thomas Thrainer
    return True
2116 7352d33b Thomas Thrainer
2117 7352d33b Thomas Thrainer
  def _VerifyNodeTime(self, ninfo, nresult,
2118 7352d33b Thomas Thrainer
                      nvinfo_starttime, nvinfo_endtime):
2119 7352d33b Thomas Thrainer
    """Check the node time.
2120 7352d33b Thomas Thrainer

2121 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2122 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2123 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2124 7352d33b Thomas Thrainer
    @param nvinfo_starttime: the start time of the RPC call
2125 7352d33b Thomas Thrainer
    @param nvinfo_endtime: the end time of the RPC call
2126 7352d33b Thomas Thrainer

2127 7352d33b Thomas Thrainer
    """
2128 7352d33b Thomas Thrainer
    ntime = nresult.get(constants.NV_TIME, None)
2129 7352d33b Thomas Thrainer
    try:
2130 7352d33b Thomas Thrainer
      ntime_merged = utils.MergeTime(ntime)
2131 7352d33b Thomas Thrainer
    except (ValueError, TypeError):
2132 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODETIME, ninfo.name,
2133 d0d7d7cf Thomas Thrainer
                    "Node returned invalid time")
2134 7352d33b Thomas Thrainer
      return
2135 7352d33b Thomas Thrainer
2136 7352d33b Thomas Thrainer
    if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW):
2137 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(nvinfo_starttime - ntime_merged)
2138 7352d33b Thomas Thrainer
    elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW):
2139 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(ntime_merged - nvinfo_endtime)
2140 7352d33b Thomas Thrainer
    else:
2141 7352d33b Thomas Thrainer
      ntime_diff = None
2142 7352d33b Thomas Thrainer
2143 d0d7d7cf Thomas Thrainer
    self._ErrorIf(ntime_diff is not None, constants.CV_ENODETIME, ninfo.name,
2144 d0d7d7cf Thomas Thrainer
                  "Node time diverges by at least %s from master node time",
2145 d0d7d7cf Thomas Thrainer
                  ntime_diff)
2146 7352d33b Thomas Thrainer
2147 7352d33b Thomas Thrainer
  def _UpdateVerifyNodeLVM(self, ninfo, nresult, vg_name, nimg):
2148 7352d33b Thomas Thrainer
    """Check the node LVM results and update info for cross-node checks.
2149 7352d33b Thomas Thrainer

2150 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2151 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2152 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2153 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2154 7352d33b Thomas Thrainer
    @type nimg: L{NodeImage}
2155 7352d33b Thomas Thrainer
    @param nimg: node image
2156 7352d33b Thomas Thrainer

2157 7352d33b Thomas Thrainer
    """
2158 7352d33b Thomas Thrainer
    if vg_name is None:
2159 7352d33b Thomas Thrainer
      return
2160 7352d33b Thomas Thrainer
2161 7352d33b Thomas Thrainer
    # checks vg existence and size > 20G
2162 7352d33b Thomas Thrainer
    vglist = nresult.get(constants.NV_VGLIST, None)
2163 7352d33b Thomas Thrainer
    test = not vglist
2164 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
2165 d0d7d7cf Thomas Thrainer
                  "unable to check volume groups")
2166 7352d33b Thomas Thrainer
    if not test:
2167 7352d33b Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist, vg_name,
2168 7352d33b Thomas Thrainer
                                            constants.MIN_VG_SIZE)
2169 d0d7d7cf Thomas Thrainer
      self._ErrorIf(vgstatus, constants.CV_ENODELVM, ninfo.name, vgstatus)
2170 7352d33b Thomas Thrainer
2171 7352d33b Thomas Thrainer
    # Check PVs
2172 5eacbcae Thomas Thrainer
    (errmsgs, pvminmax) = CheckNodePVs(nresult, self._exclusive_storage)
2173 7352d33b Thomas Thrainer
    for em in errmsgs:
2174 d0d7d7cf Thomas Thrainer
      self._Error(constants.CV_ENODELVM, ninfo.name, em)
2175 7352d33b Thomas Thrainer
    if pvminmax is not None:
2176 7352d33b Thomas Thrainer
      (nimg.pv_min, nimg.pv_max) = pvminmax
2177 7352d33b Thomas Thrainer
2178 1bb99a33 Bernardo Dal Seno
  def _VerifyGroupDRBDVersion(self, node_verify_infos):
2179 1bb99a33 Bernardo Dal Seno
    """Check cross-node DRBD version consistency.
2180 1bb99a33 Bernardo Dal Seno

2181 1bb99a33 Bernardo Dal Seno
    @type node_verify_infos: dict
2182 1bb99a33 Bernardo Dal Seno
    @param node_verify_infos: infos about nodes as returned from the
2183 1bb99a33 Bernardo Dal Seno
      node_verify call.
2184 1bb99a33 Bernardo Dal Seno

2185 1bb99a33 Bernardo Dal Seno
    """
2186 1bb99a33 Bernardo Dal Seno
    node_versions = {}
2187 1c3231aa Thomas Thrainer
    for node_uuid, ndata in node_verify_infos.items():
2188 1bb99a33 Bernardo Dal Seno
      nresult = ndata.payload
2189 fb62843c Klaus Aehlig
      if nresult:
2190 fb62843c Klaus Aehlig
        version = nresult.get(constants.NV_DRBDVERSION, "Missing DRBD version")
2191 fb62843c Klaus Aehlig
        node_versions[node_uuid] = version
2192 1bb99a33 Bernardo Dal Seno
2193 1bb99a33 Bernardo Dal Seno
    if len(set(node_versions.values())) > 1:
2194 1c3231aa Thomas Thrainer
      for node_uuid, version in sorted(node_versions.items()):
2195 1bb99a33 Bernardo Dal Seno
        msg = "DRBD version mismatch: %s" % version
2196 1c3231aa Thomas Thrainer
        self._Error(constants.CV_ENODEDRBDHELPER, node_uuid, msg,
2197 1bb99a33 Bernardo Dal Seno
                    code=self.ETYPE_WARNING)
2198 1bb99a33 Bernardo Dal Seno
2199 7352d33b Thomas Thrainer
  def _VerifyGroupLVM(self, node_image, vg_name):
2200 7352d33b Thomas Thrainer
    """Check cross-node consistency in LVM.
2201 7352d33b Thomas Thrainer

2202 7352d33b Thomas Thrainer
    @type node_image: dict
2203 7352d33b Thomas Thrainer
    @param node_image: info about nodes, mapping from node to names to
2204 7352d33b Thomas Thrainer
      L{NodeImage} objects
2205 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2206 7352d33b Thomas Thrainer

2207 7352d33b Thomas Thrainer
    """
2208 7352d33b Thomas Thrainer
    if vg_name is None:
2209 7352d33b Thomas Thrainer
      return
2210 7352d33b Thomas Thrainer
2211 bb935d8d Thomas Thrainer
    # Only exclusive storage needs this kind of checks
2212 7352d33b Thomas Thrainer
    if not self._exclusive_storage:
2213 7352d33b Thomas Thrainer
      return
2214 7352d33b Thomas Thrainer
2215 7352d33b Thomas Thrainer
    # exclusive_storage wants all PVs to have the same size (approximately),
2216 7352d33b Thomas Thrainer
    # if the smallest and the biggest ones are okay, everything is fine.
2217 7352d33b Thomas Thrainer
    # pv_min is None iff pv_max is None
2218 7352d33b Thomas Thrainer
    vals = filter((lambda ni: ni.pv_min is not None), node_image.values())
2219 7352d33b Thomas Thrainer
    if not vals:
2220 7352d33b Thomas Thrainer
      return
2221 bb935d8d Thomas Thrainer
    (pvmin, minnode_uuid) = min((ni.pv_min, ni.uuid) for ni in vals)
2222 bb935d8d Thomas Thrainer
    (pvmax, maxnode_uuid) = max((ni.pv_max, ni.uuid) for ni in vals)
2223 7352d33b Thomas Thrainer
    bad = utils.LvmExclusiveTestBadPvSizes(pvmin, pvmax)
2224 7352d33b Thomas Thrainer
    self._ErrorIf(bad, constants.CV_EGROUPDIFFERENTPVSIZE, self.group_info.name,
2225 7352d33b Thomas Thrainer
                  "PV sizes differ too much in the group; smallest (%s MB) is"
2226 7352d33b Thomas Thrainer
                  " on %s, biggest (%s MB) is on %s",
2227 bb935d8d Thomas Thrainer
                  pvmin, self.cfg.GetNodeName(minnode_uuid),
2228 bb935d8d Thomas Thrainer
                  pvmax, self.cfg.GetNodeName(maxnode_uuid))
2229 7352d33b Thomas Thrainer
2230 7352d33b Thomas Thrainer
  def _VerifyNodeBridges(self, ninfo, nresult, bridges):
2231 7352d33b Thomas Thrainer
    """Check the node bridges.
2232 7352d33b Thomas Thrainer

2233 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2234 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2235 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2236 7352d33b Thomas Thrainer
    @param bridges: the expected list of bridges
2237 7352d33b Thomas Thrainer

2238 7352d33b Thomas Thrainer
    """
2239 7352d33b Thomas Thrainer
    if not bridges:
2240 7352d33b Thomas Thrainer
      return
2241 7352d33b Thomas Thrainer
2242 7352d33b Thomas Thrainer
    missing = nresult.get(constants.NV_BRIDGES, None)
2243 7352d33b Thomas Thrainer
    test = not isinstance(missing, list)
2244 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2245 d0d7d7cf Thomas Thrainer
                  "did not return valid bridge information")
2246 7352d33b Thomas Thrainer
    if not test:
2247 d0d7d7cf Thomas Thrainer
      self._ErrorIf(bool(missing), constants.CV_ENODENET, ninfo.name,
2248 d0d7d7cf Thomas Thrainer
                    "missing bridges: %s" % utils.CommaJoin(sorted(missing)))
2249 7352d33b Thomas Thrainer
2250 7352d33b Thomas Thrainer
  def _VerifyNodeUserScripts(self, ninfo, nresult):
2251 7352d33b Thomas Thrainer
    """Check the results of user scripts presence and executability on the node
2252 7352d33b Thomas Thrainer

2253 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2254 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2255 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2256 7352d33b Thomas Thrainer

2257 7352d33b Thomas Thrainer
    """
2258 7352d33b Thomas Thrainer
    test = not constants.NV_USERSCRIPTS in nresult
2259 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
2260 7352d33b Thomas Thrainer
                  "did not return user scripts information")
2261 7352d33b Thomas Thrainer
2262 7352d33b Thomas Thrainer
    broken_scripts = nresult.get(constants.NV_USERSCRIPTS, None)
2263 7352d33b Thomas Thrainer
    if not test:
2264 d0d7d7cf Thomas Thrainer
      self._ErrorIf(broken_scripts, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
2265 7352d33b Thomas Thrainer
                    "user scripts not present or not executable: %s" %
2266 7352d33b Thomas Thrainer
                    utils.CommaJoin(sorted(broken_scripts)))
2267 7352d33b Thomas Thrainer
2268 7352d33b Thomas Thrainer
  def _VerifyNodeNetwork(self, ninfo, nresult):
2269 7352d33b Thomas Thrainer
    """Check the node network connectivity results.
2270 7352d33b Thomas Thrainer

2271 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2272 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2273 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2274 7352d33b Thomas Thrainer

2275 7352d33b Thomas Thrainer
    """
2276 7352d33b Thomas Thrainer
    test = constants.NV_NODELIST not in nresult
2277 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESSH, ninfo.name,
2278 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node ssh connectivity data")
2279 7352d33b Thomas Thrainer
    if not test:
2280 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODELIST]:
2281 7352d33b Thomas Thrainer
        for a_node, a_msg in nresult[constants.NV_NODELIST].items():
2282 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODESSH, ninfo.name,
2283 d0d7d7cf Thomas Thrainer
                        "ssh communication with node '%s': %s", a_node, a_msg)
2284 7352d33b Thomas Thrainer
2285 7352d33b Thomas Thrainer
    test = constants.NV_NODENETTEST not in nresult
2286 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2287 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node tcp connectivity data")
2288 7352d33b Thomas Thrainer
    if not test:
2289 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODENETTEST]:
2290 7352d33b Thomas Thrainer
        nlist = utils.NiceSort(nresult[constants.NV_NODENETTEST].keys())
2291 7352d33b Thomas Thrainer
        for anode in nlist:
2292 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODENET, ninfo.name,
2293 d0d7d7cf Thomas Thrainer
                        "tcp communication with node '%s': %s",
2294 d0d7d7cf Thomas Thrainer
                        anode, nresult[constants.NV_NODENETTEST][anode])
2295 7352d33b Thomas Thrainer
2296 7352d33b Thomas Thrainer
    test = constants.NV_MASTERIP not in nresult
2297 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2298 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node master IP reachability data")
2299 7352d33b Thomas Thrainer
    if not test:
2300 7352d33b Thomas Thrainer
      if not nresult[constants.NV_MASTERIP]:
2301 1c3231aa Thomas Thrainer
        if ninfo.uuid == self.master_node:
2302 7352d33b Thomas Thrainer
          msg = "the master node cannot reach the master IP (not configured?)"
2303 7352d33b Thomas Thrainer
        else:
2304 7352d33b Thomas Thrainer
          msg = "cannot reach the master IP"
2305 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODENET, ninfo.name, msg)
2306 7352d33b Thomas Thrainer
2307 da4a52a3 Thomas Thrainer
  def _VerifyInstance(self, instance, node_image, diskstatus):
2308 7352d33b Thomas Thrainer
    """Verify an instance.
2309 7352d33b Thomas Thrainer

2310 7352d33b Thomas Thrainer
    This function checks to see if the required block devices are
2311 7352d33b Thomas Thrainer
    available on the instance's node, and that the nodes are in the correct
2312 7352d33b Thomas Thrainer
    state.
2313 7352d33b Thomas Thrainer

2314 7352d33b Thomas Thrainer
    """
2315 da4a52a3 Thomas Thrainer
    pnode_uuid = instance.primary_node
2316 da4a52a3 Thomas Thrainer
    pnode_img = node_image[pnode_uuid]
2317 7352d33b Thomas Thrainer
    groupinfo = self.cfg.GetAllNodeGroupsInfo()
2318 7352d33b Thomas Thrainer
2319 7352d33b Thomas Thrainer
    node_vol_should = {}
2320 da4a52a3 Thomas Thrainer
    instance.MapLVsByNode(node_vol_should)
2321 7352d33b Thomas Thrainer
2322 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2323 7352d33b Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
2324 7352d33b Thomas Thrainer
                                                            self.group_info)
2325 da4a52a3 Thomas Thrainer
    err = ComputeIPolicyInstanceViolation(ipolicy, instance, self.cfg)
2326 da4a52a3 Thomas Thrainer
    self._ErrorIf(err, constants.CV_EINSTANCEPOLICY, instance.name,
2327 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(err), code=self.ETYPE_WARNING)
2328 7352d33b Thomas Thrainer
2329 da4a52a3 Thomas Thrainer
    for node_uuid in node_vol_should:
2330 da4a52a3 Thomas Thrainer
      n_img = node_image[node_uuid]
2331 7352d33b Thomas Thrainer
      if n_img.offline or n_img.rpc_fail or n_img.lvm_fail:
2332 7352d33b Thomas Thrainer
        # ignore missing volumes on offline or broken nodes
2333 7352d33b Thomas Thrainer
        continue
2334 da4a52a3 Thomas Thrainer
      for volume in node_vol_should[node_uuid]:
2335 7352d33b Thomas Thrainer
        test = volume not in n_img.volumes
2336 da4a52a3 Thomas Thrainer
        self._ErrorIf(test, constants.CV_EINSTANCEMISSINGDISK, instance.name,
2337 d0d7d7cf Thomas Thrainer
                      "volume %s missing on node %s", volume,
2338 da4a52a3 Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid))
2339 7352d33b Thomas Thrainer
2340 da4a52a3 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
2341 da4a52a3 Thomas Thrainer
      test = instance.uuid not in pnode_img.instances and not pnode_img.offline
2342 da4a52a3 Thomas Thrainer
      self._ErrorIf(test, constants.CV_EINSTANCEDOWN, instance.name,
2343 d0d7d7cf Thomas Thrainer
                    "instance not running on its primary node %s",
2344 da4a52a3 Thomas Thrainer
                     self.cfg.GetNodeName(pnode_uuid))
2345 da4a52a3 Thomas Thrainer
      self._ErrorIf(pnode_img.offline, constants.CV_EINSTANCEBADNODE,
2346 da4a52a3 Thomas Thrainer
                    instance.name, "instance is marked as running and lives on"
2347 da4a52a3 Thomas Thrainer
                    " offline node %s", self.cfg.GetNodeName(pnode_uuid))
2348 7352d33b Thomas Thrainer
2349 7352d33b Thomas Thrainer
    diskdata = [(nname, success, status, idx)
2350 7352d33b Thomas Thrainer
                for (nname, disks) in diskstatus.items()
2351 7352d33b Thomas Thrainer
                for idx, (success, status) in enumerate(disks)]
2352 7352d33b Thomas Thrainer
2353 7352d33b Thomas Thrainer
    for nname, success, bdev_status, idx in diskdata:
2354 7352d33b Thomas Thrainer
      # the 'ghost node' construction in Exec() ensures that we have a
2355 7352d33b Thomas Thrainer
      # node here
2356 7352d33b Thomas Thrainer
      snode = node_image[nname]
2357 7352d33b Thomas Thrainer
      bad_snode = snode.ghost or snode.offline
2358 da4a52a3 Thomas Thrainer
      self._ErrorIf(instance.disks_active and
2359 d0d7d7cf Thomas Thrainer
                    not success and not bad_snode,
2360 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEFAULTYDISK, instance.name,
2361 d0d7d7cf Thomas Thrainer
                    "couldn't retrieve status for disk/%s on %s: %s",
2362 d0d7d7cf Thomas Thrainer
                    idx, self.cfg.GetNodeName(nname), bdev_status)
2363 9b0e86e2 Thomas Thrainer
2364 da4a52a3 Thomas Thrainer
      if instance.disks_active and success and \
2365 9b0e86e2 Thomas Thrainer
         (bdev_status.is_degraded or
2366 9b0e86e2 Thomas Thrainer
          bdev_status.ldisk_status != constants.LDS_OKAY):
2367 9b0e86e2 Thomas Thrainer
        msg = "disk/%s on %s" % (idx, self.cfg.GetNodeName(nname))
2368 9b0e86e2 Thomas Thrainer
        if bdev_status.is_degraded:
2369 9b0e86e2 Thomas Thrainer
          msg += " is degraded"
2370 9b0e86e2 Thomas Thrainer
        if bdev_status.ldisk_status != constants.LDS_OKAY:
2371 9b0e86e2 Thomas Thrainer
          msg += "; state is '%s'" % \
2372 9b0e86e2 Thomas Thrainer
                 constants.LDS_NAMES[bdev_status.ldisk_status]
2373 9b0e86e2 Thomas Thrainer
2374 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEFAULTYDISK, instance.name, msg)
2375 d0d7d7cf Thomas Thrainer
2376 d0d7d7cf Thomas Thrainer
    self._ErrorIf(pnode_img.rpc_fail and not pnode_img.offline,
2377 da4a52a3 Thomas Thrainer
                  constants.CV_ENODERPC, self.cfg.GetNodeName(pnode_uuid),
2378 da4a52a3 Thomas Thrainer
                  "instance %s, connection to primary node failed",
2379 da4a52a3 Thomas Thrainer
                  instance.name)
2380 da4a52a3 Thomas Thrainer
2381 da4a52a3 Thomas Thrainer
    self._ErrorIf(len(instance.secondary_nodes) > 1,
2382 da4a52a3 Thomas Thrainer
                  constants.CV_EINSTANCELAYOUT, instance.name,
2383 da4a52a3 Thomas Thrainer
                  "instance has multiple secondary nodes: %s",
2384 da4a52a3 Thomas Thrainer
                  utils.CommaJoin(instance.secondary_nodes),
2385 d0d7d7cf Thomas Thrainer
                  code=self.ETYPE_WARNING)
2386 7352d33b Thomas Thrainer
2387 da4a52a3 Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg, instance.all_nodes)
2388 c69b147d Bernardo Dal Seno
    if any(es_flags.values()):
2389 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_EXCL_STORAGE:
2390 c69b147d Bernardo Dal Seno
        # Disk template not compatible with exclusive_storage: no instance
2391 c69b147d Bernardo Dal Seno
        # node should have the flag set
2392 c69b147d Bernardo Dal Seno
        es_nodes = [n
2393 c69b147d Bernardo Dal Seno
                    for (n, es) in es_flags.items()
2394 c69b147d Bernardo Dal Seno
                    if es]
2395 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEUNSUITABLENODE, instance.name,
2396 c69b147d Bernardo Dal Seno
                    "instance has template %s, which is not supported on nodes"
2397 c69b147d Bernardo Dal Seno
                    " that have exclusive storage set: %s",
2398 da4a52a3 Thomas Thrainer
                    instance.disk_template,
2399 1c3231aa Thomas Thrainer
                    utils.CommaJoin(self.cfg.GetNodeNames(es_nodes)))
2400 da4a52a3 Thomas Thrainer
      for (idx, disk) in enumerate(instance.disks):
2401 d0d7d7cf Thomas Thrainer
        self._ErrorIf(disk.spindles is None,
2402 da4a52a3 Thomas Thrainer
                      constants.CV_EINSTANCEMISSINGCFGPARAMETER, instance.name,
2403 d0d7d7cf Thomas Thrainer
                      "number of spindles not configured for disk %s while"
2404 d0d7d7cf Thomas Thrainer
                      " exclusive storage is enabled, try running"
2405 d0d7d7cf Thomas Thrainer
                      " gnt-cluster repair-disk-sizes", idx)
2406 7352d33b Thomas Thrainer
2407 da4a52a3 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
2408 da4a52a3 Thomas Thrainer
      instance_nodes = utils.NiceSort(instance.all_nodes)
2409 7352d33b Thomas Thrainer
      instance_groups = {}
2410 7352d33b Thomas Thrainer
2411 da4a52a3 Thomas Thrainer
      for node_uuid in instance_nodes:
2412 da4a52a3 Thomas Thrainer
        instance_groups.setdefault(self.all_node_info[node_uuid].group,
2413 da4a52a3 Thomas Thrainer
                                   []).append(node_uuid)
2414 7352d33b Thomas Thrainer
2415 7352d33b Thomas Thrainer
      pretty_list = [
2416 1c3231aa Thomas Thrainer
        "%s (group %s)" % (utils.CommaJoin(self.cfg.GetNodeNames(nodes)),
2417 1c3231aa Thomas Thrainer
                           groupinfo[group].name)
2418 7352d33b Thomas Thrainer
        # Sort so that we always list the primary node first.
2419 7352d33b Thomas Thrainer
        for group, nodes in sorted(instance_groups.items(),
2420 da4a52a3 Thomas Thrainer
                                   key=lambda (_, nodes): pnode_uuid in nodes,
2421 7352d33b Thomas Thrainer
                                   reverse=True)]
2422 7352d33b Thomas Thrainer
2423 7352d33b Thomas Thrainer
      self._ErrorIf(len(instance_groups) > 1,
2424 7352d33b Thomas Thrainer
                    constants.CV_EINSTANCESPLITGROUPS,
2425 da4a52a3 Thomas Thrainer
                    instance.name, "instance has primary and secondary nodes in"
2426 7352d33b Thomas Thrainer
                    " different groups: %s", utils.CommaJoin(pretty_list),
2427 7352d33b Thomas Thrainer
                    code=self.ETYPE_WARNING)
2428 7352d33b Thomas Thrainer
2429 7352d33b Thomas Thrainer
    inst_nodes_offline = []
2430 da4a52a3 Thomas Thrainer
    for snode in instance.secondary_nodes:
2431 7352d33b Thomas Thrainer
      s_img = node_image[snode]
2432 d0d7d7cf Thomas Thrainer
      self._ErrorIf(s_img.rpc_fail and not s_img.offline, constants.CV_ENODERPC,
2433 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(snode),
2434 da4a52a3 Thomas Thrainer
                    "instance %s, connection to secondary node failed",
2435 da4a52a3 Thomas Thrainer
                    instance.name)
2436 7352d33b Thomas Thrainer
2437 7352d33b Thomas Thrainer
      if s_img.offline:
2438 7352d33b Thomas Thrainer
        inst_nodes_offline.append(snode)
2439 7352d33b Thomas Thrainer
2440 7352d33b Thomas Thrainer
    # warn that the instance lives on offline nodes
2441 da4a52a3 Thomas Thrainer
    self._ErrorIf(inst_nodes_offline, constants.CV_EINSTANCEBADNODE,
2442 da4a52a3 Thomas Thrainer
                  instance.name, "instance has offline secondary node(s) %s",
2443 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(self.cfg.GetNodeNames(inst_nodes_offline)))
2444 7352d33b Thomas Thrainer
    # ... or ghost/non-vm_capable nodes
2445 da4a52a3 Thomas Thrainer
    for node_uuid in instance.all_nodes:
2446 da4a52a3 Thomas Thrainer
      self._ErrorIf(node_image[node_uuid].ghost, constants.CV_EINSTANCEBADNODE,
2447 da4a52a3 Thomas Thrainer
                    instance.name, "instance lives on ghost node %s",
2448 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2449 da4a52a3 Thomas Thrainer
      self._ErrorIf(not node_image[node_uuid].vm_capable,
2450 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEBADNODE, instance.name,
2451 d0d7d7cf Thomas Thrainer
                    "instance lives on non-vm_capable node %s",
2452 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2453 7352d33b Thomas Thrainer
2454 7352d33b Thomas Thrainer
  def _VerifyOrphanVolumes(self, node_vol_should, node_image, reserved):
2455 7352d33b Thomas Thrainer
    """Verify if there are any unknown volumes in the cluster.
2456 7352d33b Thomas Thrainer

2457 7352d33b Thomas Thrainer
    The .os, .swap and backup volumes are ignored. All other volumes are
2458 7352d33b Thomas Thrainer
    reported as unknown.
2459 7352d33b Thomas Thrainer

2460 7352d33b Thomas Thrainer
    @type reserved: L{ganeti.utils.FieldSet}
2461 7352d33b Thomas Thrainer
    @param reserved: a FieldSet of reserved volume names
2462 7352d33b Thomas Thrainer

2463 7352d33b Thomas Thrainer
    """
2464 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2465 7352d33b Thomas Thrainer
      if (n_img.offline or n_img.rpc_fail or n_img.lvm_fail or
2466 1c3231aa Thomas Thrainer
          self.all_node_info[node_uuid].group != self.group_uuid):
2467 7352d33b Thomas Thrainer
        # skip non-healthy nodes
2468 7352d33b Thomas Thrainer
        continue
2469 7352d33b Thomas Thrainer
      for volume in n_img.volumes:
2470 1c3231aa Thomas Thrainer
        test = ((node_uuid not in node_vol_should or
2471 1c3231aa Thomas Thrainer
                volume not in node_vol_should[node_uuid]) and
2472 7352d33b Thomas Thrainer
                not reserved.Matches(volume))
2473 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEORPHANLV,
2474 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2475 b0e8ed3f Santi Raffa
                      "volume %s is unknown", volume,
2476 b0e8ed3f Santi Raffa
                      code=_VerifyErrors.ETYPE_WARNING)
2477 7352d33b Thomas Thrainer
2478 da4a52a3 Thomas Thrainer
  def _VerifyNPlusOneMemory(self, node_image, all_insts):
2479 7352d33b Thomas Thrainer
    """Verify N+1 Memory Resilience.
2480 7352d33b Thomas Thrainer

2481 7352d33b Thomas Thrainer
    Check that if one single node dies we can still start all the
2482 7352d33b Thomas Thrainer
    instances it was primary for.
2483 7352d33b Thomas Thrainer

2484 7352d33b Thomas Thrainer
    """
2485 7352d33b Thomas Thrainer
    cluster_info = self.cfg.GetClusterInfo()
2486 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2487 7352d33b Thomas Thrainer
      # This code checks that every node which is now listed as
2488 7352d33b Thomas Thrainer
      # secondary has enough memory to host all instances it is
2489 7352d33b Thomas Thrainer
      # supposed to should a single other node in the cluster fail.
2490 7352d33b Thomas Thrainer
      # FIXME: not ready for failover to an arbitrary node
2491 7352d33b Thomas Thrainer
      # FIXME: does not support file-backed instances
2492 7352d33b Thomas Thrainer
      # WARNING: we currently take into account down instances as well
2493 7352d33b Thomas Thrainer
      # as up ones, considering that even if they're down someone
2494 7352d33b Thomas Thrainer
      # might want to start them even in the event of a node failure.
2495 1c3231aa Thomas Thrainer
      if n_img.offline or \
2496 1c3231aa Thomas Thrainer
         self.all_node_info[node_uuid].group != self.group_uuid:
2497 7352d33b Thomas Thrainer
        # we're skipping nodes marked offline and nodes in other groups from
2498 7352d33b Thomas Thrainer
        # the N+1 warning, since most likely we don't have good memory
2499 9fdb10be Thomas Thrainer
        # information from them; we already list instances living on such
2500 7352d33b Thomas Thrainer
        # nodes, and that's enough warning
2501 7352d33b Thomas Thrainer
        continue
2502 7352d33b Thomas Thrainer
      #TODO(dynmem): also consider ballooning out other instances
2503 da4a52a3 Thomas Thrainer
      for prinode, inst_uuids in n_img.sbp.items():
2504 7352d33b Thomas Thrainer
        needed_mem = 0
2505 da4a52a3 Thomas Thrainer
        for inst_uuid in inst_uuids:
2506 da4a52a3 Thomas Thrainer
          bep = cluster_info.FillBE(all_insts[inst_uuid])
2507 7352d33b Thomas Thrainer
          if bep[constants.BE_AUTO_BALANCE]:
2508 7352d33b Thomas Thrainer
            needed_mem += bep[constants.BE_MINMEM]
2509 7352d33b Thomas Thrainer
        test = n_img.mfree < needed_mem
2510 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEN1,
2511 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2512 7352d33b Thomas Thrainer
                      "not enough memory to accomodate instance failovers"
2513 7352d33b Thomas Thrainer
                      " should node %s fail (%dMiB needed, %dMiB available)",
2514 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(prinode), needed_mem, n_img.mfree)
2515 7352d33b Thomas Thrainer
2516 a6c43c02 Helga Velroyen
  def _VerifyClientCertificates(self, nodes, all_nvinfo):
2517 a6c43c02 Helga Velroyen
    """Verifies the consistency of the client certificates.
2518 a6c43c02 Helga Velroyen

2519 a6c43c02 Helga Velroyen
    This includes several aspects:
2520 a6c43c02 Helga Velroyen
      - the individual validation of all nodes' certificates
2521 a6c43c02 Helga Velroyen
      - the consistency of the master candidate certificate map
2522 a6c43c02 Helga Velroyen
      - the consistency of the master candidate certificate map with the
2523 a6c43c02 Helga Velroyen
        certificates that the master candidates are actually using.
2524 a6c43c02 Helga Velroyen

2525 a6c43c02 Helga Velroyen
    @param nodes: the list of nodes to consider in this verification
2526 a6c43c02 Helga Velroyen
    @param all_nvinfo: the map of results of the verify_node call to
2527 a6c43c02 Helga Velroyen
      all nodes
2528 a6c43c02 Helga Velroyen

2529 a6c43c02 Helga Velroyen
    """
2530 a6c43c02 Helga Velroyen
    candidate_certs = self.cfg.GetClusterInfo().candidate_certs
2531 a6c43c02 Helga Velroyen
    if candidate_certs is None or len(candidate_certs) == 0:
2532 a6c43c02 Helga Velroyen
      self._ErrorIf(
2533 a6c43c02 Helga Velroyen
        True, constants.CV_ECLUSTERCLIENTCERT, None,
2534 a6c43c02 Helga Velroyen
        "The cluster's list of master candidate certificates is empty."
2535 46ae85de Helga Velroyen
        " If you just updated the cluster, please run"
2536 a6c43c02 Helga Velroyen
        " 'gnt-cluster renew-crypto --new-node-certificates'.")
2537 a6c43c02 Helga Velroyen
      return
2538 a6c43c02 Helga Velroyen
2539 a6c43c02 Helga Velroyen
    self._ErrorIf(
2540 a6c43c02 Helga Velroyen
      len(candidate_certs) != len(set(candidate_certs.values())),
2541 a6c43c02 Helga Velroyen
      constants.CV_ECLUSTERCLIENTCERT, None,
2542 a6c43c02 Helga Velroyen
      "There are at least two master candidates configured to use the same"
2543 a6c43c02 Helga Velroyen
      " certificate.")
2544 a6c43c02 Helga Velroyen
2545 a6c43c02 Helga Velroyen
    # collect the client certificate
2546 a6c43c02 Helga Velroyen
    for node in nodes:
2547 a6c43c02 Helga Velroyen
      if node.offline:
2548 a6c43c02 Helga Velroyen
        continue
2549 a6c43c02 Helga Velroyen
2550 a6c43c02 Helga Velroyen
      nresult = all_nvinfo[node.uuid]
2551 a6c43c02 Helga Velroyen
      if nresult.fail_msg or not nresult.payload:
2552 a6c43c02 Helga Velroyen
        continue
2553 a6c43c02 Helga Velroyen
2554 a6c43c02 Helga Velroyen
      (errcode, msg) = nresult.payload.get(constants.NV_CLIENT_CERT, None)
2555 a6c43c02 Helga Velroyen
2556 a6c43c02 Helga Velroyen
      self._ErrorIf(
2557 a6c43c02 Helga Velroyen
        errcode is not None, constants.CV_ECLUSTERCLIENTCERT, None,
2558 a6c43c02 Helga Velroyen
        "Client certificate of node '%s' failed validation: %s (code '%s')",
2559 a6c43c02 Helga Velroyen
        node.uuid, msg, errcode)
2560 a6c43c02 Helga Velroyen
2561 a6c43c02 Helga Velroyen
      if not errcode:
2562 a6c43c02 Helga Velroyen
        digest = msg
2563 a6c43c02 Helga Velroyen
        if node.master_candidate:
2564 a6c43c02 Helga Velroyen
          if node.uuid in candidate_certs:
2565 a6c43c02 Helga Velroyen
            self._ErrorIf(
2566 a6c43c02 Helga Velroyen
              digest != candidate_certs[node.uuid],
2567 a6c43c02 Helga Velroyen
              constants.CV_ECLUSTERCLIENTCERT, None,
2568 a6c43c02 Helga Velroyen
              "Client certificate digest of master candidate '%s' does not"
2569 a6c43c02 Helga Velroyen
              " match its entry in the cluster's map of master candidate"
2570 a6c43c02 Helga Velroyen
              " certificates. Expected: %s Got: %s", node.uuid,
2571 a6c43c02 Helga Velroyen
              digest, candidate_certs[node.uuid])
2572 a6c43c02 Helga Velroyen
          else:
2573 a6c43c02 Helga Velroyen
            self._ErrorIf(
2574 a6c43c02 Helga Velroyen
              True, constants.CV_ECLUSTERCLIENTCERT, None,
2575 a6c43c02 Helga Velroyen
              "The master candidate '%s' does not have an entry in the"
2576 a6c43c02 Helga Velroyen
              " map of candidate certificates.", node.uuid)
2577 a6c43c02 Helga Velroyen
            self._ErrorIf(
2578 a6c43c02 Helga Velroyen
              digest in candidate_certs.values(),
2579 a6c43c02 Helga Velroyen
              constants.CV_ECLUSTERCLIENTCERT, None,
2580 a6c43c02 Helga Velroyen
              "Master candidate '%s' is using a certificate of another node.",
2581 a6c43c02 Helga Velroyen
              node.uuid)
2582 a6c43c02 Helga Velroyen
        else:
2583 a6c43c02 Helga Velroyen
          self._ErrorIf(
2584 a6c43c02 Helga Velroyen
            node.uuid in candidate_certs,
2585 a6c43c02 Helga Velroyen
            constants.CV_ECLUSTERCLIENTCERT, None,
2586 a6c43c02 Helga Velroyen
            "Node '%s' is not a master candidate, but still listed in the"
2587 a6c43c02 Helga Velroyen
            " map of master candidate certificates.", node.uuid)
2588 a6c43c02 Helga Velroyen
          self._ErrorIf(
2589 a6c43c02 Helga Velroyen
            (node.uuid not in candidate_certs) and
2590 a6c43c02 Helga Velroyen
              (digest in candidate_certs.values()),
2591 a6c43c02 Helga Velroyen
            constants.CV_ECLUSTERCLIENTCERT, None,
2592 a6c43c02 Helga Velroyen
            "Node '%s' is not a master candidate and is incorrectly using a"
2593 a6c43c02 Helga Velroyen
            " certificate of another node which is master candidate.",
2594 a6c43c02 Helga Velroyen
            node.uuid)
2595 a6c43c02 Helga Velroyen
2596 1c3231aa Thomas Thrainer
  def _VerifyFiles(self, nodes, master_node_uuid, all_nvinfo,
2597 7352d33b Thomas Thrainer
                   (files_all, files_opt, files_mc, files_vm)):
2598 7352d33b Thomas Thrainer
    """Verifies file checksums collected from all nodes.
2599 7352d33b Thomas Thrainer

2600 1c3231aa Thomas Thrainer
    @param nodes: List of L{objects.Node} objects
2601 1c3231aa Thomas Thrainer
    @param master_node_uuid: UUID of master node
2602 7352d33b Thomas Thrainer
    @param all_nvinfo: RPC results
2603 7352d33b Thomas Thrainer

2604 7352d33b Thomas Thrainer
    """
2605 7352d33b Thomas Thrainer
    # Define functions determining which nodes to consider for a file
2606 7352d33b Thomas Thrainer
    files2nodefn = [
2607 7352d33b Thomas Thrainer
      (files_all, None),
2608 7352d33b Thomas Thrainer
      (files_mc, lambda node: (node.master_candidate or
2609 1c3231aa Thomas Thrainer
                               node.uuid == master_node_uuid)),
2610 7352d33b Thomas Thrainer
      (files_vm, lambda node: node.vm_capable),
2611 7352d33b Thomas Thrainer
      ]
2612 7352d33b Thomas Thrainer
2613 7352d33b Thomas Thrainer
    # Build mapping from filename to list of nodes which should have the file
2614 7352d33b Thomas Thrainer
    nodefiles = {}
2615 7352d33b Thomas Thrainer
    for (files, fn) in files2nodefn:
2616 7352d33b Thomas Thrainer
      if fn is None:
2617 1c3231aa Thomas Thrainer
        filenodes = nodes
2618 7352d33b Thomas Thrainer
      else:
2619 1c3231aa Thomas Thrainer
        filenodes = filter(fn, nodes)
2620 7352d33b Thomas Thrainer
      nodefiles.update((filename,
2621 1c3231aa Thomas Thrainer
                        frozenset(map(operator.attrgetter("uuid"), filenodes)))
2622 7352d33b Thomas Thrainer
                       for filename in files)
2623 7352d33b Thomas Thrainer
2624 7352d33b Thomas Thrainer
    assert set(nodefiles) == (files_all | files_mc | files_vm)
2625 7352d33b Thomas Thrainer
2626 7352d33b Thomas Thrainer
    fileinfo = dict((filename, {}) for filename in nodefiles)
2627 7352d33b Thomas Thrainer
    ignore_nodes = set()
2628 7352d33b Thomas Thrainer
2629 1c3231aa Thomas Thrainer
    for node in nodes:
2630 7352d33b Thomas Thrainer
      if node.offline:
2631 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2632 7352d33b Thomas Thrainer
        continue
2633 7352d33b Thomas Thrainer
2634 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node.uuid]
2635 7352d33b Thomas Thrainer
2636 7352d33b Thomas Thrainer
      if nresult.fail_msg or not nresult.payload:
2637 7352d33b Thomas Thrainer
        node_files = None
2638 7352d33b Thomas Thrainer
      else:
2639 a6c43c02 Helga Velroyen
        fingerprints = nresult.payload.get(constants.NV_FILELIST, {})
2640 7352d33b Thomas Thrainer
        node_files = dict((vcluster.LocalizeVirtualPath(key), value)
2641 7352d33b Thomas Thrainer
                          for (key, value) in fingerprints.items())
2642 7352d33b Thomas Thrainer
        del fingerprints
2643 7352d33b Thomas Thrainer
2644 7352d33b Thomas Thrainer
      test = not (node_files and isinstance(node_files, dict))
2645 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEFILECHECK, node.name,
2646 1c3231aa Thomas Thrainer
                    "Node did not return file checksum data")
2647 7352d33b Thomas Thrainer
      if test:
2648 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2649 7352d33b Thomas Thrainer
        continue
2650 7352d33b Thomas Thrainer
2651 7352d33b Thomas Thrainer
      # Build per-checksum mapping from filename to nodes having it
2652 7352d33b Thomas Thrainer
      for (filename, checksum) in node_files.items():
2653 7352d33b Thomas Thrainer
        assert filename in nodefiles
2654 1c3231aa Thomas Thrainer
        fileinfo[filename].setdefault(checksum, set()).add(node.uuid)
2655 7352d33b Thomas Thrainer
2656 7352d33b Thomas Thrainer
    for (filename, checksums) in fileinfo.items():
2657 7352d33b Thomas Thrainer
      assert compat.all(len(i) > 10 for i in checksums), "Invalid checksum"
2658 7352d33b Thomas Thrainer
2659 7352d33b Thomas Thrainer
      # Nodes having the file
2660 1c3231aa Thomas Thrainer
      with_file = frozenset(node_uuid
2661 1c3231aa Thomas Thrainer
                            for node_uuids in fileinfo[filename].values()
2662 1c3231aa Thomas Thrainer
                            for node_uuid in node_uuids) - ignore_nodes
2663 7352d33b Thomas Thrainer
2664 7352d33b Thomas Thrainer
      expected_nodes = nodefiles[filename] - ignore_nodes
2665 7352d33b Thomas Thrainer
2666 7352d33b Thomas Thrainer
      # Nodes missing file
2667 7352d33b Thomas Thrainer
      missing_file = expected_nodes - with_file
2668 7352d33b Thomas Thrainer
2669 7352d33b Thomas Thrainer
      if filename in files_opt:
2670 7352d33b Thomas Thrainer
        # All or no nodes
2671 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file and missing_file != expected_nodes,
2672 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2673 1c3231aa Thomas Thrainer
                      "File %s is optional, but it must exist on all or no"
2674 1c3231aa Thomas Thrainer
                      " nodes (not found on %s)",
2675 1c3231aa Thomas Thrainer
                      filename,
2676 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2677 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2678 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2679 7352d33b Thomas Thrainer
      else:
2680 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file, constants.CV_ECLUSTERFILECHECK, None,
2681 1c3231aa Thomas Thrainer
                      "File %s is missing from node(s) %s", filename,
2682 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2683 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2684 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2685 7352d33b Thomas Thrainer
2686 7352d33b Thomas Thrainer
        # Warn if a node has a file it shouldn't
2687 7352d33b Thomas Thrainer
        unexpected = with_file - expected_nodes
2688 1c3231aa Thomas Thrainer
        self._ErrorIf(unexpected,
2689 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2690 1c3231aa Thomas Thrainer
                      "File %s should not exist on node(s) %s",
2691 1c3231aa Thomas Thrainer
                      filename, utils.CommaJoin(
2692 1c3231aa Thomas Thrainer
                        utils.NiceSort(map(self.cfg.GetNodeName, unexpected))))
2693 7352d33b Thomas Thrainer
2694 7352d33b Thomas Thrainer
      # See if there are multiple versions of the file
2695 7352d33b Thomas Thrainer
      test = len(checksums) > 1
2696 7352d33b Thomas Thrainer
      if test:
2697 7352d33b Thomas Thrainer
        variants = ["variant %s on %s" %
2698 1c3231aa Thomas Thrainer
                    (idx + 1,
2699 1c3231aa Thomas Thrainer
                     utils.CommaJoin(utils.NiceSort(
2700 1c3231aa Thomas Thrainer
                       map(self.cfg.GetNodeName, node_uuids))))
2701 1c3231aa Thomas Thrainer
                    for (idx, (checksum, node_uuids)) in
2702 7352d33b Thomas Thrainer
                      enumerate(sorted(checksums.items()))]
2703 7352d33b Thomas Thrainer
      else:
2704 7352d33b Thomas Thrainer
        variants = []
2705 7352d33b Thomas Thrainer
2706 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERFILECHECK, None,
2707 1c3231aa Thomas Thrainer
                    "File %s found with %s different checksums (%s)",
2708 1c3231aa Thomas Thrainer
                    filename, len(checksums), "; ".join(variants))
2709 7352d33b Thomas Thrainer
2710 9af7ece3 Helga Velroyen
  def _VerifyNodeDrbdHelper(self, ninfo, nresult, drbd_helper):
2711 9af7ece3 Helga Velroyen
    """Verify the drbd helper.
2712 7352d33b Thomas Thrainer

2713 7352d33b Thomas Thrainer
    """
2714 7352d33b Thomas Thrainer
    if drbd_helper:
2715 7352d33b Thomas Thrainer
      helper_result = nresult.get(constants.NV_DRBDHELPER, None)
2716 7352d33b Thomas Thrainer
      test = (helper_result is None)
2717 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2718 d0d7d7cf Thomas Thrainer
                    "no drbd usermode helper returned")
2719 7352d33b Thomas Thrainer
      if helper_result:
2720 7352d33b Thomas Thrainer
        status, payload = helper_result
2721 7352d33b Thomas Thrainer
        test = not status
2722 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2723 d0d7d7cf Thomas Thrainer
                      "drbd usermode helper check unsuccessful: %s", payload)
2724 7352d33b Thomas Thrainer
        test = status and (payload != drbd_helper)
2725 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2726 d0d7d7cf Thomas Thrainer
                      "wrong drbd usermode helper: %s", payload)
2727 7352d33b Thomas Thrainer
2728 9af7ece3 Helga Velroyen
  def _VerifyNodeDrbd(self, ninfo, nresult, instanceinfo, drbd_helper,
2729 9af7ece3 Helga Velroyen
                      drbd_map):
2730 9af7ece3 Helga Velroyen
    """Verifies and the node DRBD status.
2731 9af7ece3 Helga Velroyen

2732 9af7ece3 Helga Velroyen
    @type ninfo: L{objects.Node}
2733 9af7ece3 Helga Velroyen
    @param ninfo: the node to check
2734 9af7ece3 Helga Velroyen
    @param nresult: the remote results for the node
2735 9af7ece3 Helga Velroyen
    @param instanceinfo: the dict of instances
2736 9af7ece3 Helga Velroyen
    @param drbd_helper: the configured DRBD usermode helper
2737 9af7ece3 Helga Velroyen
    @param drbd_map: the DRBD map as returned by
2738 9af7ece3 Helga Velroyen
        L{ganeti.config.ConfigWriter.ComputeDRBDMap}
2739 9af7ece3 Helga Velroyen

2740 9af7ece3 Helga Velroyen
    """
2741 9af7ece3 Helga Velroyen
    self._VerifyNodeDrbdHelper(ninfo, nresult, drbd_helper)
2742 9af7ece3 Helga Velroyen
2743 7352d33b Thomas Thrainer
    # compute the DRBD minors
2744 7352d33b Thomas Thrainer
    node_drbd = {}
2745 da4a52a3 Thomas Thrainer
    for minor, inst_uuid in drbd_map[ninfo.uuid].items():
2746 da4a52a3 Thomas Thrainer
      test = inst_uuid not in instanceinfo
2747 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERCFG, None,
2748 da4a52a3 Thomas Thrainer
                    "ghost instance '%s' in temporary DRBD map", inst_uuid)
2749 7352d33b Thomas Thrainer
        # ghost instance should not be running, but otherwise we
2750 7352d33b Thomas Thrainer
        # don't give double warnings (both ghost instance and
2751 7352d33b Thomas Thrainer
        # unallocated minor in use)
2752 7352d33b Thomas Thrainer
      if test:
2753 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, False)
2754 7352d33b Thomas Thrainer
      else:
2755 da4a52a3 Thomas Thrainer
        instance = instanceinfo[inst_uuid]
2756 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, instance.disks_active)
2757 7352d33b Thomas Thrainer
2758 7352d33b Thomas Thrainer
    # and now check them
2759 7352d33b Thomas Thrainer
    used_minors = nresult.get(constants.NV_DRBDLIST, [])
2760 7352d33b Thomas Thrainer
    test = not isinstance(used_minors, (tuple, list))
2761 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2762 d0d7d7cf Thomas Thrainer
                  "cannot parse drbd status file: %s", str(used_minors))
2763 7352d33b Thomas Thrainer
    if test:
2764 7352d33b Thomas Thrainer
      # we cannot check drbd status
2765 7352d33b Thomas Thrainer
      return
2766 7352d33b Thomas Thrainer
2767 da4a52a3 Thomas Thrainer
    for minor, (inst_uuid, must_exist) in node_drbd.items():
2768 7352d33b Thomas Thrainer
      test = minor not in used_minors and must_exist
2769 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2770 da4a52a3 Thomas Thrainer
                    "drbd minor %d of instance %s is not active", minor,
2771 da4a52a3 Thomas Thrainer
                    self.cfg.GetInstanceName(inst_uuid))
2772 7352d33b Thomas Thrainer
    for minor in used_minors:
2773 7352d33b Thomas Thrainer
      test = minor not in node_drbd
2774 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2775 d0d7d7cf Thomas Thrainer
                    "unallocated drbd minor %d is in use", minor)
2776 7352d33b Thomas Thrainer
2777 7352d33b Thomas Thrainer
  def _UpdateNodeOS(self, ninfo, nresult, nimg):
2778 7352d33b Thomas Thrainer
    """Builds the node OS structures.
2779 7352d33b Thomas Thrainer

2780 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2781 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2782 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2783 7352d33b Thomas Thrainer
    @param nimg: the node image object
2784 7352d33b Thomas Thrainer

2785 7352d33b Thomas Thrainer
    """
2786 7352d33b Thomas Thrainer
    remote_os = nresult.get(constants.NV_OSLIST, None)
2787 7352d33b Thomas Thrainer
    test = (not isinstance(remote_os, list) or
2788 7352d33b Thomas Thrainer
            not compat.all(isinstance(v, list) and len(v) == 7
2789 7352d33b Thomas Thrainer
                           for v in remote_os))
2790 7352d33b Thomas Thrainer
2791 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2792 d0d7d7cf Thomas Thrainer
                  "node hasn't returned valid OS data")
2793 7352d33b Thomas Thrainer
2794 7352d33b Thomas Thrainer
    nimg.os_fail = test
2795 7352d33b Thomas Thrainer
2796 7352d33b Thomas Thrainer
    if test:
2797 7352d33b Thomas Thrainer
      return
2798 7352d33b Thomas Thrainer
2799 7352d33b Thomas Thrainer
    os_dict = {}
2800 7352d33b Thomas Thrainer
2801 7352d33b Thomas Thrainer
    for (name, os_path, status, diagnose,
2802 7352d33b Thomas Thrainer
         variants, parameters, api_ver) in nresult[constants.NV_OSLIST]:
2803 7352d33b Thomas Thrainer
2804 7352d33b Thomas Thrainer
      if name not in os_dict:
2805 7352d33b Thomas Thrainer
        os_dict[name] = []
2806 7352d33b Thomas Thrainer
2807 7352d33b Thomas Thrainer
      # parameters is a list of lists instead of list of tuples due to
2808 7352d33b Thomas Thrainer
      # JSON lacking a real tuple type, fix it:
2809 7352d33b Thomas Thrainer
      parameters = [tuple(v) for v in parameters]
2810 7352d33b Thomas Thrainer
      os_dict[name].append((os_path, status, diagnose,
2811 7352d33b Thomas Thrainer
                            set(variants), set(parameters), set(api_ver)))
2812 7352d33b Thomas Thrainer
2813 7352d33b Thomas Thrainer
    nimg.oslist = os_dict
2814 7352d33b Thomas Thrainer
2815 7352d33b Thomas Thrainer
  def _VerifyNodeOS(self, ninfo, nimg, base):
2816 7352d33b Thomas Thrainer
    """Verifies the node OS list.
2817 7352d33b Thomas Thrainer

2818 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2819 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2820 7352d33b Thomas Thrainer
    @param nimg: the node image object
2821 7352d33b Thomas Thrainer
    @param base: the 'template' node we match against (e.g. from the master)
2822 7352d33b Thomas Thrainer

2823 7352d33b Thomas Thrainer
    """
2824 7352d33b Thomas Thrainer
    assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?"
2825 7352d33b Thomas Thrainer
2826 7352d33b Thomas Thrainer
    beautify_params = lambda l: ["%s: %s" % (k, v) for (k, v) in l]
2827 7352d33b Thomas Thrainer
    for os_name, os_data in nimg.oslist.items():
2828 7352d33b Thomas Thrainer
      assert os_data, "Empty OS status for OS %s?!" % os_name
2829 7352d33b Thomas Thrainer
      f_path, f_status, f_diag, f_var, f_param, f_api = os_data[0]
2830 d0d7d7cf Thomas Thrainer
      self._ErrorIf(not f_status, constants.CV_ENODEOS, ninfo.name,
2831 d0d7d7cf Thomas Thrainer
                    "Invalid OS %s (located at %s): %s",
2832 d0d7d7cf Thomas Thrainer
                    os_name, f_path, f_diag)
2833 d0d7d7cf Thomas Thrainer
      self._ErrorIf(len(os_data) > 1, constants.CV_ENODEOS, ninfo.name,
2834 d0d7d7cf Thomas Thrainer
                    "OS '%s' has multiple entries"
2835 d0d7d7cf Thomas Thrainer
                    " (first one shadows the rest): %s",
2836 d0d7d7cf Thomas Thrainer
                    os_name, utils.CommaJoin([v[0] for v in os_data]))
2837 7352d33b Thomas Thrainer
      # comparisons with the 'base' image
2838 7352d33b Thomas Thrainer
      test = os_name not in base.oslist
2839 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2840 d0d7d7cf Thomas Thrainer
                    "Extra OS %s not present on reference node (%s)",
2841 d0d7d7cf Thomas Thrainer
                    os_name, self.cfg.GetNodeName(base.uuid))
2842 7352d33b Thomas Thrainer
      if test:
2843 7352d33b Thomas Thrainer
        continue
2844 7352d33b Thomas Thrainer
      assert base.oslist[os_name], "Base node has empty OS status?"
2845 7352d33b Thomas Thrainer
      _, b_status, _, b_var, b_param, b_api = base.oslist[os_name][0]
2846 7352d33b Thomas Thrainer
      if not b_status:
2847 7352d33b Thomas Thrainer
        # base OS is invalid, skipping
2848 7352d33b Thomas Thrainer
        continue
2849 7352d33b Thomas Thrainer
      for kind, a, b in [("API version", f_api, b_api),
2850 7352d33b Thomas Thrainer
                         ("variants list", f_var, b_var),
2851 7352d33b Thomas Thrainer
                         ("parameters", beautify_params(f_param),
2852 7352d33b Thomas Thrainer
                          beautify_params(b_param))]:
2853 d0d7d7cf Thomas Thrainer
        self._ErrorIf(a != b, constants.CV_ENODEOS, ninfo.name,
2854 d0d7d7cf Thomas Thrainer
                      "OS %s for %s differs from reference node %s:"
2855 d0d7d7cf Thomas Thrainer
                      " [%s] vs. [%s]", kind, os_name,
2856 d0d7d7cf Thomas Thrainer
                      self.cfg.GetNodeName(base.uuid),
2857 d0d7d7cf Thomas Thrainer
                      utils.CommaJoin(sorted(a)), utils.CommaJoin(sorted(b)))
2858 7352d33b Thomas Thrainer
2859 7352d33b Thomas Thrainer
    # check any missing OSes
2860 7352d33b Thomas Thrainer
    missing = set(base.oslist.keys()).difference(nimg.oslist.keys())
2861 d0d7d7cf Thomas Thrainer
    self._ErrorIf(missing, constants.CV_ENODEOS, ninfo.name,
2862 d0d7d7cf Thomas Thrainer
                  "OSes present on reference node %s"
2863 d0d7d7cf Thomas Thrainer
                  " but missing on this node: %s",
2864 d0d7d7cf Thomas Thrainer
                  self.cfg.GetNodeName(base.uuid), utils.CommaJoin(missing))
2865 7352d33b Thomas Thrainer
2866 13a6c760 Helga Velroyen
  def _VerifyAcceptedFileStoragePaths(self, ninfo, nresult, is_master):
2867 7352d33b Thomas Thrainer
    """Verifies paths in L{pathutils.FILE_STORAGE_PATHS_FILE}.
2868 7352d33b Thomas Thrainer

2869 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2870 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2871 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2872 7352d33b Thomas Thrainer
    @type is_master: bool
2873 7352d33b Thomas Thrainer
    @param is_master: Whether node is the master node
2874 7352d33b Thomas Thrainer

2875 7352d33b Thomas Thrainer
    """
2876 13a6c760 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2877 7352d33b Thomas Thrainer
    if (is_master and
2878 13a6c760 Helga Velroyen
        (cluster.IsFileStorageEnabled() or
2879 13a6c760 Helga Velroyen
         cluster.IsSharedFileStorageEnabled())):
2880 7352d33b Thomas Thrainer
      try:
2881 13a6c760 Helga Velroyen
        fspaths = nresult[constants.NV_ACCEPTED_STORAGE_PATHS]
2882 7352d33b Thomas Thrainer
      except KeyError:
2883 7352d33b Thomas Thrainer
        # This should never happen
2884 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2885 7352d33b Thomas Thrainer
                      "Node did not return forbidden file storage paths")
2886 7352d33b Thomas Thrainer
      else:
2887 d0d7d7cf Thomas Thrainer
        self._ErrorIf(fspaths, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2888 7352d33b Thomas Thrainer
                      "Found forbidden file storage paths: %s",
2889 7352d33b Thomas Thrainer
                      utils.CommaJoin(fspaths))
2890 7352d33b Thomas Thrainer
    else:
2891 13a6c760 Helga Velroyen
      self._ErrorIf(constants.NV_ACCEPTED_STORAGE_PATHS in nresult,
2892 d0d7d7cf Thomas Thrainer
                    constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2893 7352d33b Thomas Thrainer
                    "Node should not have returned forbidden file storage"
2894 7352d33b Thomas Thrainer
                    " paths")
2895 7352d33b Thomas Thrainer
2896 4b322a76 Helga Velroyen
  def _VerifyStoragePaths(self, ninfo, nresult, file_disk_template,
2897 4b322a76 Helga Velroyen
                          verify_key, error_key):
2898 9c1c3c19 Helga Velroyen
    """Verifies (file) storage paths.
2899 9c1c3c19 Helga Velroyen

2900 9c1c3c19 Helga Velroyen
    @type ninfo: L{objects.Node}
2901 9c1c3c19 Helga Velroyen
    @param ninfo: the node to check
2902 9c1c3c19 Helga Velroyen
    @param nresult: the remote results for the node
2903 4b322a76 Helga Velroyen
    @type file_disk_template: string
2904 4b322a76 Helga Velroyen
    @param file_disk_template: file-based disk template, whose directory
2905 4b322a76 Helga Velroyen
        is supposed to be verified
2906 4b322a76 Helga Velroyen
    @type verify_key: string
2907 4b322a76 Helga Velroyen
    @param verify_key: key for the verification map of this file
2908 4b322a76 Helga Velroyen
        verification step
2909 4b322a76 Helga Velroyen
    @param error_key: error key to be added to the verification results
2910 4b322a76 Helga Velroyen
        in case something goes wrong in this verification step
2911 9c1c3c19 Helga Velroyen

2912 9c1c3c19 Helga Velroyen
    """
2913 5a904197 Santi Raffa
    assert (file_disk_template in utils.storage.GetDiskTemplatesOfStorageTypes(
2914 5a904197 Santi Raffa
              constants.ST_FILE, constants.ST_SHARED_FILE
2915 5a904197 Santi Raffa
           ))
2916 5a904197 Santi Raffa
2917 9c1c3c19 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2918 4b322a76 Helga Velroyen
    if cluster.IsDiskTemplateEnabled(file_disk_template):
2919 9c1c3c19 Helga Velroyen
      self._ErrorIf(
2920 4b322a76 Helga Velroyen
          verify_key in nresult,
2921 4b322a76 Helga Velroyen
          error_key, ninfo.name,
2922 4b322a76 Helga Velroyen
          "The configured %s storage path is unusable: %s" %
2923 4b322a76 Helga Velroyen
          (file_disk_template, nresult.get(verify_key)))
2924 4b322a76 Helga Velroyen
2925 4b322a76 Helga Velroyen
  def _VerifyFileStoragePaths(self, ninfo, nresult):
2926 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2927 4b322a76 Helga Velroyen

2928 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2929 4b322a76 Helga Velroyen

2930 4b322a76 Helga Velroyen
    """
2931 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2932 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_FILE,
2933 4b322a76 Helga Velroyen
        constants.NV_FILE_STORAGE_PATH,
2934 4b322a76 Helga Velroyen
        constants.CV_ENODEFILESTORAGEPATHUNUSABLE)
2935 4b322a76 Helga Velroyen
2936 4b322a76 Helga Velroyen
  def _VerifySharedFileStoragePaths(self, ninfo, nresult):
2937 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2938 4b322a76 Helga Velroyen

2939 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2940 4b322a76 Helga Velroyen

2941 4b322a76 Helga Velroyen
    """
2942 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2943 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_SHARED_FILE,
2944 4b322a76 Helga Velroyen
        constants.NV_SHARED_FILE_STORAGE_PATH,
2945 4b322a76 Helga Velroyen
        constants.CV_ENODESHAREDFILESTORAGEPATHUNUSABLE)
2946 9c1c3c19 Helga Velroyen
2947 7352d33b Thomas Thrainer
  def _VerifyOob(self, ninfo, nresult):
2948 7352d33b Thomas Thrainer
    """Verifies out of band functionality of a node.
2949 7352d33b Thomas Thrainer

2950 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2951 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2952 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2953 7352d33b Thomas Thrainer

2954 7352d33b Thomas Thrainer
    """
2955 7352d33b Thomas Thrainer
    # We just have to verify the paths on master and/or master candidates
2956 7352d33b Thomas Thrainer
    # as the oob helper is invoked on the master
2957 7352d33b Thomas Thrainer
    if ((ninfo.master_candidate or ninfo.master_capable) and
2958 7352d33b Thomas Thrainer
        constants.NV_OOB_PATHS in nresult):
2959 7352d33b Thomas Thrainer
      for path_result in nresult[constants.NV_OOB_PATHS]:
2960 1c3231aa Thomas Thrainer
        self._ErrorIf(path_result, constants.CV_ENODEOOBPATH,
2961 d0d7d7cf Thomas Thrainer
                      ninfo.name, path_result)
2962 7352d33b Thomas Thrainer
2963 7352d33b Thomas Thrainer
  def _UpdateNodeVolumes(self, ninfo, nresult, nimg, vg_name):
2964 7352d33b Thomas Thrainer
    """Verifies and updates the node volume data.
2965 7352d33b Thomas Thrainer

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

2969 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2970 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2971 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2972 7352d33b Thomas Thrainer
    @param nimg: the node image object
2973 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2974 7352d33b Thomas Thrainer

2975 7352d33b Thomas Thrainer
    """
2976 7352d33b Thomas Thrainer
    nimg.lvm_fail = True
2977 7352d33b Thomas Thrainer
    lvdata = nresult.get(constants.NV_LVLIST, "Missing LV data")
2978 7352d33b Thomas Thrainer
    if vg_name is None:
2979 7352d33b Thomas Thrainer
      pass
2980 7352d33b Thomas Thrainer
    elif isinstance(lvdata, basestring):
2981 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2982 d0d7d7cf Thomas Thrainer
                    "LVM problem on node: %s", utils.SafeEncode(lvdata))
2983 7352d33b Thomas Thrainer
    elif not isinstance(lvdata, dict):
2984 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2985 d0d7d7cf Thomas Thrainer
                    "rpc call to node failed (lvlist)")
2986 7352d33b Thomas Thrainer
    else:
2987 7352d33b Thomas Thrainer
      nimg.volumes = lvdata
2988 7352d33b Thomas Thrainer
      nimg.lvm_fail = False
2989 7352d33b Thomas Thrainer
2990 7352d33b Thomas Thrainer
  def _UpdateNodeInstances(self, ninfo, nresult, nimg):
2991 7352d33b Thomas Thrainer
    """Verifies and updates the node instance list.
2992 7352d33b Thomas Thrainer

2993 7352d33b Thomas Thrainer
    If the listing was successful, then updates this node's instance
2994 7352d33b Thomas Thrainer
    list. Otherwise, it marks the RPC call as failed for the instance
2995 7352d33b Thomas Thrainer
    list key.
2996 7352d33b Thomas Thrainer

2997 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2998 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2999 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
3000 7352d33b Thomas Thrainer
    @param nimg: the node image object
3001 7352d33b Thomas Thrainer

3002 7352d33b Thomas Thrainer
    """
3003 7352d33b Thomas Thrainer
    idata = nresult.get(constants.NV_INSTANCELIST, None)
3004 7352d33b Thomas Thrainer
    test = not isinstance(idata, list)
3005 7352d33b Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
3006 7352d33b Thomas Thrainer
                  "rpc call to node failed (instancelist): %s",
3007 7352d33b Thomas Thrainer
                  utils.SafeEncode(str(idata)))
3008 7352d33b Thomas Thrainer
    if test:
3009 7352d33b Thomas Thrainer
      nimg.hyp_fail = True
3010 7352d33b Thomas Thrainer
    else:
3011 da4a52a3 Thomas Thrainer
      nimg.instances = [inst.uuid for (_, inst) in
3012 da4a52a3 Thomas Thrainer
                        self.cfg.GetMultiInstanceInfoByName(idata)]
3013 7352d33b Thomas Thrainer
3014 7352d33b Thomas Thrainer
  def _UpdateNodeInfo(self, ninfo, nresult, nimg, vg_name):
3015 7352d33b Thomas Thrainer
    """Verifies and computes a node information map
3016 7352d33b Thomas Thrainer

3017 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
3018 7352d33b Thomas Thrainer
    @param ninfo: the node to check
3019 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
3020 7352d33b Thomas Thrainer
    @param nimg: the node image object
3021 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
3022 7352d33b Thomas Thrainer

3023 7352d33b Thomas Thrainer
    """
3024 7352d33b Thomas Thrainer
    # try to read free memory (from the hypervisor)
3025 7352d33b Thomas Thrainer
    hv_info = nresult.get(constants.NV_HVINFO, None)
3026 7352d33b Thomas Thrainer
    test = not isinstance(hv_info, dict) or "memory_free" not in hv_info
3027 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
3028 d0d7d7cf Thomas Thrainer
                  "rpc call to node failed (hvinfo)")
3029 7352d33b Thomas Thrainer
    if not test:
3030 7352d33b Thomas Thrainer
      try:
3031 7352d33b Thomas Thrainer
        nimg.mfree = int(hv_info["memory_free"])
3032 7352d33b Thomas Thrainer
      except (ValueError, TypeError):
3033 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
3034 d0d7d7cf Thomas Thrainer
                      "node returned invalid nodeinfo, check hypervisor")
3035 7352d33b Thomas Thrainer
3036 7352d33b Thomas Thrainer
    # FIXME: devise a free space model for file based instances as well
3037 7352d33b Thomas Thrainer
    if vg_name is not None:
3038 7352d33b Thomas Thrainer
      test = (constants.NV_VGLIST not in nresult or
3039 7352d33b Thomas Thrainer
              vg_name not in nresult[constants.NV_VGLIST])
3040 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
3041 d0d7d7cf Thomas Thrainer
                    "node didn't return data for the volume group '%s'"
3042 d0d7d7cf Thomas Thrainer
                    " - it is either missing or broken", vg_name)
3043 7352d33b Thomas Thrainer
      if not test:
3044 7352d33b Thomas Thrainer
        try:
3045 7352d33b Thomas Thrainer
          nimg.dfree = int(nresult[constants.NV_VGLIST][vg_name])
3046 7352d33b Thomas Thrainer
        except (ValueError, TypeError):
3047 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
3048 d0d7d7cf Thomas Thrainer
                        "node returned invalid LVM info, check LVM status")
3049 7352d33b Thomas Thrainer
3050 1c3231aa Thomas Thrainer
  def _CollectDiskInfo(self, node_uuids, node_image, instanceinfo):
3051 7352d33b Thomas Thrainer
    """Gets per-disk status information for all instances.
3052 7352d33b Thomas Thrainer

3053 1c3231aa Thomas Thrainer
    @type node_uuids: list of strings
3054 1c3231aa Thomas Thrainer
    @param node_uuids: Node UUIDs
3055 da4a52a3 Thomas Thrainer
    @type node_image: dict of (UUID, L{objects.Node})
3056 7352d33b Thomas Thrainer
    @param node_image: Node objects
3057 da4a52a3 Thomas Thrainer
    @type instanceinfo: dict of (UUID, L{objects.Instance})
3058 7352d33b Thomas Thrainer
    @param instanceinfo: Instance objects
3059 7352d33b Thomas Thrainer
    @rtype: {instance: {node: [(succes, payload)]}}
3060 7352d33b Thomas Thrainer
    @return: a dictionary of per-instance dictionaries with nodes as
3061 7352d33b Thomas Thrainer
        keys and disk information as values; the disk information is a
3062 7352d33b Thomas Thrainer
        list of tuples (success, payload)
3063 7352d33b Thomas Thrainer

3064 7352d33b Thomas Thrainer
    """
3065 7352d33b Thomas Thrainer
    node_disks = {}
3066 0c3d9c7c Thomas Thrainer
    node_disks_dev_inst_only = {}
3067 7352d33b Thomas Thrainer
    diskless_instances = set()
3068 099ed3b2 Klaus Aehlig
    nodisk_instances = set()
3069 7352d33b Thomas Thrainer
    diskless = constants.DT_DISKLESS
3070 7352d33b Thomas Thrainer
3071 1c3231aa Thomas Thrainer
    for nuuid in node_uuids:
3072 da4a52a3 Thomas Thrainer
      node_inst_uuids = list(itertools.chain(node_image[nuuid].pinst,
3073 da4a52a3 Thomas Thrainer
                                             node_image[nuuid].sinst))
3074 da4a52a3 Thomas Thrainer
      diskless_instances.update(uuid for uuid in node_inst_uuids
3075 da4a52a3 Thomas Thrainer
                                if instanceinfo[uuid].disk_template == diskless)
3076 da4a52a3 Thomas Thrainer
      disks = [(inst_uuid, disk)
3077 da4a52a3 Thomas Thrainer
               for inst_uuid in node_inst_uuids
3078 da4a52a3 Thomas Thrainer
               for disk in instanceinfo[inst_uuid].disks]
3079 7352d33b Thomas Thrainer
3080 7352d33b Thomas Thrainer
      if not disks:
3081 099ed3b2 Klaus Aehlig
        nodisk_instances.update(uuid for uuid in node_inst_uuids
3082 099ed3b2 Klaus Aehlig
                                if instanceinfo[uuid].disk_template != diskless)
3083 7352d33b Thomas Thrainer
        # No need to collect data
3084 7352d33b Thomas Thrainer
        continue
3085 7352d33b Thomas Thrainer
3086 1c3231aa Thomas Thrainer
      node_disks[nuuid] = disks
3087 7352d33b Thomas Thrainer
3088 7352d33b Thomas Thrainer
      # _AnnotateDiskParams makes already copies of the disks
3089 0c3d9c7c Thomas Thrainer
      dev_inst_only = []
3090 da4a52a3 Thomas Thrainer
      for (inst_uuid, dev) in disks:
3091 da4a52a3 Thomas Thrainer
        (anno_disk,) = AnnotateDiskParams(instanceinfo[inst_uuid], [dev],
3092 da4a52a3 Thomas Thrainer
                                          self.cfg)
3093 0c3d9c7c Thomas Thrainer
        dev_inst_only.append((anno_disk, instanceinfo[inst_uuid]))
3094 7352d33b Thomas Thrainer
3095 0c3d9c7c Thomas Thrainer
      node_disks_dev_inst_only[nuuid] = dev_inst_only
3096 7352d33b Thomas Thrainer
3097 0c3d9c7c Thomas Thrainer
    assert len(node_disks) == len(node_disks_dev_inst_only)
3098 7352d33b Thomas Thrainer
3099 7352d33b Thomas Thrainer
    # Collect data from all nodes with disks
3100 0c3d9c7c Thomas Thrainer
    result = self.rpc.call_blockdev_getmirrorstatus_multi(
3101 0c3d9c7c Thomas Thrainer
               node_disks.keys(), node_disks_dev_inst_only)
3102 7352d33b Thomas Thrainer
3103 7352d33b Thomas Thrainer
    assert len(result) == len(node_disks)
3104 7352d33b Thomas Thrainer
3105 7352d33b Thomas Thrainer
    instdisk = {}
3106 7352d33b Thomas Thrainer
3107 1c3231aa Thomas Thrainer
    for (nuuid, nres) in result.items():
3108 1c3231aa Thomas Thrainer
      node = self.cfg.GetNodeInfo(nuuid)
3109 1c3231aa Thomas Thrainer
      disks = node_disks[node.uuid]
3110 7352d33b Thomas Thrainer
3111 7352d33b Thomas Thrainer
      if nres.offline:
3112 7352d33b Thomas Thrainer
        # No data from this node
3113 7352d33b Thomas Thrainer
        data = len(disks) * [(False, "node offline")]
3114 7352d33b Thomas Thrainer
      else:
3115 7352d33b Thomas Thrainer
        msg = nres.fail_msg
3116 1c3231aa Thomas Thrainer
        self._ErrorIf(msg, constants.CV_ENODERPC, node.name,
3117 1c3231aa Thomas Thrainer
                      "while getting disk information: %s", msg)
3118 7352d33b Thomas Thrainer
        if msg:
3119 7352d33b Thomas Thrainer
          # No data from this node
3120 7352d33b Thomas Thrainer
          data = len(disks) * [(False, msg)]
3121 7352d33b Thomas Thrainer
        else:
3122 7352d33b Thomas Thrainer
          data = []
3123 7352d33b Thomas Thrainer
          for idx, i in enumerate(nres.payload):
3124 7352d33b Thomas Thrainer
            if isinstance(i, (tuple, list)) and len(i) == 2:
3125 7352d33b Thomas Thrainer
              data.append(i)
3126 7352d33b Thomas Thrainer
            else:
3127 7352d33b Thomas Thrainer
              logging.warning("Invalid result from node %s, entry %d: %s",
3128 1c3231aa Thomas Thrainer
                              node.name, idx, i)
3129 7352d33b Thomas Thrainer
              data.append((False, "Invalid result from the remote node"))
3130 7352d33b Thomas Thrainer
3131 da4a52a3 Thomas Thrainer
      for ((inst_uuid, _), status) in zip(disks, data):
3132 da4a52a3 Thomas Thrainer
        instdisk.setdefault(inst_uuid, {}).setdefault(node.uuid, []) \
3133 da4a52a3 Thomas Thrainer
          .append(status)
3134 7352d33b Thomas Thrainer
3135 7352d33b Thomas Thrainer
    # Add empty entries for diskless instances.
3136 da4a52a3 Thomas Thrainer
    for inst_uuid in diskless_instances:
3137 da4a52a3 Thomas Thrainer
      assert inst_uuid not in instdisk
3138 da4a52a3 Thomas Thrainer
      instdisk[inst_uuid] = {}
3139 099ed3b2 Klaus Aehlig
    # ...and disk-full instances that happen to have no disks
3140 099ed3b2 Klaus Aehlig
    for inst_uuid in nodisk_instances:
3141 099ed3b2 Klaus Aehlig
      assert inst_uuid not in instdisk
3142 099ed3b2 Klaus Aehlig
      instdisk[inst_uuid] = {}
3143 7352d33b Thomas Thrainer
3144 7352d33b Thomas Thrainer
    assert compat.all(len(statuses) == len(instanceinfo[inst].disks) and
3145 1c3231aa Thomas Thrainer
                      len(nuuids) <= len(instanceinfo[inst].all_nodes) and
3146 7352d33b Thomas Thrainer
                      compat.all(isinstance(s, (tuple, list)) and
3147 7352d33b Thomas Thrainer
                                 len(s) == 2 for s in statuses)
3148 1c3231aa Thomas Thrainer
                      for inst, nuuids in instdisk.items()
3149 1c3231aa Thomas Thrainer
                      for nuuid, statuses in nuuids.items())
3150 7352d33b Thomas Thrainer
    if __debug__:
3151 7352d33b Thomas Thrainer
      instdisk_keys = set(instdisk)
3152 7352d33b Thomas Thrainer
      instanceinfo_keys = set(instanceinfo)
3153 7352d33b Thomas Thrainer
      assert instdisk_keys == instanceinfo_keys, \
3154 7352d33b Thomas Thrainer
        ("instdisk keys (%s) do not match instanceinfo keys (%s)" %
3155 7352d33b Thomas Thrainer
         (instdisk_keys, instanceinfo_keys))
3156 7352d33b Thomas Thrainer
3157 7352d33b Thomas Thrainer
    return instdisk
3158 7352d33b Thomas Thrainer
3159 7352d33b Thomas Thrainer
  @staticmethod
3160 7352d33b Thomas Thrainer
  def _SshNodeSelector(group_uuid, all_nodes):
3161 7352d33b Thomas Thrainer
    """Create endless iterators for all potential SSH check hosts.
3162 7352d33b Thomas Thrainer

3163 7352d33b Thomas Thrainer
    """
3164 7352d33b Thomas Thrainer
    nodes = [node for node in all_nodes
3165 7352d33b Thomas Thrainer
             if (node.group != group_uuid and
3166 7352d33b Thomas Thrainer
                 not node.offline)]
3167 7352d33b Thomas Thrainer
    keyfunc = operator.attrgetter("group")
3168 7352d33b Thomas Thrainer
3169 7352d33b Thomas Thrainer
    return map(itertools.cycle,
3170 7352d33b Thomas Thrainer
               [sorted(map(operator.attrgetter("name"), names))
3171 7352d33b Thomas Thrainer
                for _, names in itertools.groupby(sorted(nodes, key=keyfunc),
3172 7352d33b Thomas Thrainer
                                                  keyfunc)])
3173 7352d33b Thomas Thrainer
3174 7352d33b Thomas Thrainer
  @classmethod
3175 7352d33b Thomas Thrainer
  def _SelectSshCheckNodes(cls, group_nodes, group_uuid, all_nodes):
3176 7352d33b Thomas Thrainer
    """Choose which nodes should talk to which other nodes.
3177 7352d33b Thomas Thrainer

3178 7352d33b Thomas Thrainer
    We will make nodes contact all nodes in their group, and one node from
3179 7352d33b Thomas Thrainer
    every other group.
3180 7352d33b Thomas Thrainer

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

3185 7352d33b Thomas Thrainer
    """
3186 7352d33b Thomas Thrainer
    online_nodes = sorted(node.name for node in group_nodes if not node.offline)
3187 7352d33b Thomas Thrainer
    sel = cls._SshNodeSelector(group_uuid, all_nodes)
3188 7352d33b Thomas Thrainer
3189 7352d33b Thomas Thrainer
    return (online_nodes,
3190 7352d33b Thomas Thrainer
            dict((name, sorted([i.next() for i in sel]))
3191 7352d33b Thomas Thrainer
                 for name in online_nodes))
3192 7352d33b Thomas Thrainer
3193 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
3194 7352d33b Thomas Thrainer
    """Build hooks env.
3195 7352d33b Thomas Thrainer

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

3199 7352d33b Thomas Thrainer
    """
3200 7352d33b Thomas Thrainer
    env = {
3201 7352d33b Thomas Thrainer
      "CLUSTER_TAGS": " ".join(self.cfg.GetClusterInfo().GetTags()),
3202 7352d33b Thomas Thrainer
      }
3203 7352d33b Thomas Thrainer
3204 7352d33b Thomas Thrainer
    env.update(("NODE_TAGS_%s" % node.name, " ".join(node.GetTags()))
3205 7352d33b Thomas Thrainer
               for node in self.my_node_info.values())
3206 7352d33b Thomas Thrainer
3207 7352d33b Thomas Thrainer
    return env
3208 7352d33b Thomas Thrainer
3209 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
3210 7352d33b Thomas Thrainer
    """Build hooks nodes.
3211 7352d33b Thomas Thrainer

3212 7352d33b Thomas Thrainer
    """
3213 1c3231aa Thomas Thrainer
    return ([], list(self.my_node_info.keys()))
3214 7352d33b Thomas Thrainer
3215 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
3216 7352d33b Thomas Thrainer
    """Verify integrity of the node group, performing various test on nodes.
3217 7352d33b Thomas Thrainer

3218 7352d33b Thomas Thrainer
    """
3219 7352d33b Thomas Thrainer
    # This method has too many local variables. pylint: disable=R0914
3220 7352d33b Thomas Thrainer
    feedback_fn("* Verifying group '%s'" % self.group_info.name)
3221 7352d33b Thomas Thrainer
3222 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
3223 7352d33b Thomas Thrainer
      # empty node group
3224 7352d33b Thomas Thrainer
      feedback_fn("* Empty node group, skipping verification")
3225 7352d33b Thomas Thrainer
      return True
3226 7352d33b Thomas Thrainer
3227 7352d33b Thomas Thrainer
    self.bad = False
3228 7352d33b Thomas Thrainer
    verbose = self.op.verbose
3229 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
3230 7352d33b Thomas Thrainer
3231 7352d33b Thomas Thrainer
    vg_name = self.cfg.GetVGName()
3232 7352d33b Thomas Thrainer
    drbd_helper = self.cfg.GetDRBDHelper()
3233 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
3234 7352d33b Thomas Thrainer
    hypervisors = cluster.enabled_hypervisors
3235 1c3231aa Thomas Thrainer
    node_data_list = self.my_node_info.values()
3236 7352d33b Thomas Thrainer
3237 7352d33b Thomas Thrainer
    i_non_redundant = [] # Non redundant instances
3238 7352d33b Thomas Thrainer
    i_non_a_balanced = [] # Non auto-balanced instances
3239 7352d33b Thomas Thrainer
    i_offline = 0 # Count of offline instances
3240 7352d33b Thomas Thrainer
    n_offline = 0 # Count of offline nodes
3241 7352d33b Thomas Thrainer
    n_drained = 0 # Count of nodes being drained
3242 7352d33b Thomas Thrainer
    node_vol_should = {}
3243 7352d33b Thomas Thrainer
3244 7352d33b Thomas Thrainer
    # FIXME: verify OS list
3245 7352d33b Thomas Thrainer
3246 7352d33b Thomas Thrainer
    # File verification
3247 5eacbcae Thomas Thrainer
    filemap = ComputeAncillaryFiles(cluster, False)
3248 7352d33b Thomas Thrainer
3249 7352d33b Thomas Thrainer
    # do local checksums
3250 1c3231aa Thomas Thrainer
    master_node_uuid = self.master_node = self.cfg.GetMasterNode()
3251 7352d33b Thomas Thrainer
    master_ip = self.cfg.GetMasterIP()
3252 7352d33b Thomas Thrainer
3253 1c3231aa Thomas Thrainer
    feedback_fn("* Gathering data (%d nodes)" % len(self.my_node_uuids))
3254 7352d33b Thomas Thrainer
3255 7352d33b Thomas Thrainer
    user_scripts = []
3256 7352d33b Thomas Thrainer
    if self.cfg.GetUseExternalMipScript():
3257 7352d33b Thomas Thrainer
      user_scripts.append(pathutils.EXTERNAL_MASTER_SETUP_SCRIPT)
3258 7352d33b Thomas Thrainer
3259 7352d33b Thomas Thrainer
    node_verify_param = {
3260 7352d33b Thomas Thrainer
      constants.NV_FILELIST:
3261 7352d33b Thomas Thrainer
        map(vcluster.MakeVirtualPath,
3262 7352d33b Thomas Thrainer
            utils.UniqueSequence(filename
3263 7352d33b Thomas Thrainer
                                 for files in filemap
3264 7352d33b Thomas Thrainer
                                 for filename in files)),
3265 7352d33b Thomas Thrainer
      constants.NV_NODELIST:
3266 7352d33b Thomas Thrainer
        self._SelectSshCheckNodes(node_data_list, self.group_uuid,
3267 7352d33b Thomas Thrainer
                                  self.all_node_info.values()),
3268 7352d33b Thomas Thrainer
      constants.NV_HYPERVISOR: hypervisors,
3269 7352d33b Thomas Thrainer
      constants.NV_HVPARAMS:
3270 7352d33b Thomas Thrainer
        _GetAllHypervisorParameters(cluster, self.all_inst_info.values()),
3271 7352d33b Thomas Thrainer
      constants.NV_NODENETTEST: [(node.name, node.primary_ip, node.secondary_ip)
3272 7352d33b Thomas Thrainer
                                 for node in node_data_list
3273 7352d33b Thomas Thrainer
                                 if not node.offline],
3274 7352d33b Thomas Thrainer
      constants.NV_INSTANCELIST: hypervisors,
3275 7352d33b Thomas Thrainer
      constants.NV_VERSION: None,
3276 7352d33b Thomas Thrainer
      constants.NV_HVINFO: self.cfg.GetHypervisorType(),
3277 7352d33b Thomas Thrainer
      constants.NV_NODESETUP: None,
3278 7352d33b Thomas Thrainer
      constants.NV_TIME: None,
3279 1c3231aa Thomas Thrainer
      constants.NV_MASTERIP: (self.cfg.GetMasterNodeName(), master_ip),
3280 7352d33b Thomas Thrainer
      constants.NV_OSLIST: None,
3281 7352d33b Thomas Thrainer
      constants.NV_VMNODES: self.cfg.GetNonVmCapableNodeList(),
3282 7352d33b Thomas Thrainer
      constants.NV_USERSCRIPTS: user_scripts,
3283 a6c43c02 Helga Velroyen
      constants.NV_CLIENT_CERT: None,
3284 7352d33b Thomas Thrainer
      }
3285 7352d33b Thomas Thrainer
3286 7352d33b Thomas Thrainer
    if vg_name is not None:
3287 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_VGLIST] = None
3288 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_LVLIST] = vg_name
3289 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_PVLIST] = [vg_name]
3290 7352d33b Thomas Thrainer
3291 9af7ece3 Helga Velroyen
    if cluster.IsDiskTemplateEnabled(constants.DT_DRBD8):
3292 9af7ece3 Helga Velroyen
      if drbd_helper:
3293 9af7ece3 Helga Velroyen
        node_verify_param[constants.NV_DRBDVERSION] = None
3294 9af7ece3 Helga Velroyen
        node_verify_param[constants.NV_DRBDLIST] = None
3295 9af7ece3 Helga Velroyen
        node_verify_param[constants.NV_DRBDHELPER] = drbd_helper
3296 7352d33b Thomas Thrainer
3297 850c53f1 Helga Velroyen
    if cluster.IsFileStorageEnabled() or \
3298 850c53f1 Helga Velroyen
        cluster.IsSharedFileStorageEnabled():
3299 7352d33b Thomas Thrainer
      # Load file storage paths only from master node
3300 13a6c760 Helga Velroyen
      node_verify_param[constants.NV_ACCEPTED_STORAGE_PATHS] = \
3301 1c3231aa Thomas Thrainer
        self.cfg.GetMasterNodeName()
3302 13a6c760 Helga Velroyen
      if cluster.IsFileStorageEnabled():
3303 13a6c760 Helga Velroyen
        node_verify_param[constants.NV_FILE_STORAGE_PATH] = \
3304 13a6c760 Helga Velroyen
          cluster.file_storage_dir
3305 7352d33b Thomas Thrainer
3306 7352d33b Thomas Thrainer
    # bridge checks
3307 7352d33b Thomas Thrainer
    # FIXME: this needs to be changed per node-group, not cluster-wide
3308 7352d33b Thomas Thrainer
    bridges = set()
3309 7352d33b Thomas Thrainer
    default_nicpp = cluster.nicparams[constants.PP_DEFAULT]
3310 7352d33b Thomas Thrainer
    if default_nicpp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
3311 7352d33b Thomas Thrainer
      bridges.add(default_nicpp[constants.NIC_LINK])
3312 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_info.values():
3313 da4a52a3 Thomas Thrainer
      for nic in inst_uuid.nics:
3314 7352d33b Thomas Thrainer
        full_nic = cluster.SimpleFillNIC(nic.nicparams)
3315 7352d33b Thomas Thrainer
        if full_nic[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
3316 7352d33b Thomas Thrainer
          bridges.add(full_nic[constants.NIC_LINK])
3317 7352d33b Thomas Thrainer
3318 7352d33b Thomas Thrainer
    if bridges:
3319 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_BRIDGES] = list(bridges)
3320 7352d33b Thomas Thrainer
3321 7352d33b Thomas Thrainer
    # Build our expected cluster state
3322 1c3231aa Thomas Thrainer
    node_image = dict((node.uuid, self.NodeImage(offline=node.offline,
3323 1c3231aa Thomas Thrainer
                                                 uuid=node.uuid,
3324 7352d33b Thomas Thrainer
                                                 vm_capable=node.vm_capable))
3325 7352d33b Thomas Thrainer
                      for node in node_data_list)
3326 7352d33b Thomas Thrainer
3327 7352d33b Thomas Thrainer
    # Gather OOB paths
3328 7352d33b Thomas Thrainer
    oob_paths = []
3329 7352d33b Thomas Thrainer
    for node in self.all_node_info.values():
3330 5eacbcae Thomas Thrainer
      path = SupportsOob(self.cfg, node)
3331 7352d33b Thomas Thrainer
      if path and path not in oob_paths:
3332 7352d33b Thomas Thrainer
        oob_paths.append(path)
3333 7352d33b Thomas Thrainer
3334 7352d33b Thomas Thrainer
    if oob_paths:
3335 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_OOB_PATHS] = oob_paths
3336 7352d33b Thomas Thrainer
3337 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_uuids:
3338 da4a52a3 Thomas Thrainer
      instance = self.my_inst_info[inst_uuid]
3339 da4a52a3 Thomas Thrainer
      if instance.admin_state == constants.ADMINST_OFFLINE:
3340 7352d33b Thomas Thrainer
        i_offline += 1
3341 7352d33b Thomas Thrainer
3342 da4a52a3 Thomas Thrainer
      for nuuid in instance.all_nodes:
3343 1c3231aa Thomas Thrainer
        if nuuid not in node_image:
3344 1c3231aa Thomas Thrainer
          gnode = self.NodeImage(uuid=nuuid)
3345 1c3231aa Thomas Thrainer
          gnode.ghost = (nuuid not in self.all_node_info)
3346 1c3231aa Thomas Thrainer
          node_image[nuuid] = gnode
3347 7352d33b Thomas Thrainer
3348 da4a52a3 Thomas Thrainer
      instance.MapLVsByNode(node_vol_should)
3349 7352d33b Thomas Thrainer
3350 da4a52a3 Thomas Thrainer
      pnode = instance.primary_node
3351 da4a52a3 Thomas Thrainer
      node_image[pnode].pinst.append(instance.uuid)
3352 7352d33b Thomas Thrainer
3353 da4a52a3 Thomas Thrainer
      for snode in instance.secondary_nodes:
3354 7352d33b Thomas Thrainer
        nimg = node_image[snode]
3355 da4a52a3 Thomas Thrainer
        nimg.sinst.append(instance.uuid)
3356 7352d33b Thomas Thrainer
        if pnode not in nimg.sbp:
3357 7352d33b Thomas Thrainer
          nimg.sbp[pnode] = []
3358 da4a52a3 Thomas Thrainer
        nimg.sbp[pnode].append(instance.uuid)
3359 7352d33b Thomas Thrainer
3360 1c3231aa Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg,
3361 1c3231aa Thomas Thrainer
                                               self.my_node_info.keys())
3362 7352d33b Thomas Thrainer
    # The value of exclusive_storage should be the same across the group, so if
3363 7352d33b Thomas Thrainer
    # it's True for at least a node, we act as if it were set for all the nodes
3364 7352d33b Thomas Thrainer
    self._exclusive_storage = compat.any(es_flags.values())
3365 7352d33b Thomas Thrainer
    if self._exclusive_storage:
3366 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_EXCLUSIVEPVS] = True
3367 7352d33b Thomas Thrainer
3368 a9f33339 Petr Pudlak
    node_group_uuids = dict(map(lambda n: (n.name, n.group),
3369 a9f33339 Petr Pudlak
                                self.cfg.GetAllNodesInfo().values()))
3370 a9f33339 Petr Pudlak
    groups_config = self.cfg.GetAllNodeGroupsInfoDict()
3371 a9f33339 Petr Pudlak
3372 7352d33b Thomas Thrainer
    # At this point, we have the in-memory data structures complete,
3373 7352d33b Thomas Thrainer
    # except for the runtime information, which we'll gather next
3374 7352d33b Thomas Thrainer
3375 7352d33b Thomas Thrainer
    # Due to the way our RPC system works, exact response times cannot be
3376 7352d33b Thomas Thrainer
    # guaranteed (e.g. a broken node could run into a timeout). By keeping the
3377 7352d33b Thomas Thrainer
    # time before and after executing the request, we can at least have a time
3378 7352d33b Thomas Thrainer
    # window.
3379 7352d33b Thomas Thrainer
    nvinfo_starttime = time.time()
3380 1c3231aa Thomas Thrainer
    all_nvinfo = self.rpc.call_node_verify(self.my_node_uuids,
3381 7352d33b Thomas Thrainer
                                           node_verify_param,
3382 5b0dfcef Helga Velroyen
                                           self.cfg.GetClusterName(),
3383 a9f33339 Petr Pudlak
                                           self.cfg.GetClusterInfo().hvparams,
3384 a9f33339 Petr Pudlak
                                           node_group_uuids,
3385 a9f33339 Petr Pudlak
                                           groups_config)
3386 7352d33b Thomas Thrainer
    nvinfo_endtime = time.time()
3387 7352d33b Thomas Thrainer
3388 7352d33b Thomas Thrainer
    if self.extra_lv_nodes and vg_name is not None:
3389 7352d33b Thomas Thrainer
      extra_lv_nvinfo = \
3390 7352d33b Thomas Thrainer
          self.rpc.call_node_verify(self.extra_lv_nodes,
3391 7352d33b Thomas Thrainer
                                    {constants.NV_LVLIST: vg_name},
3392 5b0dfcef Helga Velroyen
                                    self.cfg.GetClusterName(),
3393 a9f33339 Petr Pudlak
                                    self.cfg.GetClusterInfo().hvparams,
3394 a9f33339 Petr Pudlak
                                    node_group_uuids,
3395 a9f33339 Petr Pudlak
                                    groups_config)
3396 7352d33b Thomas Thrainer
    else:
3397 7352d33b Thomas Thrainer
      extra_lv_nvinfo = {}
3398 7352d33b Thomas Thrainer
3399 7352d33b Thomas Thrainer
    all_drbd_map = self.cfg.ComputeDRBDMap()
3400 7352d33b Thomas Thrainer
3401 7352d33b Thomas Thrainer
    feedback_fn("* Gathering disk information (%s nodes)" %
3402 1c3231aa Thomas Thrainer
                len(self.my_node_uuids))
3403 1c3231aa Thomas Thrainer
    instdisk = self._CollectDiskInfo(self.my_node_info.keys(), node_image,
3404 7352d33b Thomas Thrainer
                                     self.my_inst_info)
3405 7352d33b Thomas Thrainer
3406 7352d33b Thomas Thrainer
    feedback_fn("* Verifying configuration file consistency")
3407 7352d33b Thomas Thrainer
3408 d5104ca4 Helga Velroyen
    self._VerifyClientCertificates(self.my_node_info.values(), all_nvinfo)
3409 7352d33b Thomas Thrainer
    # If not all nodes are being checked, we need to make sure the master node
3410 7352d33b Thomas Thrainer
    # and a non-checked vm_capable node are in the list.
3411 1c3231aa Thomas Thrainer
    absent_node_uuids = set(self.all_node_info).difference(self.my_node_info)
3412 1c3231aa Thomas Thrainer
    if absent_node_uuids:
3413 7352d33b Thomas Thrainer
      vf_nvinfo = all_nvinfo.copy()
3414 7352d33b Thomas Thrainer
      vf_node_info = list(self.my_node_info.values())
3415 1c3231aa Thomas Thrainer
      additional_node_uuids = []
3416 1c3231aa Thomas Thrainer
      if master_node_uuid not in self.my_node_info:
3417 1c3231aa Thomas Thrainer
        additional_node_uuids.append(master_node_uuid)
3418 1c3231aa Thomas Thrainer
        vf_node_info.append(self.all_node_info[master_node_uuid])
3419 7352d33b Thomas Thrainer
      # Add the first vm_capable node we find which is not included,
3420 7352d33b Thomas Thrainer
      # excluding the master node (which we already have)
3421 1c3231aa Thomas Thrainer
      for node_uuid in absent_node_uuids:
3422 1c3231aa Thomas Thrainer
        nodeinfo = self.all_node_info[node_uuid]
3423 7352d33b Thomas Thrainer
        if (nodeinfo.vm_capable and not nodeinfo.offline and
3424 1c3231aa Thomas Thrainer
            node_uuid != master_node_uuid):
3425 1c3231aa Thomas Thrainer
          additional_node_uuids.append(node_uuid)
3426 1c3231aa Thomas Thrainer
          vf_node_info.append(self.all_node_info[node_uuid])
3427 7352d33b Thomas Thrainer
          break
3428 7352d33b Thomas Thrainer
      key = constants.NV_FILELIST
3429 5b0dfcef Helga Velroyen
      vf_nvinfo.update(self.rpc.call_node_verify(
3430 1c3231aa Thomas Thrainer
         additional_node_uuids, {key: node_verify_param[key]},
3431 a9f33339 Petr Pudlak
         self.cfg.GetClusterName(), self.cfg.GetClusterInfo().hvparams,
3432 a9f33339 Petr Pudlak
         node_group_uuids,
3433 a9f33339 Petr Pudlak
         groups_config))
3434 7352d33b Thomas Thrainer
    else:
3435 7352d33b Thomas Thrainer
      vf_nvinfo = all_nvinfo
3436 7352d33b Thomas Thrainer
      vf_node_info = self.my_node_info.values()
3437 7352d33b Thomas Thrainer
3438 1c3231aa Thomas Thrainer
    self._VerifyFiles(vf_node_info, master_node_uuid, vf_nvinfo, filemap)
3439 7352d33b Thomas Thrainer
3440 7352d33b Thomas Thrainer
    feedback_fn("* Verifying node status")
3441 7352d33b Thomas Thrainer
3442 7352d33b Thomas Thrainer
    refos_img = None
3443 7352d33b Thomas Thrainer
3444 7352d33b Thomas Thrainer
    for node_i in node_data_list:
3445 1c3231aa Thomas Thrainer
      nimg = node_image[node_i.uuid]
3446 7352d33b Thomas Thrainer
3447 7352d33b Thomas Thrainer
      if node_i.offline:
3448 7352d33b Thomas Thrainer
        if verbose:
3449 1c3231aa Thomas Thrainer
          feedback_fn("* Skipping offline node %s" % (node_i.name,))
3450 7352d33b Thomas Thrainer
        n_offline += 1
3451 7352d33b Thomas Thrainer
        continue
3452 7352d33b Thomas Thrainer
3453 1c3231aa Thomas Thrainer
      if node_i.uuid == master_node_uuid:
3454 7352d33b Thomas Thrainer
        ntype = "master"
3455 7352d33b Thomas Thrainer
      elif node_i.master_candidate:
3456 7352d33b Thomas Thrainer
        ntype = "master candidate"
3457 7352d33b Thomas Thrainer
      elif node_i.drained:
3458 7352d33b Thomas Thrainer
        ntype = "drained"
3459 7352d33b Thomas Thrainer
        n_drained += 1
3460 7352d33b Thomas Thrainer
      else:
3461 7352d33b Thomas Thrainer
        ntype = "regular"
3462 7352d33b Thomas Thrainer
      if verbose:
3463 1c3231aa Thomas Thrainer
        feedback_fn("* Verifying node %s (%s)" % (node_i.name, ntype))
3464 7352d33b Thomas Thrainer
3465 1c3231aa Thomas Thrainer
      msg = all_nvinfo[node_i.uuid].fail_msg
3466 d0d7d7cf Thomas Thrainer
      self._ErrorIf(msg, constants.CV_ENODERPC, node_i.name,
3467 d0d7d7cf Thomas Thrainer
                    "while contacting node: %s", msg)
3468 7352d33b Thomas Thrainer
      if msg:
3469 7352d33b Thomas Thrainer
        nimg.rpc_fail = True
3470 7352d33b Thomas Thrainer
        continue
3471 7352d33b Thomas Thrainer
3472 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node_i.uuid].payload
3473 7352d33b Thomas Thrainer
3474 7352d33b Thomas Thrainer
      nimg.call_ok = self._VerifyNode(node_i, nresult)
3475 7352d33b Thomas Thrainer
      self._VerifyNodeTime(node_i, nresult, nvinfo_starttime, nvinfo_endtime)
3476 7352d33b Thomas Thrainer
      self._VerifyNodeNetwork(node_i, nresult)
3477 7352d33b Thomas Thrainer
      self._VerifyNodeUserScripts(node_i, nresult)
3478 7352d33b Thomas Thrainer
      self._VerifyOob(node_i, nresult)
3479 13a6c760 Helga Velroyen
      self._VerifyAcceptedFileStoragePaths(node_i, nresult,
3480 13a6c760 Helga Velroyen
                                           node_i.uuid == master_node_uuid)
3481 4b322a76 Helga Velroyen
      self._VerifyFileStoragePaths(node_i, nresult)
3482 4b322a76 Helga Velroyen
      self._VerifySharedFileStoragePaths(node_i, nresult)
3483 7352d33b Thomas Thrainer
3484 7352d33b Thomas Thrainer
      if nimg.vm_capable:
3485 7352d33b Thomas Thrainer
        self._UpdateVerifyNodeLVM(node_i, nresult, vg_name, nimg)
3486 7352d33b Thomas Thrainer
        self._VerifyNodeDrbd(node_i, nresult, self.all_inst_info, drbd_helper,
3487 7352d33b Thomas Thrainer
                             all_drbd_map)
3488 7352d33b Thomas Thrainer
3489 7352d33b Thomas Thrainer
        self._UpdateNodeVolumes(node_i, nresult, nimg, vg_name)
3490 7352d33b Thomas Thrainer
        self._UpdateNodeInstances(node_i, nresult, nimg)
3491 7352d33b Thomas Thrainer
        self._UpdateNodeInfo(node_i, nresult, nimg, vg_name)
3492 7352d33b Thomas Thrainer
        self._UpdateNodeOS(node_i, nresult, nimg)
3493 7352d33b Thomas Thrainer
3494 7352d33b Thomas Thrainer
        if not nimg.os_fail:
3495 7352d33b Thomas Thrainer
          if refos_img is None:
3496 7352d33b Thomas Thrainer
            refos_img = nimg
3497 7352d33b Thomas Thrainer
          self._VerifyNodeOS(node_i, nimg, refos_img)
3498 7352d33b Thomas Thrainer
        self._VerifyNodeBridges(node_i, nresult, bridges)
3499 7352d33b Thomas Thrainer
3500 da4a52a3 Thomas Thrainer
        # Check whether all running instances are primary for the node. (This
3501 7352d33b Thomas Thrainer
        # can no longer be done from _VerifyInstance below, since some of the
3502 7352d33b Thomas Thrainer
        # wrong instances could be from other node groups.)
3503 da4a52a3 Thomas Thrainer
        non_primary_inst_uuids = set(nimg.instances).difference(nimg.pinst)
3504 7352d33b Thomas Thrainer
3505 da4a52a3 Thomas Thrainer
        for inst_uuid in non_primary_inst_uuids:
3506 da4a52a3 Thomas Thrainer
          test = inst_uuid in self.all_inst_info
3507 da4a52a3 Thomas Thrainer
          self._ErrorIf(test, constants.CV_EINSTANCEWRONGNODE,
3508 da4a52a3 Thomas Thrainer
                        self.cfg.GetInstanceName(inst_uuid),
3509 d0d7d7cf Thomas Thrainer
                        "instance should not run on node %s", node_i.name)
3510 d0d7d7cf Thomas Thrainer
          self._ErrorIf(not test, constants.CV_ENODEORPHANINSTANCE, node_i.name,
3511 da4a52a3 Thomas Thrainer
                        "node is running unknown instance %s", inst_uuid)
3512 7352d33b Thomas Thrainer
3513 1bb99a33 Bernardo Dal Seno
    self._VerifyGroupDRBDVersion(all_nvinfo)
3514 7352d33b Thomas Thrainer
    self._VerifyGroupLVM(node_image, vg_name)
3515 7352d33b Thomas Thrainer
3516 1c3231aa Thomas Thrainer
    for node_uuid, result in extra_lv_nvinfo.items():
3517 1c3231aa Thomas Thrainer
      self._UpdateNodeVolumes(self.all_node_info[node_uuid], result.payload,
3518 1c3231aa Thomas Thrainer
                              node_image[node_uuid], vg_name)
3519 7352d33b Thomas Thrainer
3520 7352d33b Thomas Thrainer
    feedback_fn("* Verifying instance status")
3521 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_uuids:
3522 da4a52a3 Thomas Thrainer
      instance = self.my_inst_info[inst_uuid]
3523 7352d33b Thomas Thrainer
      if verbose:
3524 da4a52a3 Thomas Thrainer
        feedback_fn("* Verifying instance %s" % instance.name)
3525 da4a52a3 Thomas Thrainer
      self._VerifyInstance(instance, node_image, instdisk[inst_uuid])
3526 7352d33b Thomas Thrainer
3527 7352d33b Thomas Thrainer
      # If the instance is non-redundant we cannot survive losing its primary
3528 7352d33b Thomas Thrainer
      # node, so we are not N+1 compliant.
3529 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_MIRRORED:
3530 7352d33b Thomas Thrainer
        i_non_redundant.append(instance)
3531 7352d33b Thomas Thrainer
3532 da4a52a3 Thomas Thrainer
      if not cluster.FillBE(instance)[constants.BE_AUTO_BALANCE]:
3533 7352d33b Thomas Thrainer
        i_non_a_balanced.append(instance)
3534 7352d33b Thomas Thrainer
3535 7352d33b Thomas Thrainer
    feedback_fn("* Verifying orphan volumes")
3536 7352d33b Thomas Thrainer
    reserved = utils.FieldSet(*cluster.reserved_lvs)
3537 7352d33b Thomas Thrainer
3538 7352d33b Thomas Thrainer
    # We will get spurious "unknown volume" warnings if any node of this group
3539 7352d33b Thomas Thrainer
    # is secondary for an instance whose primary is in another group. To avoid
3540 7352d33b Thomas Thrainer
    # them, we find these instances and add their volumes to node_vol_should.
3541 da4a52a3 Thomas Thrainer
    for instance in self.all_inst_info.values():
3542 da4a52a3 Thomas Thrainer
      for secondary in instance.secondary_nodes:
3543 7352d33b Thomas Thrainer
        if (secondary in self.my_node_info
3544 da4a52a3 Thomas Thrainer
            and instance.name not in self.my_inst_info):
3545 da4a52a3 Thomas Thrainer
          instance.MapLVsByNode(node_vol_should)
3546 7352d33b Thomas Thrainer
          break
3547 7352d33b Thomas Thrainer
3548 7352d33b Thomas Thrainer
    self._VerifyOrphanVolumes(node_vol_should, node_image, reserved)
3549 7352d33b Thomas Thrainer
3550 7352d33b Thomas Thrainer
    if constants.VERIFY_NPLUSONE_MEM not in self.op.skip_checks:
3551 7352d33b Thomas Thrainer
      feedback_fn("* Verifying N+1 Memory redundancy")
3552 7352d33b Thomas Thrainer
      self._VerifyNPlusOneMemory(node_image, self.my_inst_info)
3553 7352d33b Thomas Thrainer
3554 7352d33b Thomas Thrainer
    feedback_fn("* Other Notes")
3555 7352d33b Thomas Thrainer
    if i_non_redundant:
3556 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d non-redundant instance(s) found."
3557 7352d33b Thomas Thrainer
                  % len(i_non_redundant))
3558 7352d33b Thomas Thrainer
3559 7352d33b Thomas Thrainer
    if i_non_a_balanced:
3560 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d non-auto-balanced instance(s) found."
3561 7352d33b Thomas Thrainer
                  % len(i_non_a_balanced))
3562 7352d33b Thomas Thrainer
3563 7352d33b Thomas Thrainer
    if i_offline:
3564 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d offline instance(s) found." % i_offline)
3565 7352d33b Thomas Thrainer
3566 7352d33b Thomas Thrainer
    if n_offline:
3567 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d offline node(s) found." % n_offline)
3568 7352d33b Thomas Thrainer
3569 7352d33b Thomas Thrainer
    if n_drained:
3570 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d drained node(s) found." % n_drained)
3571 7352d33b Thomas Thrainer
3572 7352d33b Thomas Thrainer
    return not self.bad
3573 7352d33b Thomas Thrainer
3574 7352d33b Thomas Thrainer
  def HooksCallBack(self, phase, hooks_results, feedback_fn, lu_result):
3575 7352d33b Thomas Thrainer
    """Analyze the post-hooks' result
3576 7352d33b Thomas Thrainer

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

3580 7352d33b Thomas Thrainer
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
3581 7352d33b Thomas Thrainer
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
3582 7352d33b Thomas Thrainer
    @param hooks_results: the results of the multi-node hooks rpc call
3583 7352d33b Thomas Thrainer
    @param feedback_fn: function used send feedback back to the caller
3584 7352d33b Thomas Thrainer
    @param lu_result: previous Exec result
3585 7352d33b Thomas Thrainer
    @return: the new Exec result, based on the previous result
3586 7352d33b Thomas Thrainer
        and hook results
3587 7352d33b Thomas Thrainer

3588 7352d33b Thomas Thrainer
    """
3589 7352d33b Thomas Thrainer
    # We only really run POST phase hooks, only for non-empty groups,
3590 7352d33b Thomas Thrainer
    # and are only interested in their results
3591 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
3592 7352d33b Thomas Thrainer
      # empty node group
3593 7352d33b Thomas Thrainer
      pass
3594 7352d33b Thomas Thrainer
    elif phase == constants.HOOKS_PHASE_POST:
3595 7352d33b Thomas Thrainer
      # Used to change hooks' output to proper indentation
3596 7352d33b Thomas Thrainer
      feedback_fn("* Hooks Results")
3597 7352d33b Thomas Thrainer
      assert hooks_results, "invalid result from hooks"
3598 7352d33b Thomas Thrainer
3599 7352d33b Thomas Thrainer
      for node_name in hooks_results:
3600 7352d33b Thomas Thrainer
        res = hooks_results[node_name]
3601 7352d33b Thomas Thrainer
        msg = res.fail_msg
3602 7352d33b Thomas Thrainer
        test = msg and not res.offline
3603 7352d33b Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3604 7352d33b Thomas Thrainer
                      "Communication failure in hooks execution: %s", msg)
3605 510f672f Michele Tartara
        if test:
3606 510f672f Michele Tartara
          lu_result = False
3607 510f672f Michele Tartara
          continue
3608 510f672f Michele Tartara
        if res.offline:
3609 510f672f Michele Tartara
          # No need to investigate payload if node is offline
3610 7352d33b Thomas Thrainer
          continue
3611 7352d33b Thomas Thrainer
        for script, hkr, output in res.payload:
3612 7352d33b Thomas Thrainer
          test = hkr == constants.HKR_FAIL
3613 7352d33b Thomas Thrainer
          self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3614 7352d33b Thomas Thrainer
                        "Script %s failed, output:", script)
3615 7352d33b Thomas Thrainer
          if test:
3616 7352d33b Thomas Thrainer
            output = self._HOOKS_INDENT_RE.sub("      ", output)
3617 7352d33b Thomas Thrainer
            feedback_fn("%s" % output)
3618 7352d33b Thomas Thrainer
            lu_result = False
3619 7352d33b Thomas Thrainer
3620 7352d33b Thomas Thrainer
    return lu_result
3621 7352d33b Thomas Thrainer
3622 7352d33b Thomas Thrainer
3623 7352d33b Thomas Thrainer
class LUClusterVerifyDisks(NoHooksLU):
3624 7352d33b Thomas Thrainer
  """Verifies the cluster disks status.
3625 7352d33b Thomas Thrainer

3626 7352d33b Thomas Thrainer
  """
3627 7352d33b Thomas Thrainer
  REQ_BGL = False
3628 7352d33b Thomas Thrainer
3629 7352d33b Thomas Thrainer
  def ExpandNames(self):
3630 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3631 7352d33b Thomas Thrainer
    self.needed_locks = {
3632 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
3633 7352d33b Thomas Thrainer
      }
3634 7352d33b Thomas Thrainer
3635 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
3636 7352d33b Thomas Thrainer
    group_names = self.owned_locks(locking.LEVEL_NODEGROUP)
3637 7352d33b Thomas Thrainer
3638 7352d33b Thomas Thrainer
    # Submit one instance of L{opcodes.OpGroupVerifyDisks} per node group
3639 7352d33b Thomas Thrainer
    return ResultWithJobs([[opcodes.OpGroupVerifyDisks(group_name=group)]
3640 7352d33b Thomas Thrainer
                           for group in group_names])