Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 42f25b0b

History | View | Annotate | Download (65.3 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 2522b7c4 Dimitris Aragiorgis
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Configuration management for Ganeti
23 a8083063 Iustin Pop

24 319856a9 Michael Hanselmann
This module provides the interface to the Ganeti cluster configuration.
25 a8083063 Iustin Pop

26 319856a9 Michael Hanselmann
The configuration data is stored on every node but is updated on the master
27 319856a9 Michael Hanselmann
only. After each update, the master distributes the data to the other nodes.
28 a8083063 Iustin Pop

29 319856a9 Michael Hanselmann
Currently, the data storage format is JSON. YAML was slow and consuming too
30 319856a9 Michael Hanselmann
much memory.
31 a8083063 Iustin Pop

32 a8083063 Iustin Pop
"""
33 a8083063 Iustin Pop
34 d367b66c Manuel Franceschini
# pylint: disable-msg=R0904
35 d367b66c Manuel Franceschini
# R0904: Too many public methods
36 d367b66c Manuel Franceschini
37 a8083063 Iustin Pop
import os
38 a8083063 Iustin Pop
import random
39 d8470559 Michael Hanselmann
import logging
40 d693c864 Iustin Pop
import time
41 218f4c3d Michael Hanselmann
import itertools
42 a8083063 Iustin Pop
43 a8083063 Iustin Pop
from ganeti import errors
44 f78ede4e Guido Trotter
from ganeti import locking
45 a8083063 Iustin Pop
from ganeti import utils
46 a8083063 Iustin Pop
from ganeti import constants
47 a8083063 Iustin Pop
from ganeti import rpc
48 a8083063 Iustin Pop
from ganeti import objects
49 8d14b30d Iustin Pop
from ganeti import serializer
50 0fbae49a Balazs Lecz
from ganeti import uidpool
51 a744b676 Manuel Franceschini
from ganeti import netutils
52 e60c73a1 René Nussbaumer
from ganeti import runtime
53 243cdbcc Michael Hanselmann
54 243cdbcc Michael Hanselmann
55 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
56 f78ede4e Guido Trotter
57 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
58 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
59 4fae38c5 Guido Trotter
60 f78ede4e Guido Trotter
61 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
62 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
63 c41eea6e Iustin Pop

64 c41eea6e Iustin Pop
  This only verifies the version of the configuration.
65 c41eea6e Iustin Pop

66 c41eea6e Iustin Pop
  @raise errors.ConfigurationError: if the version differs from what
67 c41eea6e Iustin Pop
      we expect
68 c41eea6e Iustin Pop

69 c41eea6e Iustin Pop
  """
70 5b263ed7 Michael Hanselmann
  if data.version != constants.CONFIG_VERSION:
71 4b63dc7a Iustin Pop
    raise errors.ConfigVersionMismatch(constants.CONFIG_VERSION, data.version)
72 a8083063 Iustin Pop
73 319856a9 Michael Hanselmann
74 013da361 Guido Trotter
class TemporaryReservationManager:
75 013da361 Guido Trotter
  """A temporary resource reservation manager.
76 013da361 Guido Trotter

77 013da361 Guido Trotter
  This is used to reserve resources in a job, before using them, making sure
78 013da361 Guido Trotter
  other jobs cannot get them in the meantime.
79 013da361 Guido Trotter

80 013da361 Guido Trotter
  """
81 013da361 Guido Trotter
  def __init__(self):
82 013da361 Guido Trotter
    self._ec_reserved = {}
83 013da361 Guido Trotter
84 013da361 Guido Trotter
  def Reserved(self, resource):
85 a7359d91 David Knowles
    for holder_reserved in self._ec_reserved.values():
86 013da361 Guido Trotter
      if resource in holder_reserved:
87 013da361 Guido Trotter
        return True
88 013da361 Guido Trotter
    return False
89 013da361 Guido Trotter
90 013da361 Guido Trotter
  def Reserve(self, ec_id, resource):
91 013da361 Guido Trotter
    if self.Reserved(resource):
92 28a7318f Iustin Pop
      raise errors.ReservationError("Duplicate reservation for resource '%s'"
93 28a7318f Iustin Pop
                                    % str(resource))
94 013da361 Guido Trotter
    if ec_id not in self._ec_reserved:
95 013da361 Guido Trotter
      self._ec_reserved[ec_id] = set([resource])
96 013da361 Guido Trotter
    else:
97 013da361 Guido Trotter
      self._ec_reserved[ec_id].add(resource)
98 013da361 Guido Trotter
99 013da361 Guido Trotter
  def DropECReservations(self, ec_id):
100 013da361 Guido Trotter
    if ec_id in self._ec_reserved:
101 013da361 Guido Trotter
      del self._ec_reserved[ec_id]
102 013da361 Guido Trotter
103 013da361 Guido Trotter
  def GetReserved(self):
104 013da361 Guido Trotter
    all_reserved = set()
105 013da361 Guido Trotter
    for holder_reserved in self._ec_reserved.values():
106 013da361 Guido Trotter
      all_reserved.update(holder_reserved)
107 013da361 Guido Trotter
    return all_reserved
108 013da361 Guido Trotter
109 013da361 Guido Trotter
  def Generate(self, existing, generate_one_fn, ec_id):
110 013da361 Guido Trotter
    """Generate a new resource of this type
111 013da361 Guido Trotter

112 013da361 Guido Trotter
    """
113 013da361 Guido Trotter
    assert callable(generate_one_fn)
114 013da361 Guido Trotter
115 013da361 Guido Trotter
    all_elems = self.GetReserved()
116 013da361 Guido Trotter
    all_elems.update(existing)
117 013da361 Guido Trotter
    retries = 64
118 013da361 Guido Trotter
    while retries > 0:
119 013da361 Guido Trotter
      new_resource = generate_one_fn()
120 013da361 Guido Trotter
      if new_resource is not None and new_resource not in all_elems:
121 013da361 Guido Trotter
        break
122 013da361 Guido Trotter
    else:
123 013da361 Guido Trotter
      raise errors.ConfigurationError("Not able generate new resource"
124 013da361 Guido Trotter
                                      " (last tried: %s)" % new_resource)
125 013da361 Guido Trotter
    self.Reserve(ec_id, new_resource)
126 013da361 Guido Trotter
    return new_resource
127 013da361 Guido Trotter
128 013da361 Guido Trotter
129 a8083063 Iustin Pop
class ConfigWriter:
130 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
131 a8083063 Iustin Pop

132 d8aee57e Iustin Pop
  @ivar _temporary_lvs: reservation manager for temporary LVs
133 d8aee57e Iustin Pop
  @ivar _all_rms: a list of all temporary reservation managers
134 d8aee57e Iustin Pop

135 098c0958 Michael Hanselmann
  """
136 eb180fe2 Iustin Pop
  def __init__(self, cfg_file=None, offline=False, _getents=runtime.GetEnts,
137 eb180fe2 Iustin Pop
               accept_foreign=False):
138 14e15659 Iustin Pop
    self.write_count = 0
139 f78ede4e Guido Trotter
    self._lock = _config_lock
140 a8083063 Iustin Pop
    self._config_data = None
141 a8083063 Iustin Pop
    self._offline = offline
142 a8083063 Iustin Pop
    if cfg_file is None:
143 a8083063 Iustin Pop
      self._cfg_file = constants.CLUSTER_CONF_FILE
144 a8083063 Iustin Pop
    else:
145 a8083063 Iustin Pop
      self._cfg_file = cfg_file
146 e60c73a1 René Nussbaumer
    self._getents = _getents
147 4fae38c5 Guido Trotter
    self._temporary_ids = TemporaryReservationManager()
148 a81c53c9 Iustin Pop
    self._temporary_drbds = {}
149 36b66e6e Guido Trotter
    self._temporary_macs = TemporaryReservationManager()
150 afa1386e Guido Trotter
    self._temporary_secrets = TemporaryReservationManager()
151 d8aee57e Iustin Pop
    self._temporary_lvs = TemporaryReservationManager()
152 d8aee57e Iustin Pop
    self._all_rms = [self._temporary_ids, self._temporary_macs,
153 d8aee57e Iustin Pop
                     self._temporary_secrets, self._temporary_lvs]
154 89e1fc26 Iustin Pop
    # Note: in order to prevent errors when resolving our name in
155 89e1fc26 Iustin Pop
    # _DistributeConfig, we compute it here once and reuse it; it's
156 89e1fc26 Iustin Pop
    # better to raise an error before starting to modify the config
157 89e1fc26 Iustin Pop
    # file than after it was modified
158 b705c7a6 Manuel Franceschini
    self._my_hostname = netutils.Hostname.GetSysName()
159 3c7f6c44 Iustin Pop
    self._last_cluster_serial = -1
160 bd407597 Iustin Pop
    self._cfg_id = None
161 eb180fe2 Iustin Pop
    self._OpenConfig(accept_foreign)
162 a8083063 Iustin Pop
163 a8083063 Iustin Pop
  # this method needs to be static, so that we can call it on the class
164 a8083063 Iustin Pop
  @staticmethod
165 a8083063 Iustin Pop
  def IsCluster():
166 a8083063 Iustin Pop
    """Check if the cluster is configured.
167 a8083063 Iustin Pop

168 a8083063 Iustin Pop
    """
169 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
170 a8083063 Iustin Pop
171 36b66e6e Guido Trotter
  def _GenerateOneMAC(self):
172 36b66e6e Guido Trotter
    """Generate one mac address
173 36b66e6e Guido Trotter

174 36b66e6e Guido Trotter
    """
175 36b66e6e Guido Trotter
    prefix = self._config_data.cluster.mac_prefix
176 36b66e6e Guido Trotter
    byte1 = random.randrange(0, 256)
177 36b66e6e Guido Trotter
    byte2 = random.randrange(0, 256)
178 36b66e6e Guido Trotter
    byte3 = random.randrange(0, 256)
179 36b66e6e Guido Trotter
    mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
180 36b66e6e Guido Trotter
    return mac
181 36b66e6e Guido Trotter
182 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
183 5768e6a6 René Nussbaumer
  def GetNdParams(self, node):
184 5768e6a6 René Nussbaumer
    """Get the node params populated with cluster defaults.
185 5768e6a6 René Nussbaumer

186 5768e6a6 René Nussbaumer
    @type node: L{object.Node}
187 5768e6a6 René Nussbaumer
    @param node: The node we want to know the params for
188 5768e6a6 René Nussbaumer
    @return: A dict with the filled in node params
189 5768e6a6 René Nussbaumer

190 5768e6a6 René Nussbaumer
    """
191 5768e6a6 René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
192 5768e6a6 René Nussbaumer
    return self._config_data.cluster.FillND(node, nodegroup)
193 5768e6a6 René Nussbaumer
194 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
195 36b66e6e Guido Trotter
  def GenerateMAC(self, ec_id):
196 a8083063 Iustin Pop
    """Generate a MAC for an instance.
197 a8083063 Iustin Pop

198 a8083063 Iustin Pop
    This should check the current instances for duplicates.
199 a8083063 Iustin Pop

200 a8083063 Iustin Pop
    """
201 36b66e6e Guido Trotter
    existing = self._AllMACs()
202 36b66e6e Guido Trotter
    return self._temporary_ids.Generate(existing, self._GenerateOneMAC, ec_id)
203 a8083063 Iustin Pop
204 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
205 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
206 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
207 1862d460 Alexander Schreiber

208 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
209 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
210 1862d460 Alexander Schreiber

211 1862d460 Alexander Schreiber
    """
212 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
213 36b66e6e Guido Trotter
    if mac in all_macs:
214 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
215 36b66e6e Guido Trotter
    else:
216 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
217 1862d460 Alexander Schreiber
218 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
219 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
220 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
221 d8aee57e Iustin Pop

222 d8aee57e Iustin Pop
    @type lv_name: string
223 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
224 d8aee57e Iustin Pop

225 d8aee57e Iustin Pop
    """
226 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
227 d8aee57e Iustin Pop
    if lv_name in all_lvs:
228 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
229 d8aee57e Iustin Pop
    else:
230 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
231 d8aee57e Iustin Pop
232 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
233 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
234 f9518d38 Iustin Pop
    """Generate a DRBD secret.
235 f9518d38 Iustin Pop

236 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
237 f9518d38 Iustin Pop

238 f9518d38 Iustin Pop
    """
239 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
240 afa1386e Guido Trotter
                                            utils.GenerateSecret,
241 afa1386e Guido Trotter
                                            ec_id)
242 8d9c3bef Michael Hanselmann
243 34e54ebc Iustin Pop
  def _AllLVs(self):
244 923b1523 Iustin Pop
    """Compute the list of all LVs.
245 923b1523 Iustin Pop

246 923b1523 Iustin Pop
    """
247 923b1523 Iustin Pop
    lvnames = set()
248 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
249 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
250 923b1523 Iustin Pop
      for lv_list in node_data.values():
251 923b1523 Iustin Pop
        lvnames.update(lv_list)
252 923b1523 Iustin Pop
    return lvnames
253 923b1523 Iustin Pop
254 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
255 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
256 34e54ebc Iustin Pop

257 34e54ebc Iustin Pop
    @type include_temporary: boolean
258 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
259 34e54ebc Iustin Pop
    @rtype: set
260 34e54ebc Iustin Pop
    @return: a set of IDs
261 34e54ebc Iustin Pop

262 34e54ebc Iustin Pop
    """
263 34e54ebc Iustin Pop
    existing = set()
264 34e54ebc Iustin Pop
    if include_temporary:
265 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
266 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
267 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
268 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
269 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
270 34e54ebc Iustin Pop
    return existing
271 34e54ebc Iustin Pop
272 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
273 430b923c Iustin Pop
    """Generate an unique UUID.
274 923b1523 Iustin Pop

275 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
276 923b1523 Iustin Pop
    duplicates.
277 923b1523 Iustin Pop

278 c41eea6e Iustin Pop
    @rtype: string
279 c41eea6e Iustin Pop
    @return: the unique id
280 923b1523 Iustin Pop

281 923b1523 Iustin Pop
    """
282 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
283 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
284 923b1523 Iustin Pop
285 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
286 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
287 430b923c Iustin Pop
    """Generate an unique ID.
288 430b923c Iustin Pop

289 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
290 430b923c Iustin Pop

291 4fae38c5 Guido Trotter
    @type ec_id: string
292 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
293 34d657ba Iustin Pop

294 34d657ba Iustin Pop
    """
295 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
296 34d657ba Iustin Pop
297 a8083063 Iustin Pop
  def _AllMACs(self):
298 a8083063 Iustin Pop
    """Return all MACs present in the config.
299 a8083063 Iustin Pop

300 c41eea6e Iustin Pop
    @rtype: list
301 c41eea6e Iustin Pop
    @return: the list of all MACs
302 c41eea6e Iustin Pop

303 a8083063 Iustin Pop
    """
304 a8083063 Iustin Pop
    result = []
305 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
306 a8083063 Iustin Pop
      for nic in instance.nics:
307 a8083063 Iustin Pop
        result.append(nic.mac)
308 a8083063 Iustin Pop
309 a8083063 Iustin Pop
    return result
310 a8083063 Iustin Pop
311 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
312 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
313 f9518d38 Iustin Pop

314 c41eea6e Iustin Pop
    @rtype: list
315 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
316 c41eea6e Iustin Pop

317 f9518d38 Iustin Pop
    """
318 f9518d38 Iustin Pop
    def helper(disk, result):
319 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
320 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
321 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
322 f9518d38 Iustin Pop
      if disk.children:
323 f9518d38 Iustin Pop
        for child in disk.children:
324 f9518d38 Iustin Pop
          helper(child, result)
325 f9518d38 Iustin Pop
326 f9518d38 Iustin Pop
    result = []
327 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
328 f9518d38 Iustin Pop
      for disk in instance.disks:
329 f9518d38 Iustin Pop
        helper(disk, result)
330 f9518d38 Iustin Pop
331 f9518d38 Iustin Pop
    return result
332 f9518d38 Iustin Pop
333 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
334 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
335 4b98ac29 Iustin Pop

336 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
337 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
338 4b98ac29 Iustin Pop
    @type l_ids: list
339 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
340 4b98ac29 Iustin Pop
    @type p_ids: list
341 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
342 4b98ac29 Iustin Pop
    @rtype: list
343 4b98ac29 Iustin Pop
    @return: a list of error messages
344 4b98ac29 Iustin Pop

345 4b98ac29 Iustin Pop
    """
346 4b98ac29 Iustin Pop
    result = []
347 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
348 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
349 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
350 25ae22e4 Iustin Pop
      else:
351 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
352 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
353 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
354 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
355 25ae22e4 Iustin Pop
      else:
356 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
357 4b98ac29 Iustin Pop
358 4b98ac29 Iustin Pop
    if disk.children:
359 4b98ac29 Iustin Pop
      for child in disk.children:
360 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
361 4b98ac29 Iustin Pop
    return result
362 4b98ac29 Iustin Pop
363 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
364 a8efbb40 Iustin Pop
    """Verify function.
365 a8efbb40 Iustin Pop

366 4a89c54a Iustin Pop
    @rtype: list
367 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
368 4a89c54a Iustin Pop
        configuration errors
369 4a89c54a Iustin Pop

370 a8083063 Iustin Pop
    """
371 26f2fd8d Iustin Pop
    # pylint: disable-msg=R0914
372 a8083063 Iustin Pop
    result = []
373 a8083063 Iustin Pop
    seen_macs = []
374 48ce9fd9 Iustin Pop
    ports = {}
375 a8083063 Iustin Pop
    data = self._config_data
376 7e01d204 Iustin Pop
    cluster = data.cluster
377 4b98ac29 Iustin Pop
    seen_lids = []
378 4b98ac29 Iustin Pop
    seen_pids = []
379 9a5fba23 Guido Trotter
380 9a5fba23 Guido Trotter
    # global cluster checks
381 7e01d204 Iustin Pop
    if not cluster.enabled_hypervisors:
382 9a5fba23 Guido Trotter
      result.append("enabled hypervisors list doesn't have any entries")
383 7e01d204 Iustin Pop
    invalid_hvs = set(cluster.enabled_hypervisors) - constants.HYPER_TYPES
384 9a5fba23 Guido Trotter
    if invalid_hvs:
385 9a5fba23 Guido Trotter
      result.append("enabled hypervisors contains invalid entries: %s" %
386 9a5fba23 Guido Trotter
                    invalid_hvs)
387 7e01d204 Iustin Pop
    missing_hvp = (set(cluster.enabled_hypervisors) -
388 7e01d204 Iustin Pop
                   set(cluster.hvparams.keys()))
389 9f3ac970 Iustin Pop
    if missing_hvp:
390 9f3ac970 Iustin Pop
      result.append("hypervisor parameters missing for the enabled"
391 9f3ac970 Iustin Pop
                    " hypervisor(s) %s" % utils.CommaJoin(missing_hvp))
392 9a5fba23 Guido Trotter
393 7e01d204 Iustin Pop
    if cluster.master_node not in data.nodes:
394 9a5fba23 Guido Trotter
      result.append("cluster has invalid primary node '%s'" %
395 7e01d204 Iustin Pop
                    cluster.master_node)
396 9a5fba23 Guido Trotter
397 26f2fd8d Iustin Pop
    def _helper(owner, attr, value, template):
398 26f2fd8d Iustin Pop
      try:
399 26f2fd8d Iustin Pop
        utils.ForceDictType(value, template)
400 26f2fd8d Iustin Pop
      except errors.GenericError, err:
401 26f2fd8d Iustin Pop
        result.append("%s has invalid %s: %s" % (owner, attr, err))
402 26f2fd8d Iustin Pop
403 26f2fd8d Iustin Pop
    def _helper_nic(owner, params):
404 26f2fd8d Iustin Pop
      try:
405 26f2fd8d Iustin Pop
        objects.NIC.CheckParameterSyntax(params)
406 26f2fd8d Iustin Pop
      except errors.ConfigurationError, err:
407 26f2fd8d Iustin Pop
        result.append("%s has invalid nicparams: %s" % (owner, err))
408 26f2fd8d Iustin Pop
409 26f2fd8d Iustin Pop
    # check cluster parameters
410 26f2fd8d Iustin Pop
    _helper("cluster", "beparams", cluster.SimpleFillBE({}),
411 26f2fd8d Iustin Pop
            constants.BES_PARAMETER_TYPES)
412 26f2fd8d Iustin Pop
    _helper("cluster", "nicparams", cluster.SimpleFillNIC({}),
413 26f2fd8d Iustin Pop
            constants.NICS_PARAMETER_TYPES)
414 26f2fd8d Iustin Pop
    _helper_nic("cluster", cluster.SimpleFillNIC({}))
415 26f2fd8d Iustin Pop
    _helper("cluster", "ndparams", cluster.SimpleFillND({}),
416 26f2fd8d Iustin Pop
            constants.NDS_PARAMETER_TYPES)
417 26f2fd8d Iustin Pop
418 9a5fba23 Guido Trotter
    # per-instance checks
419 a8083063 Iustin Pop
    for instance_name in data.instances:
420 a8083063 Iustin Pop
      instance = data.instances[instance_name]
421 81196341 Iustin Pop
      if instance.name != instance_name:
422 81196341 Iustin Pop
        result.append("instance '%s' is indexed by wrong name '%s'" %
423 81196341 Iustin Pop
                      (instance.name, instance_name))
424 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
425 8522ceeb Iustin Pop
        result.append("instance '%s' has invalid primary node '%s'" %
426 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
427 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
428 a8083063 Iustin Pop
        if snode not in data.nodes:
429 8522ceeb Iustin Pop
          result.append("instance '%s' has invalid secondary node '%s'" %
430 a8083063 Iustin Pop
                        (instance_name, snode))
431 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
432 a8083063 Iustin Pop
        if nic.mac in seen_macs:
433 8522ceeb Iustin Pop
          result.append("instance '%s' has NIC %d mac %s duplicate" %
434 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
435 a8083063 Iustin Pop
        else:
436 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
437 26f2fd8d Iustin Pop
        if nic.nicparams:
438 26f2fd8d Iustin Pop
          filled = cluster.SimpleFillNIC(nic.nicparams)
439 26f2fd8d Iustin Pop
          owner = "instance %s nic %d" % (instance.name, idx)
440 26f2fd8d Iustin Pop
          _helper(owner, "nicparams",
441 26f2fd8d Iustin Pop
                  filled, constants.NICS_PARAMETER_TYPES)
442 26f2fd8d Iustin Pop
          _helper_nic(owner, filled)
443 26f2fd8d Iustin Pop
444 26f2fd8d Iustin Pop
      # parameter checks
445 26f2fd8d Iustin Pop
      if instance.beparams:
446 26f2fd8d Iustin Pop
        _helper("instance %s" % instance.name, "beparams",
447 26f2fd8d Iustin Pop
                cluster.FillBE(instance), constants.BES_PARAMETER_TYPES)
448 48ce9fd9 Iustin Pop
449 48ce9fd9 Iustin Pop
      # gather the drbd ports for duplicate checks
450 48ce9fd9 Iustin Pop
      for dsk in instance.disks:
451 48ce9fd9 Iustin Pop
        if dsk.dev_type in constants.LDS_DRBD:
452 48ce9fd9 Iustin Pop
          tcp_port = dsk.logical_id[2]
453 48ce9fd9 Iustin Pop
          if tcp_port not in ports:
454 48ce9fd9 Iustin Pop
            ports[tcp_port] = []
455 48ce9fd9 Iustin Pop
          ports[tcp_port].append((instance.name, "drbd disk %s" % dsk.iv_name))
456 48ce9fd9 Iustin Pop
      # gather network port reservation
457 48ce9fd9 Iustin Pop
      net_port = getattr(instance, "network_port", None)
458 48ce9fd9 Iustin Pop
      if net_port is not None:
459 48ce9fd9 Iustin Pop
        if net_port not in ports:
460 48ce9fd9 Iustin Pop
          ports[net_port] = []
461 48ce9fd9 Iustin Pop
        ports[net_port].append((instance.name, "network port"))
462 48ce9fd9 Iustin Pop
463 332d0e37 Iustin Pop
      # instance disk verify
464 332d0e37 Iustin Pop
      for idx, disk in enumerate(instance.disks):
465 332d0e37 Iustin Pop
        result.extend(["instance '%s' disk %d error: %s" %
466 332d0e37 Iustin Pop
                       (instance.name, idx, msg) for msg in disk.Verify()])
467 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(disk, seen_lids, seen_pids))
468 332d0e37 Iustin Pop
469 48ce9fd9 Iustin Pop
    # cluster-wide pool of free ports
470 7e01d204 Iustin Pop
    for free_port in cluster.tcpudp_port_pool:
471 48ce9fd9 Iustin Pop
      if free_port not in ports:
472 48ce9fd9 Iustin Pop
        ports[free_port] = []
473 48ce9fd9 Iustin Pop
      ports[free_port].append(("cluster", "port marked as free"))
474 48ce9fd9 Iustin Pop
475 48ce9fd9 Iustin Pop
    # compute tcp/udp duplicate ports
476 48ce9fd9 Iustin Pop
    keys = ports.keys()
477 48ce9fd9 Iustin Pop
    keys.sort()
478 48ce9fd9 Iustin Pop
    for pnum in keys:
479 48ce9fd9 Iustin Pop
      pdata = ports[pnum]
480 48ce9fd9 Iustin Pop
      if len(pdata) > 1:
481 1f864b60 Iustin Pop
        txt = utils.CommaJoin(["%s/%s" % val for val in pdata])
482 48ce9fd9 Iustin Pop
        result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
483 48ce9fd9 Iustin Pop
484 48ce9fd9 Iustin Pop
    # highest used tcp port check
485 48ce9fd9 Iustin Pop
    if keys:
486 7e01d204 Iustin Pop
      if keys[-1] > cluster.highest_used_port:
487 48ce9fd9 Iustin Pop
        result.append("Highest used port mismatch, saved %s, computed %s" %
488 7e01d204 Iustin Pop
                      (cluster.highest_used_port, keys[-1]))
489 a8efbb40 Iustin Pop
490 7e01d204 Iustin Pop
    if not data.nodes[cluster.master_node].master_candidate:
491 3a26773f Iustin Pop
      result.append("Master node is not a master candidate")
492 3a26773f Iustin Pop
493 4a89c54a Iustin Pop
    # master candidate checks
494 e623dbe3 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats()
495 ec0292f1 Iustin Pop
    if mc_now < mc_max:
496 ec0292f1 Iustin Pop
      result.append("Not enough master candidates: actual %d, target %d" %
497 ec0292f1 Iustin Pop
                    (mc_now, mc_max))
498 48ce9fd9 Iustin Pop
499 5bf07049 Iustin Pop
    # node checks
500 81196341 Iustin Pop
    for node_name, node in data.nodes.items():
501 81196341 Iustin Pop
      if node.name != node_name:
502 81196341 Iustin Pop
        result.append("Node '%s' is indexed by wrong name '%s'" %
503 81196341 Iustin Pop
                      (node.name, node_name))
504 5bf07049 Iustin Pop
      if [node.master_candidate, node.drained, node.offline].count(True) > 1:
505 5bf07049 Iustin Pop
        result.append("Node %s state is invalid: master_candidate=%s,"
506 5bf07049 Iustin Pop
                      " drain=%s, offline=%s" %
507 3d889a7d Michael Hanselmann
                      (node.name, node.master_candidate, node.drained,
508 5bf07049 Iustin Pop
                       node.offline))
509 26f2fd8d Iustin Pop
      if node.group not in data.nodegroups:
510 26f2fd8d Iustin Pop
        result.append("Node '%s' has invalid group '%s'" %
511 26f2fd8d Iustin Pop
                      (node.name, node.group))
512 26f2fd8d Iustin Pop
      else:
513 26f2fd8d Iustin Pop
        _helper("node %s" % node.name, "ndparams",
514 26f2fd8d Iustin Pop
                cluster.FillND(node, data.nodegroups[node.group]),
515 26f2fd8d Iustin Pop
                constants.NDS_PARAMETER_TYPES)
516 5bf07049 Iustin Pop
517 6520ba14 Guido Trotter
    # nodegroups checks
518 ace16501 Guido Trotter
    nodegroups_names = set()
519 6520ba14 Guido Trotter
    for nodegroup_uuid in data.nodegroups:
520 6520ba14 Guido Trotter
      nodegroup = data.nodegroups[nodegroup_uuid]
521 6520ba14 Guido Trotter
      if nodegroup.uuid != nodegroup_uuid:
522 913cc25e Adeodato Simo
        result.append("node group '%s' (uuid: '%s') indexed by wrong uuid '%s'"
523 6520ba14 Guido Trotter
                      % (nodegroup.name, nodegroup.uuid, nodegroup_uuid))
524 485ba212 Guido Trotter
      if utils.UUID_RE.match(nodegroup.name.lower()):
525 913cc25e Adeodato Simo
        result.append("node group '%s' (uuid: '%s') has uuid-like name" %
526 485ba212 Guido Trotter
                      (nodegroup.name, nodegroup.uuid))
527 ace16501 Guido Trotter
      if nodegroup.name in nodegroups_names:
528 913cc25e Adeodato Simo
        result.append("duplicate node group name '%s'" % nodegroup.name)
529 ace16501 Guido Trotter
      else:
530 ace16501 Guido Trotter
        nodegroups_names.add(nodegroup.name)
531 26f2fd8d Iustin Pop
      if nodegroup.ndparams:
532 26f2fd8d Iustin Pop
        _helper("group %s" % nodegroup.name, "ndparams",
533 26f2fd8d Iustin Pop
                cluster.SimpleFillND(nodegroup.ndparams),
534 26f2fd8d Iustin Pop
                constants.NDS_PARAMETER_TYPES)
535 26f2fd8d Iustin Pop
536 6520ba14 Guido Trotter
537 4a89c54a Iustin Pop
    # drbd minors check
538 1122eb25 Iustin Pop
    _, duplicates = self._UnlockedComputeDRBDMap()
539 4a89c54a Iustin Pop
    for node, minor, instance_a, instance_b in duplicates:
540 4a89c54a Iustin Pop
      result.append("DRBD minor %d on node %s is assigned twice to instances"
541 4a89c54a Iustin Pop
                    " %s and %s" % (minor, node, instance_a, instance_b))
542 4a89c54a Iustin Pop
543 0ce8f948 Iustin Pop
    # IP checks
544 7e01d204 Iustin Pop
    default_nicparams = cluster.nicparams[constants.PP_DEFAULT]
545 b8716596 Michael Hanselmann
    ips = {}
546 b8716596 Michael Hanselmann
547 b8716596 Michael Hanselmann
    def _AddIpAddress(ip, name):
548 b8716596 Michael Hanselmann
      ips.setdefault(ip, []).append(name)
549 b8716596 Michael Hanselmann
550 7e01d204 Iustin Pop
    _AddIpAddress(cluster.master_ip, "cluster_ip")
551 0ce8f948 Iustin Pop
552 0ce8f948 Iustin Pop
    for node in data.nodes.values():
553 b8716596 Michael Hanselmann
      _AddIpAddress(node.primary_ip, "node:%s/primary" % node.name)
554 0ce8f948 Iustin Pop
      if node.secondary_ip != node.primary_ip:
555 b8716596 Michael Hanselmann
        _AddIpAddress(node.secondary_ip, "node:%s/secondary" % node.name)
556 b8716596 Michael Hanselmann
557 b8716596 Michael Hanselmann
    for instance in data.instances.values():
558 b8716596 Michael Hanselmann
      for idx, nic in enumerate(instance.nics):
559 b8716596 Michael Hanselmann
        if nic.ip is None:
560 b8716596 Michael Hanselmann
          continue
561 b8716596 Michael Hanselmann
562 b8716596 Michael Hanselmann
        nicparams = objects.FillDict(default_nicparams, nic.nicparams)
563 b8716596 Michael Hanselmann
        nic_mode = nicparams[constants.NIC_MODE]
564 b8716596 Michael Hanselmann
        nic_link = nicparams[constants.NIC_LINK]
565 b8716596 Michael Hanselmann
566 b8716596 Michael Hanselmann
        if nic_mode == constants.NIC_MODE_BRIDGED:
567 b8716596 Michael Hanselmann
          link = "bridge:%s" % nic_link
568 b8716596 Michael Hanselmann
        elif nic_mode == constants.NIC_MODE_ROUTED:
569 b8716596 Michael Hanselmann
          link = "route:%s" % nic_link
570 b8716596 Michael Hanselmann
        else:
571 b8716596 Michael Hanselmann
          raise errors.ProgrammerError("NIC mode '%s' not handled" % nic_mode)
572 b8716596 Michael Hanselmann
573 b8716596 Michael Hanselmann
        _AddIpAddress("%s/%s" % (link, nic.ip),
574 b8716596 Michael Hanselmann
                      "instance:%s/nic:%d" % (instance.name, idx))
575 0ce8f948 Iustin Pop
576 0ce8f948 Iustin Pop
    for ip, owners in ips.items():
577 0ce8f948 Iustin Pop
      if len(owners) > 1:
578 0ce8f948 Iustin Pop
        result.append("IP address %s is used by multiple owners: %s" %
579 1f864b60 Iustin Pop
                      (ip, utils.CommaJoin(owners)))
580 b8716596 Michael Hanselmann
581 a8083063 Iustin Pop
    return result
582 a8083063 Iustin Pop
583 4a89c54a Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
584 4a89c54a Iustin Pop
  def VerifyConfig(self):
585 4a89c54a Iustin Pop
    """Verify function.
586 4a89c54a Iustin Pop

587 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
588 4a89c54a Iustin Pop

589 4a89c54a Iustin Pop
    @rtype: list
590 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
591 4a89c54a Iustin Pop
        configuration errors
592 4a89c54a Iustin Pop

593 4a89c54a Iustin Pop
    """
594 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
595 4a89c54a Iustin Pop
596 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
597 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
598 a8083063 Iustin Pop

599 a8083063 Iustin Pop
    This is used only for drbd, which needs ip/port configuration.
600 a8083063 Iustin Pop

601 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
602 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
603 a8083063 Iustin Pop
    node.
604 a8083063 Iustin Pop

605 f78ede4e Guido Trotter
    This function is for internal use, when the config lock is already held.
606 f78ede4e Guido Trotter

607 a8083063 Iustin Pop
    """
608 a8083063 Iustin Pop
    if disk.children:
609 a8083063 Iustin Pop
      for child in disk.children:
610 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
611 a8083063 Iustin Pop
612 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
613 a8083063 Iustin Pop
      return
614 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
615 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
616 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
617 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
618 3ecf6786 Iustin Pop
                                        node_name)
619 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
620 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
621 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
622 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
623 a8083063 Iustin Pop
                                        " for %s" % str(disk))
624 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
625 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
626 a8083063 Iustin Pop
      if pnode == node_name:
627 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
628 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
629 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
630 a8083063 Iustin Pop
    else:
631 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
632 a8083063 Iustin Pop
    return
633 a8083063 Iustin Pop
634 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
635 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
636 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
637 f78ede4e Guido Trotter

638 f78ede4e Guido Trotter
    This is used only for drbd, which needs ip/port configuration.
639 f78ede4e Guido Trotter

640 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
641 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
642 f78ede4e Guido Trotter
    node.
643 f78ede4e Guido Trotter

644 f78ede4e Guido Trotter
    """
645 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
646 f78ede4e Guido Trotter
647 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
648 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
649 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
650 b2fddf63 Iustin Pop

651 2522b7c4 Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
652 2522b7c4 Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
653 2522b7c4 Dimitris Aragiorgis
        configuration is stable
654 2522b7c4 Dimitris Aragiorgis

655 b2fddf63 Iustin Pop
    """
656 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
657 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
658 264bb3c5 Michael Hanselmann
659 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
660 264bb3c5 Michael Hanselmann
661 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
662 b2fddf63 Iustin Pop
  def GetPortList(self):
663 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
664 264bb3c5 Michael Hanselmann

665 264bb3c5 Michael Hanselmann
    """
666 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
667 264bb3c5 Michael Hanselmann
668 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
669 a8083063 Iustin Pop
  def AllocatePort(self):
670 a8083063 Iustin Pop
    """Allocate a port.
671 a8083063 Iustin Pop

672 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
673 b2fddf63 Iustin Pop
    default port range (and in this case we increase
674 b2fddf63 Iustin Pop
    highest_used_port).
675 a8083063 Iustin Pop

676 a8083063 Iustin Pop
    """
677 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
678 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
679 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
680 264bb3c5 Michael Hanselmann
    else:
681 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
682 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
683 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
684 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
685 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
686 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
687 a8083063 Iustin Pop
688 a8083063 Iustin Pop
    self._WriteConfig()
689 a8083063 Iustin Pop
    return port
690 a8083063 Iustin Pop
691 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
692 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
693 a81c53c9 Iustin Pop

694 4a89c54a Iustin Pop
    @rtype: (dict, list)
695 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
696 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
697 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
698 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
699 4a89c54a Iustin Pop
        should raise an exception
700 a81c53c9 Iustin Pop

701 a81c53c9 Iustin Pop
    """
702 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
703 4a89c54a Iustin Pop
      duplicates = []
704 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
705 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
706 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
707 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
708 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
709 a81c53c9 Iustin Pop
          if port in used[node]:
710 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
711 4a89c54a Iustin Pop
          else:
712 4a89c54a Iustin Pop
            used[node][port] = instance_name
713 a81c53c9 Iustin Pop
      if disk.children:
714 a81c53c9 Iustin Pop
        for child in disk.children:
715 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
716 4a89c54a Iustin Pop
      return duplicates
717 a81c53c9 Iustin Pop
718 4a89c54a Iustin Pop
    duplicates = []
719 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
720 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
721 79b26a7a Iustin Pop
      for disk in instance.disks:
722 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
723 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
724 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
725 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
726 4a89c54a Iustin Pop
      else:
727 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
728 4a89c54a Iustin Pop
    return my_dict, duplicates
729 a81c53c9 Iustin Pop
730 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
731 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
732 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
733 6d2e83d5 Iustin Pop

734 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
735 6d2e83d5 Iustin Pop

736 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
737 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
738 6d2e83d5 Iustin Pop
        an empty list).
739 6d2e83d5 Iustin Pop

740 6d2e83d5 Iustin Pop
    """
741 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
742 4a89c54a Iustin Pop
    if duplicates:
743 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
744 4a89c54a Iustin Pop
                                      str(duplicates))
745 4a89c54a Iustin Pop
    return d_map
746 6d2e83d5 Iustin Pop
747 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
748 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
749 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
750 a81c53c9 Iustin Pop

751 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
752 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
753 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
754 a81c53c9 Iustin Pop
    order as the passed nodes.
755 a81c53c9 Iustin Pop

756 32388e6d Iustin Pop
    @type instance: string
757 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
758 32388e6d Iustin Pop

759 a81c53c9 Iustin Pop
    """
760 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
761 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
762 32388e6d Iustin Pop
763 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
764 4a89c54a Iustin Pop
    if duplicates:
765 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
766 4a89c54a Iustin Pop
                                      str(duplicates))
767 a81c53c9 Iustin Pop
    result = []
768 a81c53c9 Iustin Pop
    for nname in nodes:
769 a81c53c9 Iustin Pop
      ndata = d_map[nname]
770 a81c53c9 Iustin Pop
      if not ndata:
771 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
772 a81c53c9 Iustin Pop
        result.append(0)
773 a81c53c9 Iustin Pop
        ndata[0] = instance
774 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
775 a81c53c9 Iustin Pop
        continue
776 a81c53c9 Iustin Pop
      keys = ndata.keys()
777 a81c53c9 Iustin Pop
      keys.sort()
778 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
779 a81c53c9 Iustin Pop
      if ffree is None:
780 a81c53c9 Iustin Pop
        # return the next minor
781 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
782 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
783 a81c53c9 Iustin Pop
      else:
784 a81c53c9 Iustin Pop
        minor = ffree
785 4a89c54a Iustin Pop
      # double-check minor against current instances
786 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
787 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
788 4a89c54a Iustin Pop
              " already allocated to instance %s" %
789 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
790 a81c53c9 Iustin Pop
      ndata[minor] = instance
791 4a89c54a Iustin Pop
      # double-check minor against reservation
792 4a89c54a Iustin Pop
      r_key = (nname, minor)
793 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
794 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
795 4a89c54a Iustin Pop
              " reserved for instance %s" %
796 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
797 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
798 4a89c54a Iustin Pop
      result.append(minor)
799 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
800 a81c53c9 Iustin Pop
                  nodes, result)
801 a81c53c9 Iustin Pop
    return result
802 a81c53c9 Iustin Pop
803 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
804 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
805 a81c53c9 Iustin Pop

806 a81c53c9 Iustin Pop
    @type instance: string
807 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
808 a81c53c9 Iustin Pop
                     released
809 a81c53c9 Iustin Pop

810 a81c53c9 Iustin Pop
    """
811 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
812 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
813 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
814 a81c53c9 Iustin Pop
      if name == instance:
815 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
816 a81c53c9 Iustin Pop
817 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
818 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
819 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
820 61cf6b5e Iustin Pop

821 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
822 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
823 61cf6b5e Iustin Pop
    functions.
824 61cf6b5e Iustin Pop

825 61cf6b5e Iustin Pop
    This function is just a wrapper over L{_UnlockedReleaseDRBDMinors}.
826 61cf6b5e Iustin Pop

827 61cf6b5e Iustin Pop
    @type instance: string
828 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
829 61cf6b5e Iustin Pop
                     released
830 61cf6b5e Iustin Pop

831 61cf6b5e Iustin Pop
    """
832 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
833 61cf6b5e Iustin Pop
834 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
835 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
836 4a8b186a Michael Hanselmann
    """Get the configuration version.
837 4a8b186a Michael Hanselmann

838 4a8b186a Michael Hanselmann
    @return: Config version
839 4a8b186a Michael Hanselmann

840 4a8b186a Michael Hanselmann
    """
841 4a8b186a Michael Hanselmann
    return self._config_data.version
842 4a8b186a Michael Hanselmann
843 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
844 4a8b186a Michael Hanselmann
  def GetClusterName(self):
845 4a8b186a Michael Hanselmann
    """Get cluster name.
846 4a8b186a Michael Hanselmann

847 4a8b186a Michael Hanselmann
    @return: Cluster name
848 4a8b186a Michael Hanselmann

849 4a8b186a Michael Hanselmann
    """
850 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
851 4a8b186a Michael Hanselmann
852 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
853 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
854 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
855 4a8b186a Michael Hanselmann

856 4a8b186a Michael Hanselmann
    @return: Master hostname
857 4a8b186a Michael Hanselmann

858 4a8b186a Michael Hanselmann
    """
859 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
860 4a8b186a Michael Hanselmann
861 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
862 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
863 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
864 4a8b186a Michael Hanselmann

865 4a8b186a Michael Hanselmann
    @return: Master IP
866 4a8b186a Michael Hanselmann

867 4a8b186a Michael Hanselmann
    """
868 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
869 4a8b186a Michael Hanselmann
870 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
871 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
872 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
873 4a8b186a Michael Hanselmann

874 4a8b186a Michael Hanselmann
    """
875 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
876 4a8b186a Michael Hanselmann
877 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
878 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
879 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
880 4a8b186a Michael Hanselmann

881 4a8b186a Michael Hanselmann
    """
882 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
883 4a8b186a Michael Hanselmann
884 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
885 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
886 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
887 4a8b186a Michael Hanselmann

888 4a8b186a Michael Hanselmann
    """
889 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
890 4a8b186a Michael Hanselmann
891 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
892 a8083063 Iustin Pop
  def GetHostKey(self):
893 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
894 a8083063 Iustin Pop

895 c41eea6e Iustin Pop
    @rtype: string
896 c41eea6e Iustin Pop
    @return: the rsa hostkey
897 a8083063 Iustin Pop

898 a8083063 Iustin Pop
    """
899 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
900 a8083063 Iustin Pop
901 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
902 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
903 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
904 bf4af505 Apollon Oikonomopoulos

905 bf4af505 Apollon Oikonomopoulos
    """
906 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
907 bf4af505 Apollon Oikonomopoulos
908 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
909 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
910 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
911 868a98ca Manuel Franceschini

912 868a98ca Manuel Franceschini
    @return: primary ip family
913 868a98ca Manuel Franceschini

914 868a98ca Manuel Franceschini
    """
915 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
916 868a98ca Manuel Franceschini
917 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
918 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
919 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
920 e11a1b77 Adeodato Simo

921 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
922 90e99856 Adeodato Simo
    according to their default values.
923 90e99856 Adeodato Simo

924 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
925 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
926 e11a1b77 Adeodato Simo
    @type ec_id: string
927 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
928 e11a1b77 Adeodato Simo
    @type check_uuid: bool
929 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
930 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
931 e11a1b77 Adeodato Simo
                       configuration already
932 e11a1b77 Adeodato Simo

933 e11a1b77 Adeodato Simo
    """
934 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
935 e11a1b77 Adeodato Simo
    self._WriteConfig()
936 e11a1b77 Adeodato Simo
937 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
938 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
939 e11a1b77 Adeodato Simo

940 e11a1b77 Adeodato Simo
    """
941 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
942 e11a1b77 Adeodato Simo
943 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
944 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
945 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
946 e11a1b77 Adeodato Simo
    if check_uuid:
947 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
948 e11a1b77 Adeodato Simo
949 18ffc0fe Stephen Shirley
    try:
950 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
951 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
952 18ffc0fe Stephen Shirley
      pass
953 18ffc0fe Stephen Shirley
    else:
954 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
955 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
956 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
957 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
958 18ffc0fe Stephen Shirley
959 e11a1b77 Adeodato Simo
    group.serial_no = 1
960 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
961 90e99856 Adeodato Simo
    group.UpgradeConfig()
962 e11a1b77 Adeodato Simo
963 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
964 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
965 e11a1b77 Adeodato Simo
966 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
967 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
968 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
969 e11a1b77 Adeodato Simo

970 e11a1b77 Adeodato Simo
    @type group_uuid: string
971 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
972 e11a1b77 Adeodato Simo

973 e11a1b77 Adeodato Simo
    """
974 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
975 e11a1b77 Adeodato Simo
976 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
977 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
978 e11a1b77 Adeodato Simo
979 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
980 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
981 0389c42a Stephen Shirley
982 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
983 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
984 e11a1b77 Adeodato Simo
    self._WriteConfig()
985 e11a1b77 Adeodato Simo
986 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
987 412b3531 Guido Trotter
    """Lookup a node group's UUID.
988 eaa98a04 Guido Trotter

989 eaa98a04 Guido Trotter
    @type target: string or None
990 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
991 eaa98a04 Guido Trotter
    @rtype: string
992 412b3531 Guido Trotter
    @return: nodegroup UUID
993 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
994 eaa98a04 Guido Trotter

995 eaa98a04 Guido Trotter
    """
996 eaa98a04 Guido Trotter
    if target is None:
997 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
998 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
999 eaa98a04 Guido Trotter
                                   " group must be specified explicitely.")
1000 eaa98a04 Guido Trotter
      else:
1001 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1002 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1003 eaa98a04 Guido Trotter
      return target
1004 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1005 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1006 eaa98a04 Guido Trotter
        return nodegroup.uuid
1007 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1008 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1009 eaa98a04 Guido Trotter
1010 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1011 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1012 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1013 e85d8982 Stephen Shirley

1014 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1015 e85d8982 Stephen Shirley

1016 e85d8982 Stephen Shirley
    @type target: string or None
1017 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1018 e85d8982 Stephen Shirley
    @rtype: string
1019 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1020 e85d8982 Stephen Shirley

1021 e85d8982 Stephen Shirley
    """
1022 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1023 e85d8982 Stephen Shirley
1024 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1025 648e4196 Guido Trotter
    """Lookup a node group.
1026 648e4196 Guido Trotter

1027 648e4196 Guido Trotter
    @type uuid: string
1028 648e4196 Guido Trotter
    @param uuid: group UUID
1029 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1030 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1031 648e4196 Guido Trotter

1032 648e4196 Guido Trotter
    """
1033 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1034 648e4196 Guido Trotter
      return None
1035 648e4196 Guido Trotter
1036 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1037 648e4196 Guido Trotter
1038 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1039 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1040 5768e6a6 René Nussbaumer
    """Lookup a node group.
1041 5768e6a6 René Nussbaumer

1042 5768e6a6 René Nussbaumer
    @type uuid: string
1043 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1044 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1045 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1046 5768e6a6 René Nussbaumer

1047 5768e6a6 René Nussbaumer
    """
1048 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1049 5768e6a6 René Nussbaumer
1050 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1051 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1052 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1053 622444e5 Iustin Pop

1054 622444e5 Iustin Pop
    """
1055 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1056 622444e5 Iustin Pop
1057 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1058 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1059 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1060 1ac6f2ad Guido Trotter

1061 1ac6f2ad Guido Trotter
    """
1062 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1063 1ac6f2ad Guido Trotter
1064 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1065 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1066 a8083063 Iustin Pop
    """Add an instance to the config.
1067 a8083063 Iustin Pop

1068 a8083063 Iustin Pop
    This should be used after creating a new instance.
1069 a8083063 Iustin Pop

1070 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1071 c41eea6e Iustin Pop
    @param instance: the instance object
1072 c41eea6e Iustin Pop

1073 a8083063 Iustin Pop
    """
1074 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1075 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1076 a8083063 Iustin Pop
1077 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1078 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1079 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1080 923b1523 Iustin Pop
1081 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1082 e4640214 Guido Trotter
    for nic in instance.nics:
1083 e4640214 Guido Trotter
      if nic.mac in all_macs:
1084 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1085 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1086 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1087 430b923c Iustin Pop
1088 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1089 e4640214 Guido Trotter
1090 b989e85d Iustin Pop
    instance.serial_no = 1
1091 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1092 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1093 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1094 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1095 a8083063 Iustin Pop
    self._WriteConfig()
1096 a8083063 Iustin Pop
1097 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1098 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1099 430b923c Iustin Pop

1100 430b923c Iustin Pop
    @param item: the instance or node to be checked
1101 0debfb35 Guido Trotter
    @param ec_id: the execution context id for the uuid reservation
1102 430b923c Iustin Pop

1103 430b923c Iustin Pop
    """
1104 430b923c Iustin Pop
    if not item.uuid:
1105 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1106 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1107 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1108 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1109 430b923c Iustin Pop
1110 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1111 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1112 a8083063 Iustin Pop

1113 a8083063 Iustin Pop
    """
1114 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
1115 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1116 a8083063 Iustin Pop
1117 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1118 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1119 3ecf6786 Iustin Pop
                                      instance_name)
1120 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1121 0d68c45d Iustin Pop
    if instance.admin_up != status:
1122 0d68c45d Iustin Pop
      instance.admin_up = status
1123 b989e85d Iustin Pop
      instance.serial_no += 1
1124 d693c864 Iustin Pop
      instance.mtime = time.time()
1125 455a3445 Iustin Pop
      self._WriteConfig()
1126 a8083063 Iustin Pop
1127 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1128 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1129 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1130 6a408fb2 Iustin Pop

1131 6a408fb2 Iustin Pop
    """
1132 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
1133 6a408fb2 Iustin Pop
1134 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1135 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1136 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1137 a8083063 Iustin Pop

1138 a8083063 Iustin Pop
    """
1139 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1140 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1141 f396ad8c Vangelis Koukis
1142 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1143 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1144 f396ad8c Vangelis Koukis
    inst = self._config_data.instances[instance_name]
1145 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1146 f396ad8c Vangelis Koukis
    if network_port is not None:
1147 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1148 f396ad8c Vangelis Koukis
1149 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1150 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1151 a8083063 Iustin Pop
    self._WriteConfig()
1152 a8083063 Iustin Pop
1153 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1154 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1155 fc95f88f Iustin Pop
    """Rename an instance.
1156 fc95f88f Iustin Pop

1157 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1158 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1159 fc95f88f Iustin Pop
    rename.
1160 fc95f88f Iustin Pop

1161 fc95f88f Iustin Pop
    """
1162 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
1163 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
1164 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
1165 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
1166 fc95f88f Iustin Pop
    inst.name = new_name
1167 b23c4333 Manuel Franceschini
1168 b23c4333 Manuel Franceschini
    for disk in inst.disks:
1169 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1170 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1171 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1172 3721d2fe Guido Trotter
        disk_fname = "disk%s" % disk.iv_name.split("/")[1]
1173 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
1174 c4feafe8 Iustin Pop
                                              utils.PathJoin(file_storage_dir,
1175 c4feafe8 Iustin Pop
                                                             inst.name,
1176 3721d2fe Guido Trotter
                                                             disk_fname))
1177 b23c4333 Manuel Franceschini
1178 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1179 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1180 1fc34c48 Michael Hanselmann
1181 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
1182 fc95f88f Iustin Pop
    self._WriteConfig()
1183 fc95f88f Iustin Pop
1184 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1185 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
1186 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1187 a8083063 Iustin Pop

1188 a8083063 Iustin Pop
    """
1189 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
1190 a8083063 Iustin Pop
1191 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1192 94bbfece Iustin Pop
    """Get the list of instances.
1193 94bbfece Iustin Pop

1194 94bbfece Iustin Pop
    This function is for internal use, when the config lock is already held.
1195 94bbfece Iustin Pop

1196 94bbfece Iustin Pop
    """
1197 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1198 94bbfece Iustin Pop
1199 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1200 a8083063 Iustin Pop
  def GetInstanceList(self):
1201 a8083063 Iustin Pop
    """Get the list of instances.
1202 a8083063 Iustin Pop

1203 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1204 c41eea6e Iustin Pop
        'instance1.example.com']
1205 a8083063 Iustin Pop

1206 a8083063 Iustin Pop
    """
1207 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1208 a8083063 Iustin Pop
1209 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1210 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1211 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1212 a8083063 Iustin Pop

1213 a8083063 Iustin Pop
    """
1214 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1215 bcdf16d7 Guido Trotter
                                    self._config_data.instances.keys(),
1216 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1217 a8083063 Iustin Pop
1218 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1219 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1220 94bbfece Iustin Pop

1221 94bbfece Iustin Pop
    This function is for internal use, when the config lock is already held.
1222 94bbfece Iustin Pop

1223 94bbfece Iustin Pop
    """
1224 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1225 94bbfece Iustin Pop
      return None
1226 94bbfece Iustin Pop
1227 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1228 94bbfece Iustin Pop
1229 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1230 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1231 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1232 a8083063 Iustin Pop

1233 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1234 a8083063 Iustin Pop
    an instance are taken from the live systems.
1235 a8083063 Iustin Pop

1236 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1237 c41eea6e Iustin Pop
        I{instance1.example.com}
1238 a8083063 Iustin Pop

1239 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1240 c41eea6e Iustin Pop
    @return: the instance object
1241 a8083063 Iustin Pop

1242 a8083063 Iustin Pop
    """
1243 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1244 a8083063 Iustin Pop
1245 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1246 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1247 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1248 0b2de758 Iustin Pop

1249 0b2de758 Iustin Pop
    @rtype: dict
1250 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1251 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1252 0b2de758 Iustin Pop

1253 0b2de758 Iustin Pop
    """
1254 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1255 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1256 0b2de758 Iustin Pop
    return my_dict
1257 0b2de758 Iustin Pop
1258 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1259 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1260 a8083063 Iustin Pop
    """Add a node to the configuration.
1261 a8083063 Iustin Pop

1262 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1263 c41eea6e Iustin Pop
    @param node: a Node instance
1264 a8083063 Iustin Pop

1265 a8083063 Iustin Pop
    """
1266 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1267 d8470559 Michael Hanselmann
1268 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1269 430b923c Iustin Pop
1270 b989e85d Iustin Pop
    node.serial_no = 1
1271 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1272 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1273 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1274 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1275 a8083063 Iustin Pop
    self._WriteConfig()
1276 a8083063 Iustin Pop
1277 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1278 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1279 a8083063 Iustin Pop
    """Remove a node from the configuration.
1280 a8083063 Iustin Pop

1281 a8083063 Iustin Pop
    """
1282 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1283 d8470559 Michael Hanselmann
1284 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1285 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1286 a8083063 Iustin Pop
1287 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1288 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1289 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1290 a8083063 Iustin Pop
    self._WriteConfig()
1291 a8083063 Iustin Pop
1292 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1293 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1294 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1295 a8083063 Iustin Pop

1296 a8083063 Iustin Pop
    """
1297 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1298 bcdf16d7 Guido Trotter
                                    self._config_data.nodes.keys(),
1299 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1300 a8083063 Iustin Pop
1301 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1302 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1303 a8083063 Iustin Pop

1304 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1305 c41eea6e Iustin Pop
    held.
1306 f78ede4e Guido Trotter

1307 c41eea6e Iustin Pop
    @param node_name: the node name, e.g. I{node1.example.com}
1308 a8083063 Iustin Pop

1309 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1310 c41eea6e Iustin Pop
    @return: the node object
1311 a8083063 Iustin Pop

1312 a8083063 Iustin Pop
    """
1313 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1314 a8083063 Iustin Pop
      return None
1315 a8083063 Iustin Pop
1316 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1317 a8083063 Iustin Pop
1318 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1319 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1320 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1321 f78ede4e Guido Trotter

1322 c41eea6e Iustin Pop
    This is just a locked wrapper over L{_UnlockedGetNodeInfo}.
1323 f78ede4e Guido Trotter

1324 c41eea6e Iustin Pop
    @param node_name: the node name, e.g. I{node1.example.com}
1325 c41eea6e Iustin Pop

1326 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1327 c41eea6e Iustin Pop
    @return: the node object
1328 f78ede4e Guido Trotter

1329 f78ede4e Guido Trotter
    """
1330 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1331 f78ede4e Guido Trotter
1332 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1333 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1334 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1335 8bf9e9a5 Iustin Pop

1336 8bf9e9a5 Iustin Pop
    @param node_name: the node name, e.g. I{node1.example.com}
1337 8bf9e9a5 Iustin Pop

1338 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1339 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1340 8bf9e9a5 Iustin Pop

1341 8bf9e9a5 Iustin Pop
    """
1342 8bf9e9a5 Iustin Pop
    pri = []
1343 8bf9e9a5 Iustin Pop
    sec = []
1344 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1345 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1346 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1347 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1348 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1349 8bf9e9a5 Iustin Pop
    return (pri, sec)
1350 8bf9e9a5 Iustin Pop
1351 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1352 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1353 a8083063 Iustin Pop

1354 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1355 c41eea6e Iustin Pop
    held.
1356 c41eea6e Iustin Pop

1357 c41eea6e Iustin Pop
    @rtype: list
1358 f78ede4e Guido Trotter

1359 a8083063 Iustin Pop
    """
1360 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1361 a8083063 Iustin Pop
1362 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1363 f78ede4e Guido Trotter
  def GetNodeList(self):
1364 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1365 f78ede4e Guido Trotter

1366 f78ede4e Guido Trotter
    """
1367 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1368 f78ede4e Guido Trotter
1369 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1370 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1371 94a02bb5 Iustin Pop

1372 94a02bb5 Iustin Pop
    """
1373 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1374 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1375 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1376 94a02bb5 Iustin Pop
1377 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1378 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1379 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1380 6819dc49 Iustin Pop

1381 6819dc49 Iustin Pop
    """
1382 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1383 6819dc49 Iustin Pop
1384 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1385 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1386 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1387 075b62ca Iustin Pop

1388 075b62ca Iustin Pop
    """
1389 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1390 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1391 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1392 075b62ca Iustin Pop
1393 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1394 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1395 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1396 8bf9e9a5 Iustin Pop

1397 8bf9e9a5 Iustin Pop
    """
1398 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1399 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1400 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1401 8bf9e9a5 Iustin Pop
1402 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1403 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1404 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1405 d65e5776 Iustin Pop

1406 d65e5776 Iustin Pop
    @rtype: dict
1407 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1408 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1409 d65e5776 Iustin Pop

1410 d65e5776 Iustin Pop
    """
1411 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1412 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1413 d65e5776 Iustin Pop
    return my_dict
1414 d65e5776 Iustin Pop
1415 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1416 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1417 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1418 9d5b1371 Michael Hanselmann

1419 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1420 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1421 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1422 9d5b1371 Michael Hanselmann

1423 9d5b1371 Michael Hanselmann
    """
1424 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1425 9d5b1371 Michael Hanselmann
1426 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1427 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1428 ec0292f1 Iustin Pop

1429 23f06b2b Iustin Pop
    @type exceptions: list
1430 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1431 ec0292f1 Iustin Pop
    @rtype: tuple
1432 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1433 ec0292f1 Iustin Pop

1434 ec0292f1 Iustin Pop
    """
1435 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1436 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1437 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1438 23f06b2b Iustin Pop
        continue
1439 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1440 ec0292f1 Iustin Pop
        mc_max += 1
1441 ec0292f1 Iustin Pop
      if node.master_candidate:
1442 ec0292f1 Iustin Pop
        mc_now += 1
1443 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1444 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1445 ec0292f1 Iustin Pop
1446 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1447 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1448 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1449 ec0292f1 Iustin Pop

1450 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1451 ec0292f1 Iustin Pop

1452 23f06b2b Iustin Pop
    @type exceptions: list
1453 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1454 ec0292f1 Iustin Pop
    @rtype: tuple
1455 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1456 ec0292f1 Iustin Pop

1457 ec0292f1 Iustin Pop
    """
1458 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1459 ec0292f1 Iustin Pop
1460 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1461 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1462 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1463 ec0292f1 Iustin Pop

1464 44485f49 Guido Trotter
    @type exceptions: list
1465 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1466 ec0292f1 Iustin Pop
    @rtype: list
1467 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1468 ec0292f1 Iustin Pop

1469 ec0292f1 Iustin Pop
    """
1470 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1471 ec0292f1 Iustin Pop
    mod_list = []
1472 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1473 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1474 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1475 ec0292f1 Iustin Pop
      for name in node_list:
1476 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1477 ec0292f1 Iustin Pop
          break
1478 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1479 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1480 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1481 ec0292f1 Iustin Pop
          continue
1482 ee513a66 Iustin Pop
        mod_list.append(node)
1483 ec0292f1 Iustin Pop
        node.master_candidate = True
1484 ec0292f1 Iustin Pop
        node.serial_no += 1
1485 ec0292f1 Iustin Pop
        mc_now += 1
1486 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1487 ec0292f1 Iustin Pop
        # this should not happen
1488 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1489 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1490 ec0292f1 Iustin Pop
      if mod_list:
1491 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1492 ec0292f1 Iustin Pop
        self._WriteConfig()
1493 ec0292f1 Iustin Pop
1494 ec0292f1 Iustin Pop
    return mod_list
1495 ec0292f1 Iustin Pop
1496 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1497 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1498 190e3cb6 Guido Trotter

1499 190e3cb6 Guido Trotter
    """
1500 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1501 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1502 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1503 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1504 190e3cb6 Guido Trotter
      # is not found anymore.
1505 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1506 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1507 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1508 190e3cb6 Guido Trotter
1509 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1510 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1511 190e3cb6 Guido Trotter

1512 190e3cb6 Guido Trotter
    """
1513 f936c153 Iustin Pop
    nodegroup = node.group
1514 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1515 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1516 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1517 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1518 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1519 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1520 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1521 190e3cb6 Guido Trotter
    else:
1522 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1523 190e3cb6 Guido Trotter
1524 218f4c3d Michael Hanselmann
  @locking.ssynchronized(_config_lock)
1525 218f4c3d Michael Hanselmann
  def AssignGroupNodes(self, mods):
1526 218f4c3d Michael Hanselmann
    """Changes the group of a number of nodes.
1527 218f4c3d Michael Hanselmann

1528 218f4c3d Michael Hanselmann
    @type mods: list of tuples; (node name, new group UUID)
1529 1730d4a1 Michael Hanselmann
    @param mods: Node membership modifications
1530 218f4c3d Michael Hanselmann

1531 218f4c3d Michael Hanselmann
    """
1532 218f4c3d Michael Hanselmann
    groups = self._config_data.nodegroups
1533 218f4c3d Michael Hanselmann
    nodes = self._config_data.nodes
1534 218f4c3d Michael Hanselmann
1535 218f4c3d Michael Hanselmann
    resmod = []
1536 218f4c3d Michael Hanselmann
1537 218f4c3d Michael Hanselmann
    # Try to resolve names/UUIDs first
1538 218f4c3d Michael Hanselmann
    for (node_name, new_group_uuid) in mods:
1539 218f4c3d Michael Hanselmann
      try:
1540 218f4c3d Michael Hanselmann
        node = nodes[node_name]
1541 218f4c3d Michael Hanselmann
      except KeyError:
1542 218f4c3d Michael Hanselmann
        raise errors.ConfigurationError("Unable to find node '%s'" % node_name)
1543 218f4c3d Michael Hanselmann
1544 218f4c3d Michael Hanselmann
      if node.group == new_group_uuid:
1545 218f4c3d Michael Hanselmann
        # Node is being assigned to its current group
1546 218f4c3d Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
1547 218f4c3d Michael Hanselmann
                      node_name, node.group)
1548 218f4c3d Michael Hanselmann
        continue
1549 218f4c3d Michael Hanselmann
1550 218f4c3d Michael Hanselmann
      # Try to find current group of node
1551 218f4c3d Michael Hanselmann
      try:
1552 218f4c3d Michael Hanselmann
        old_group = groups[node.group]
1553 218f4c3d Michael Hanselmann
      except KeyError:
1554 218f4c3d Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
1555 218f4c3d Michael Hanselmann
                                        node.group)
1556 218f4c3d Michael Hanselmann
1557 218f4c3d Michael Hanselmann
      # Try to find new group for node
1558 218f4c3d Michael Hanselmann
      try:
1559 218f4c3d Michael Hanselmann
        new_group = groups[new_group_uuid]
1560 218f4c3d Michael Hanselmann
      except KeyError:
1561 218f4c3d Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
1562 218f4c3d Michael Hanselmann
                                        new_group_uuid)
1563 218f4c3d Michael Hanselmann
1564 218f4c3d Michael Hanselmann
      assert node.name in old_group.members, \
1565 218f4c3d Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
1566 218f4c3d Michael Hanselmann
         " old group '%s'" % (node.name, old_group.uuid))
1567 218f4c3d Michael Hanselmann
      assert node.name not in new_group.members, \
1568 218f4c3d Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
1569 218f4c3d Michael Hanselmann
         " its new group '%s'" % (node.name, new_group.uuid))
1570 218f4c3d Michael Hanselmann
1571 218f4c3d Michael Hanselmann
      resmod.append((node, old_group, new_group))
1572 218f4c3d Michael Hanselmann
1573 218f4c3d Michael Hanselmann
    # Apply changes
1574 218f4c3d Michael Hanselmann
    for (node, old_group, new_group) in resmod:
1575 218f4c3d Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
1576 218f4c3d Michael Hanselmann
        "Assigning to current group is not possible"
1577 218f4c3d Michael Hanselmann
1578 218f4c3d Michael Hanselmann
      node.group = new_group.uuid
1579 218f4c3d Michael Hanselmann
1580 218f4c3d Michael Hanselmann
      # Update members of involved groups
1581 218f4c3d Michael Hanselmann
      if node.name in old_group.members:
1582 218f4c3d Michael Hanselmann
        old_group.members.remove(node.name)
1583 218f4c3d Michael Hanselmann
      if node.name not in new_group.members:
1584 218f4c3d Michael Hanselmann
        new_group.members.append(node.name)
1585 218f4c3d Michael Hanselmann
1586 218f4c3d Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
1587 218f4c3d Michael Hanselmann
    now = time.time()
1588 218f4c3d Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable-msg=W0142
1589 218f4c3d Michael Hanselmann
      obj.serial_no += 1
1590 218f4c3d Michael Hanselmann
      obj.mtime = now
1591 218f4c3d Michael Hanselmann
1592 218f4c3d Michael Hanselmann
    # Force ssconf update
1593 218f4c3d Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1594 218f4c3d Michael Hanselmann
1595 218f4c3d Michael Hanselmann
    self._WriteConfig()
1596 218f4c3d Michael Hanselmann
1597 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1598 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1599 a8083063 Iustin Pop

1600 a8083063 Iustin Pop
    """
1601 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1602 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1603 a8083063 Iustin Pop
1604 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1605 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1606 76d5d3a3 Iustin Pop

1607 76d5d3a3 Iustin Pop
    """
1608 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1609 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1610 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1611 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1612 76d5d3a3 Iustin Pop
1613 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1614 a8083063 Iustin Pop
    """Read the config data from disk.
1615 a8083063 Iustin Pop

1616 a8083063 Iustin Pop
    """
1617 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1618 13998ef2 Michael Hanselmann
1619 a8083063 Iustin Pop
    try:
1620 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1621 13998ef2 Michael Hanselmann
    except Exception, err:
1622 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1623 5b263ed7 Michael Hanselmann
1624 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1625 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1626 5b263ed7 Michael Hanselmann
1627 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1628 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1629 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1630 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1631 90d726a8 Iustin Pop
1632 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
1633 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
1634 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
1635 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
1636 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
1637 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
1638 eb180fe2 Iustin Pop
1639 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1640 90d726a8 Iustin Pop
    data.UpgradeConfig()
1641 90d726a8 Iustin Pop
1642 a8083063 Iustin Pop
    self._config_data = data
1643 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1644 0779e3aa Iustin Pop
    # ssconf update
1645 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1646 a8083063 Iustin Pop
1647 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1648 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1649 76d5d3a3 Iustin Pop
1650 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
1651 bd407597 Iustin Pop
1652 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1653 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1654 76d5d3a3 Iustin Pop

1655 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1656 76d5d3a3 Iustin Pop
    whole configuration, etc.
1657 76d5d3a3 Iustin Pop

1658 111c4e2f Guido Trotter
    @warning: this function will call L{_WriteConfig()}, but also
1659 111c4e2f Guido Trotter
        L{DropECReservations} so it needs to be called only from a
1660 111c4e2f Guido Trotter
        "safe" place (the constructor). If one wanted to call it with
1661 111c4e2f Guido Trotter
        the lock held, a DropECReservationUnlocked would need to be
1662 111c4e2f Guido Trotter
        created first, to avoid causing deadlock.
1663 76d5d3a3 Iustin Pop

1664 76d5d3a3 Iustin Pop
    """
1665 76d5d3a3 Iustin Pop
    modified = False
1666 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1667 76d5d3a3 Iustin Pop
      if item.uuid is None:
1668 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1669 76d5d3a3 Iustin Pop
        modified = True
1670 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
1671 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
1672 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
1673 75cf411a Adeodato Simo
                                            members=[])
1674 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
1675 f9e81396 Guido Trotter
      modified = True
1676 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
1677 f936c153 Iustin Pop
      if not node.group:
1678 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
1679 190e3cb6 Guido Trotter
        modified = True
1680 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
1681 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
1682 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
1683 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
1684 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
1685 76d5d3a3 Iustin Pop
    if modified:
1686 76d5d3a3 Iustin Pop
      self._WriteConfig()
1687 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1688 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1689 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1690 4fae38c5 Guido Trotter
1691 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1692 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1693 a8083063 Iustin Pop

1694 a8083063 Iustin Pop
    Currently, this only copies the configuration file. In the future,
1695 a8083063 Iustin Pop
    it could be used to encapsulate the 2/3-phase update mechanism.
1696 a8083063 Iustin Pop

1697 a8083063 Iustin Pop
    """
1698 a8083063 Iustin Pop
    if self._offline:
1699 a8083063 Iustin Pop
      return True
1700 a4eae71f Michael Hanselmann
1701 a8083063 Iustin Pop
    bad = False
1702 a8083063 Iustin Pop
1703 6a5b8b4b Iustin Pop
    node_list = []
1704 6a5b8b4b Iustin Pop
    addr_list = []
1705 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1706 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1707 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1708 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1709 6b294c53 Iustin Pop
    # in between
1710 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1711 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1712 6a5b8b4b Iustin Pop
        continue
1713 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1714 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1715 6a5b8b4b Iustin Pop
        continue
1716 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1717 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1718 6b294c53 Iustin Pop
1719 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1720 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1721 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1722 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1723 1b54fc6c Guido Trotter
      if msg:
1724 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1725 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1726 1b54fc6c Guido Trotter
        logging.error(msg)
1727 a4eae71f Michael Hanselmann
1728 a4eae71f Michael Hanselmann
        if feedback_fn:
1729 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1730 a4eae71f Michael Hanselmann
1731 a8083063 Iustin Pop
        bad = True
1732 a4eae71f Michael Hanselmann
1733 a8083063 Iustin Pop
    return not bad
1734 a8083063 Iustin Pop
1735 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1736 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1737 a8083063 Iustin Pop

1738 a8083063 Iustin Pop
    """
1739 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
1740 a4eae71f Michael Hanselmann
1741 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
1742 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
1743 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
1744 d2231b8c Iustin Pop
    # recovery to the user
1745 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1746 4a89c54a Iustin Pop
    if config_errors:
1747 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
1748 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
1749 d2231b8c Iustin Pop
      logging.critical(errmsg)
1750 d2231b8c Iustin Pop
      if feedback_fn:
1751 d2231b8c Iustin Pop
        feedback_fn(errmsg)
1752 d2231b8c Iustin Pop
1753 a8083063 Iustin Pop
    if destination is None:
1754 a8083063 Iustin Pop
      destination = self._cfg_file
1755 a8083063 Iustin Pop
    self._BumpSerialNo()
1756 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1757 13998ef2 Michael Hanselmann
1758 e60c73a1 René Nussbaumer
    getents = self._getents()
1759 bd407597 Iustin Pop
    try:
1760 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
1761 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
1762 bd407597 Iustin Pop
    except errors.LockError:
1763 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
1764 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
1765 bd407597 Iustin Pop
                                      " update")
1766 bd407597 Iustin Pop
    try:
1767 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
1768 bd407597 Iustin Pop
    finally:
1769 bd407597 Iustin Pop
      os.close(fd)
1770 13998ef2 Michael Hanselmann
1771 14e15659 Iustin Pop
    self.write_count += 1
1772 3d3a04bc Iustin Pop
1773 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
1774 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
1775 a8083063 Iustin Pop
1776 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
1777 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
1778 d9a855f1 Michael Hanselmann
      if not self._offline:
1779 cd34faf2 Michael Hanselmann
        result = rpc.RpcRunner.call_write_ssconf_files(
1780 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
1781 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
1782 a4eae71f Michael Hanselmann
1783 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
1784 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
1785 e1e75d00 Iustin Pop
          if msg:
1786 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
1787 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
1788 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
1789 a4eae71f Michael Hanselmann
1790 a4eae71f Michael Hanselmann
            if feedback_fn:
1791 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
1792 a4eae71f Michael Hanselmann
1793 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
1794 54d1a06e Michael Hanselmann
1795 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
1796 054596f0 Iustin Pop
    """Return the values needed by ssconf.
1797 054596f0 Iustin Pop

1798 054596f0 Iustin Pop
    @rtype: dict
1799 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1800 054596f0 Iustin Pop
        associated value
1801 054596f0 Iustin Pop

1802 054596f0 Iustin Pop
    """
1803 a3316e4a Iustin Pop
    fn = "\n".join
1804 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1805 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1806 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1807 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1808 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1809 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1810 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1811 a3316e4a Iustin Pop
1812 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1813 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1814 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1815 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1816 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1817 8113a52e Luca Bigliardi
                     if node.master_candidate)
1818 a3316e4a Iustin Pop
    node_data = fn(node_names)
1819 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1820 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1821 f56618e0 Iustin Pop
1822 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1823 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1824 4f7a6a10 Iustin Pop
1825 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
1826 4f7a6a10 Iustin Pop
1827 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
1828 0fbae49a Balazs Lecz
1829 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
1830 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
1831 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
1832 6f076453 Guido Trotter
1833 03d1dba2 Michael Hanselmann
    return {
1834 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1835 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1836 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1837 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1838 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1839 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1840 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1841 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1842 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1843 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1844 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1845 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1846 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1847 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
1848 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1849 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1850 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
1851 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
1852 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
1853 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
1854 03d1dba2 Michael Hanselmann
      }
1855 03d1dba2 Michael Hanselmann
1856 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1857 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
1858 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
1859 d367b66c Manuel Franceschini

1860 d367b66c Manuel Franceschini
    """
1861 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
1862 d367b66c Manuel Franceschini
1863 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1864 a8083063 Iustin Pop
  def GetVGName(self):
1865 a8083063 Iustin Pop
    """Return the volume group name.
1866 a8083063 Iustin Pop

1867 a8083063 Iustin Pop
    """
1868 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1869 a8083063 Iustin Pop
1870 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1871 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1872 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1873 89ff8e15 Manuel Franceschini

1874 89ff8e15 Manuel Franceschini
    """
1875 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1876 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1877 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1878 89ff8e15 Manuel Franceschini
1879 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1880 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
1881 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
1882 9e33896b Luca Bigliardi

1883 9e33896b Luca Bigliardi
    """
1884 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
1885 9e33896b Luca Bigliardi
1886 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
1887 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
1888 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
1889 9e33896b Luca Bigliardi

1890 9e33896b Luca Bigliardi
    """
1891 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
1892 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
1893 9e33896b Luca Bigliardi
    self._WriteConfig()
1894 9e33896b Luca Bigliardi
1895 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1896 a8083063 Iustin Pop
  def GetMACPrefix(self):
1897 a8083063 Iustin Pop
    """Return the mac prefix.
1898 a8083063 Iustin Pop

1899 a8083063 Iustin Pop
    """
1900 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1901 62779dd0 Iustin Pop
1902 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1903 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1904 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1905 62779dd0 Iustin Pop

1906 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1907 c41eea6e Iustin Pop
    @return: the cluster object
1908 62779dd0 Iustin Pop

1909 62779dd0 Iustin Pop
    """
1910 62779dd0 Iustin Pop
    return self._config_data.cluster
1911 e00fb268 Iustin Pop
1912 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1913 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
1914 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
1915 51cb1581 Luca Bigliardi

1916 51cb1581 Luca Bigliardi
    """
1917 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
1918 51cb1581 Luca Bigliardi
1919 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1920 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1921 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1922 e00fb268 Iustin Pop

1923 e00fb268 Iustin Pop
    This function must be called when an object (as returned by
1924 e00fb268 Iustin Pop
    GetInstanceInfo, GetNodeInfo, GetCluster) has been updated and the
1925 e00fb268 Iustin Pop
    caller wants the modifications saved to the backing store. Note
1926 e00fb268 Iustin Pop
    that all modified objects will be saved, but the target argument
1927 e00fb268 Iustin Pop
    is the one the caller wants to ensure that it's saved.
1928 e00fb268 Iustin Pop

1929 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1930 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1931 c41eea6e Iustin Pop
        the cluster
1932 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1933 c41eea6e Iustin Pop

1934 e00fb268 Iustin Pop
    """
1935 e00fb268 Iustin Pop
    if self._config_data is None:
1936 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1937 3ecf6786 Iustin Pop
                                   " cannot save.")
1938 f34901f8 Iustin Pop
    update_serial = False
1939 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1940 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1941 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1942 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1943 f34901f8 Iustin Pop
      update_serial = True
1944 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1945 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1946 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
1947 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
1948 e00fb268 Iustin Pop
    else:
1949 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1950 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1951 e00fb268 Iustin Pop
    if not test:
1952 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1953 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1954 f34901f8 Iustin Pop
    target.serial_no += 1
1955 d693c864 Iustin Pop
    target.mtime = now = time.time()
1956 f34901f8 Iustin Pop
1957 cff4c037 Iustin Pop
    if update_serial:
1958 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1959 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1960 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1961 b989e85d Iustin Pop
1962 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1963 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1964 61cf6b5e Iustin Pop
1965 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
1966 73064714 Guido Trotter
1967 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
1968 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
1969 73064714 Guido Trotter
    """Drop per-execution-context reservations
1970 73064714 Guido Trotter

1971 73064714 Guido Trotter
    """
1972 d8aee57e Iustin Pop
    for rm in self._all_rms:
1973 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)