Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 3b3b1bca

History | View | Annotate | Download (68.5 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 3b3b1bca 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 b459a848 Andrea Spadaccini
# pylint: disable=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 54c31fd3 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 fe698b38 Michael Hanselmann
def _MatchNameComponentIgnoreCase(short_name, names):
130 3a93eebb Michael Hanselmann
  """Wrapper around L{utils.text.MatchNameComponent}.
131 fe698b38 Michael Hanselmann

132 fe698b38 Michael Hanselmann
  """
133 fe698b38 Michael Hanselmann
  return utils.MatchNameComponent(short_name, names, case_sensitive=False)
134 fe698b38 Michael Hanselmann
135 fe698b38 Michael Hanselmann
136 a8083063 Iustin Pop
class ConfigWriter:
137 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
138 a8083063 Iustin Pop

139 d8aee57e Iustin Pop
  @ivar _temporary_lvs: reservation manager for temporary LVs
140 d8aee57e Iustin Pop
  @ivar _all_rms: a list of all temporary reservation managers
141 d8aee57e Iustin Pop

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

175 a8083063 Iustin Pop
    """
176 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
177 a8083063 Iustin Pop
178 36b66e6e Guido Trotter
  def _GenerateOneMAC(self):
179 36b66e6e Guido Trotter
    """Generate one mac address
180 36b66e6e Guido Trotter

181 36b66e6e Guido Trotter
    """
182 36b66e6e Guido Trotter
    prefix = self._config_data.cluster.mac_prefix
183 36b66e6e Guido Trotter
    byte1 = random.randrange(0, 256)
184 36b66e6e Guido Trotter
    byte2 = random.randrange(0, 256)
185 36b66e6e Guido Trotter
    byte3 = random.randrange(0, 256)
186 36b66e6e Guido Trotter
    mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
187 36b66e6e Guido Trotter
    return mac
188 36b66e6e Guido Trotter
189 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
190 5768e6a6 René Nussbaumer
  def GetNdParams(self, node):
191 5768e6a6 René Nussbaumer
    """Get the node params populated with cluster defaults.
192 5768e6a6 René Nussbaumer

193 ce523de1 Michael Hanselmann
    @type node: L{objects.Node}
194 5768e6a6 René Nussbaumer
    @param node: The node we want to know the params for
195 5768e6a6 René Nussbaumer
    @return: A dict with the filled in node params
196 5768e6a6 René Nussbaumer

197 5768e6a6 René Nussbaumer
    """
198 5768e6a6 René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
199 5768e6a6 René Nussbaumer
    return self._config_data.cluster.FillND(node, nodegroup)
200 5768e6a6 René Nussbaumer
201 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
202 36b66e6e Guido Trotter
  def GenerateMAC(self, ec_id):
203 a8083063 Iustin Pop
    """Generate a MAC for an instance.
204 a8083063 Iustin Pop

205 a8083063 Iustin Pop
    This should check the current instances for duplicates.
206 a8083063 Iustin Pop

207 a8083063 Iustin Pop
    """
208 36b66e6e Guido Trotter
    existing = self._AllMACs()
209 36b66e6e Guido Trotter
    return self._temporary_ids.Generate(existing, self._GenerateOneMAC, ec_id)
210 a8083063 Iustin Pop
211 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
212 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
213 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
214 1862d460 Alexander Schreiber

215 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
216 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
217 1862d460 Alexander Schreiber

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

229 d8aee57e Iustin Pop
    @type lv_name: string
230 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
231 d8aee57e Iustin Pop

232 d8aee57e Iustin Pop
    """
233 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
234 d8aee57e Iustin Pop
    if lv_name in all_lvs:
235 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
236 d8aee57e Iustin Pop
    else:
237 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
238 d8aee57e Iustin Pop
239 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
240 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
241 f9518d38 Iustin Pop
    """Generate a DRBD secret.
242 f9518d38 Iustin Pop

243 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
244 f9518d38 Iustin Pop

245 f9518d38 Iustin Pop
    """
246 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
247 afa1386e Guido Trotter
                                            utils.GenerateSecret,
248 afa1386e Guido Trotter
                                            ec_id)
249 8d9c3bef Michael Hanselmann
250 34e54ebc Iustin Pop
  def _AllLVs(self):
251 923b1523 Iustin Pop
    """Compute the list of all LVs.
252 923b1523 Iustin Pop

253 923b1523 Iustin Pop
    """
254 923b1523 Iustin Pop
    lvnames = set()
255 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
256 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
257 923b1523 Iustin Pop
      for lv_list in node_data.values():
258 923b1523 Iustin Pop
        lvnames.update(lv_list)
259 923b1523 Iustin Pop
    return lvnames
260 923b1523 Iustin Pop
261 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
262 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
263 34e54ebc Iustin Pop

264 34e54ebc Iustin Pop
    @type include_temporary: boolean
265 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
266 34e54ebc Iustin Pop
    @rtype: set
267 34e54ebc Iustin Pop
    @return: a set of IDs
268 34e54ebc Iustin Pop

269 34e54ebc Iustin Pop
    """
270 34e54ebc Iustin Pop
    existing = set()
271 34e54ebc Iustin Pop
    if include_temporary:
272 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
273 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
274 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
275 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
276 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
277 34e54ebc Iustin Pop
    return existing
278 34e54ebc Iustin Pop
279 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
280 430b923c Iustin Pop
    """Generate an unique UUID.
281 923b1523 Iustin Pop

282 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
283 923b1523 Iustin Pop
    duplicates.
284 923b1523 Iustin Pop

285 c41eea6e Iustin Pop
    @rtype: string
286 c41eea6e Iustin Pop
    @return: the unique id
287 923b1523 Iustin Pop

288 923b1523 Iustin Pop
    """
289 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
290 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
291 923b1523 Iustin Pop
292 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
293 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
294 430b923c Iustin Pop
    """Generate an unique ID.
295 430b923c Iustin Pop

296 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
297 430b923c Iustin Pop

298 4fae38c5 Guido Trotter
    @type ec_id: string
299 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
300 34d657ba Iustin Pop

301 34d657ba Iustin Pop
    """
302 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
303 34d657ba Iustin Pop
304 a8083063 Iustin Pop
  def _AllMACs(self):
305 a8083063 Iustin Pop
    """Return all MACs present in the config.
306 a8083063 Iustin Pop

307 c41eea6e Iustin Pop
    @rtype: list
308 c41eea6e Iustin Pop
    @return: the list of all MACs
309 c41eea6e Iustin Pop

310 a8083063 Iustin Pop
    """
311 a8083063 Iustin Pop
    result = []
312 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
313 a8083063 Iustin Pop
      for nic in instance.nics:
314 a8083063 Iustin Pop
        result.append(nic.mac)
315 a8083063 Iustin Pop
316 a8083063 Iustin Pop
    return result
317 a8083063 Iustin Pop
318 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
319 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
320 f9518d38 Iustin Pop

321 c41eea6e Iustin Pop
    @rtype: list
322 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
323 c41eea6e Iustin Pop

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

343 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
344 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
345 4b98ac29 Iustin Pop
    @type l_ids: list
346 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
347 4b98ac29 Iustin Pop
    @type p_ids: list
348 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
349 4b98ac29 Iustin Pop
    @rtype: list
350 4b98ac29 Iustin Pop
    @return: a list of error messages
351 4b98ac29 Iustin Pop

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

373 4a89c54a Iustin Pop
    @rtype: list
374 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
375 4a89c54a Iustin Pop
        configuration errors
376 4a89c54a Iustin Pop

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

593 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
594 4a89c54a Iustin Pop

595 4a89c54a Iustin Pop
    @rtype: list
596 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
597 4a89c54a Iustin Pop
        configuration errors
598 4a89c54a Iustin Pop

599 4a89c54a Iustin Pop
    """
600 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
601 4a89c54a Iustin Pop
602 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
603 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
604 a8083063 Iustin Pop

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

607 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
608 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
609 a8083063 Iustin Pop
    node.
610 a8083063 Iustin Pop

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

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

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

646 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
647 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
648 f78ede4e Guido Trotter
    node.
649 f78ede4e Guido Trotter

650 f78ede4e Guido Trotter
    """
651 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
652 f78ede4e Guido Trotter
653 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
654 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
655 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
656 b2fddf63 Iustin Pop

657 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
658 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
659 3b3b1bca Dimitris Aragiorgis
        configuration is stable
660 3b3b1bca Dimitris Aragiorgis

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

671 264bb3c5 Michael Hanselmann
    """
672 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
673 264bb3c5 Michael Hanselmann
674 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
675 a8083063 Iustin Pop
  def AllocatePort(self):
676 a8083063 Iustin Pop
    """Allocate a port.
677 a8083063 Iustin Pop

678 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
679 b2fddf63 Iustin Pop
    default port range (and in this case we increase
680 b2fddf63 Iustin Pop
    highest_used_port).
681 a8083063 Iustin Pop

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

700 4a89c54a Iustin Pop
    @rtype: (dict, list)
701 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
702 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
703 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
704 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
705 4a89c54a Iustin Pop
        should raise an exception
706 a81c53c9 Iustin Pop

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

740 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
741 6d2e83d5 Iustin Pop

742 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
743 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
744 6d2e83d5 Iustin Pop
        an empty list).
745 6d2e83d5 Iustin Pop

746 6d2e83d5 Iustin Pop
    """
747 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
748 4a89c54a Iustin Pop
    if duplicates:
749 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
750 4a89c54a Iustin Pop
                                      str(duplicates))
751 4a89c54a Iustin Pop
    return d_map
752 6d2e83d5 Iustin Pop
753 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
754 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
755 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
756 a81c53c9 Iustin Pop

757 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
758 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
759 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
760 a81c53c9 Iustin Pop
    order as the passed nodes.
761 a81c53c9 Iustin Pop

762 32388e6d Iustin Pop
    @type instance: string
763 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
764 32388e6d Iustin Pop

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

812 a81c53c9 Iustin Pop
    @type instance: string
813 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
814 a81c53c9 Iustin Pop
                     released
815 a81c53c9 Iustin Pop

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

827 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
828 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
829 61cf6b5e Iustin Pop
    functions.
830 61cf6b5e Iustin Pop

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

833 61cf6b5e Iustin Pop
    @type instance: string
834 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
835 61cf6b5e Iustin Pop
                     released
836 61cf6b5e Iustin Pop

837 61cf6b5e Iustin Pop
    """
838 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
839 61cf6b5e Iustin Pop
840 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
841 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
842 4a8b186a Michael Hanselmann
    """Get the configuration version.
843 4a8b186a Michael Hanselmann

844 4a8b186a Michael Hanselmann
    @return: Config version
845 4a8b186a Michael Hanselmann

846 4a8b186a Michael Hanselmann
    """
847 4a8b186a Michael Hanselmann
    return self._config_data.version
848 4a8b186a Michael Hanselmann
849 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
850 4a8b186a Michael Hanselmann
  def GetClusterName(self):
851 4a8b186a Michael Hanselmann
    """Get cluster name.
852 4a8b186a Michael Hanselmann

853 4a8b186a Michael Hanselmann
    @return: Cluster name
854 4a8b186a Michael Hanselmann

855 4a8b186a Michael Hanselmann
    """
856 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
857 4a8b186a Michael Hanselmann
858 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
859 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
860 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
861 4a8b186a Michael Hanselmann

862 4a8b186a Michael Hanselmann
    @return: Master hostname
863 4a8b186a Michael Hanselmann

864 4a8b186a Michael Hanselmann
    """
865 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
866 4a8b186a Michael Hanselmann
867 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
868 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
869 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
870 4a8b186a Michael Hanselmann

871 4a8b186a Michael Hanselmann
    @return: Master IP
872 4a8b186a Michael Hanselmann

873 4a8b186a Michael Hanselmann
    """
874 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
875 4a8b186a Michael Hanselmann
876 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
877 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
878 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
879 4a8b186a Michael Hanselmann

880 4a8b186a Michael Hanselmann
    """
881 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
882 4a8b186a Michael Hanselmann
883 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
884 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
885 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
886 4a8b186a Michael Hanselmann

887 4a8b186a Michael Hanselmann
    """
888 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
889 4a8b186a Michael Hanselmann
890 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
891 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
892 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
893 4b97f902 Apollon Oikonomopoulos

894 4b97f902 Apollon Oikonomopoulos
    """
895 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
896 4b97f902 Apollon Oikonomopoulos
897 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
898 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
899 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
900 4a8b186a Michael Hanselmann

901 4a8b186a Michael Hanselmann
    """
902 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
903 4a8b186a Michael Hanselmann
904 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
905 a8083063 Iustin Pop
  def GetHostKey(self):
906 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
907 a8083063 Iustin Pop

908 c41eea6e Iustin Pop
    @rtype: string
909 c41eea6e Iustin Pop
    @return: the rsa hostkey
910 a8083063 Iustin Pop

911 a8083063 Iustin Pop
    """
912 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
913 a8083063 Iustin Pop
914 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
915 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
916 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
917 bf4af505 Apollon Oikonomopoulos

918 bf4af505 Apollon Oikonomopoulos
    """
919 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
920 bf4af505 Apollon Oikonomopoulos
921 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
922 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
923 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
924 868a98ca Manuel Franceschini

925 868a98ca Manuel Franceschini
    @return: primary ip family
926 868a98ca Manuel Franceschini

927 868a98ca Manuel Franceschini
    """
928 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
929 868a98ca Manuel Franceschini
930 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
931 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
932 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
933 e11a1b77 Adeodato Simo

934 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
935 90e99856 Adeodato Simo
    according to their default values.
936 90e99856 Adeodato Simo

937 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
938 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
939 e11a1b77 Adeodato Simo
    @type ec_id: string
940 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
941 e11a1b77 Adeodato Simo
    @type check_uuid: bool
942 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
943 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
944 e11a1b77 Adeodato Simo
                       configuration already
945 e11a1b77 Adeodato Simo

946 e11a1b77 Adeodato Simo
    """
947 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
948 e11a1b77 Adeodato Simo
    self._WriteConfig()
949 e11a1b77 Adeodato Simo
950 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
951 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
952 e11a1b77 Adeodato Simo

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

983 e11a1b77 Adeodato Simo
    @type group_uuid: string
984 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
985 e11a1b77 Adeodato Simo

986 e11a1b77 Adeodato Simo
    """
987 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
988 e11a1b77 Adeodato Simo
989 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
990 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
991 e11a1b77 Adeodato Simo
992 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
993 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
994 0389c42a Stephen Shirley
995 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
996 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
997 e11a1b77 Adeodato Simo
    self._WriteConfig()
998 e11a1b77 Adeodato Simo
999 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1000 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1001 eaa98a04 Guido Trotter

1002 eaa98a04 Guido Trotter
    @type target: string or None
1003 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1004 eaa98a04 Guido Trotter
    @rtype: string
1005 412b3531 Guido Trotter
    @return: nodegroup UUID
1006 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1007 eaa98a04 Guido Trotter

1008 eaa98a04 Guido Trotter
    """
1009 eaa98a04 Guido Trotter
    if target is None:
1010 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1011 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1012 eaa98a04 Guido Trotter
                                   " group must be specified explicitely.")
1013 eaa98a04 Guido Trotter
      else:
1014 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1015 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1016 eaa98a04 Guido Trotter
      return target
1017 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1018 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1019 eaa98a04 Guido Trotter
        return nodegroup.uuid
1020 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1021 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1022 eaa98a04 Guido Trotter
1023 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1024 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1025 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1026 e85d8982 Stephen Shirley

1027 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1028 e85d8982 Stephen Shirley

1029 e85d8982 Stephen Shirley
    @type target: string or None
1030 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1031 e85d8982 Stephen Shirley
    @rtype: string
1032 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1033 e85d8982 Stephen Shirley

1034 e85d8982 Stephen Shirley
    """
1035 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1036 e85d8982 Stephen Shirley
1037 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1038 648e4196 Guido Trotter
    """Lookup a node group.
1039 648e4196 Guido Trotter

1040 648e4196 Guido Trotter
    @type uuid: string
1041 648e4196 Guido Trotter
    @param uuid: group UUID
1042 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1043 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1044 648e4196 Guido Trotter

1045 648e4196 Guido Trotter
    """
1046 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1047 648e4196 Guido Trotter
      return None
1048 648e4196 Guido Trotter
1049 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1050 648e4196 Guido Trotter
1051 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1052 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1053 5768e6a6 René Nussbaumer
    """Lookup a node group.
1054 5768e6a6 René Nussbaumer

1055 5768e6a6 René Nussbaumer
    @type uuid: string
1056 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1057 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1058 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1059 5768e6a6 René Nussbaumer

1060 5768e6a6 René Nussbaumer
    """
1061 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1062 5768e6a6 René Nussbaumer
1063 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1064 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1065 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1066 622444e5 Iustin Pop

1067 622444e5 Iustin Pop
    """
1068 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1069 622444e5 Iustin Pop
1070 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1071 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1072 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1073 1ac6f2ad Guido Trotter

1074 1ac6f2ad Guido Trotter
    """
1075 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1076 1ac6f2ad Guido Trotter
1077 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1078 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1079 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1080 dac81741 Michael Hanselmann

1081 dac81741 Michael Hanselmann
    """
1082 dac81741 Michael Hanselmann
    ngfn = lambda node_name: self._UnlockedGetNodeInfo(node_name).group
1083 dac81741 Michael Hanselmann
    return frozenset(member_name
1084 dac81741 Michael Hanselmann
                     for node_name in nodes
1085 dac81741 Michael Hanselmann
                     for member_name in
1086 dac81741 Michael Hanselmann
                       self._UnlockedGetNodeGroup(ngfn(node_name)).members)
1087 dac81741 Michael Hanselmann
1088 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1089 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1090 a8083063 Iustin Pop
    """Add an instance to the config.
1091 a8083063 Iustin Pop

1092 a8083063 Iustin Pop
    This should be used after creating a new instance.
1093 a8083063 Iustin Pop

1094 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1095 c41eea6e Iustin Pop
    @param instance: the instance object
1096 c41eea6e Iustin Pop

1097 a8083063 Iustin Pop
    """
1098 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1099 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1100 a8083063 Iustin Pop
1101 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1102 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1103 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1104 923b1523 Iustin Pop
1105 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1106 e4640214 Guido Trotter
    for nic in instance.nics:
1107 e4640214 Guido Trotter
      if nic.mac in all_macs:
1108 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1109 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1110 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1111 430b923c Iustin Pop
1112 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1113 e4640214 Guido Trotter
1114 b989e85d Iustin Pop
    instance.serial_no = 1
1115 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1116 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1117 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1118 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1119 a8083063 Iustin Pop
    self._WriteConfig()
1120 a8083063 Iustin Pop
1121 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1122 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1123 430b923c Iustin Pop

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

1127 430b923c Iustin Pop
    """
1128 430b923c Iustin Pop
    if not item.uuid:
1129 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1130 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1131 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1132 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1133 430b923c Iustin Pop
1134 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1135 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1136 a8083063 Iustin Pop

1137 a8083063 Iustin Pop
    """
1138 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
1139 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1140 a8083063 Iustin Pop
1141 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1142 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1143 3ecf6786 Iustin Pop
                                      instance_name)
1144 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1145 0d68c45d Iustin Pop
    if instance.admin_up != status:
1146 0d68c45d Iustin Pop
      instance.admin_up = status
1147 b989e85d Iustin Pop
      instance.serial_no += 1
1148 d693c864 Iustin Pop
      instance.mtime = time.time()
1149 455a3445 Iustin Pop
      self._WriteConfig()
1150 a8083063 Iustin Pop
1151 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1152 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1153 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1154 6a408fb2 Iustin Pop

1155 6a408fb2 Iustin Pop
    """
1156 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
1157 6a408fb2 Iustin Pop
1158 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1159 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1160 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1161 a8083063 Iustin Pop

1162 a8083063 Iustin Pop
    """
1163 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1164 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1165 f396ad8c Vangelis Koukis
1166 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1167 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1168 f396ad8c Vangelis Koukis
    inst = self._config_data.instances[instance_name]
1169 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1170 f396ad8c Vangelis Koukis
    if network_port is not None:
1171 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1172 f396ad8c Vangelis Koukis
1173 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1174 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1175 a8083063 Iustin Pop
    self._WriteConfig()
1176 a8083063 Iustin Pop
1177 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1178 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1179 fc95f88f Iustin Pop
    """Rename an instance.
1180 fc95f88f Iustin Pop

1181 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1182 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1183 fc95f88f Iustin Pop
    rename.
1184 fc95f88f Iustin Pop

1185 fc95f88f Iustin Pop
    """
1186 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
1187 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
1188 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
1189 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
1190 fc95f88f Iustin Pop
    inst.name = new_name
1191 b23c4333 Manuel Franceschini
1192 b23c4333 Manuel Franceschini
    for disk in inst.disks:
1193 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1194 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1195 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1196 3721d2fe Guido Trotter
        disk_fname = "disk%s" % disk.iv_name.split("/")[1]
1197 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
1198 c4feafe8 Iustin Pop
                                              utils.PathJoin(file_storage_dir,
1199 c4feafe8 Iustin Pop
                                                             inst.name,
1200 3721d2fe Guido Trotter
                                                             disk_fname))
1201 b23c4333 Manuel Franceschini
1202 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1203 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1204 1fc34c48 Michael Hanselmann
1205 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
1206 fc95f88f Iustin Pop
    self._WriteConfig()
1207 fc95f88f Iustin Pop
1208 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1209 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
1210 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1211 a8083063 Iustin Pop

1212 a8083063 Iustin Pop
    """
1213 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
1214 a8083063 Iustin Pop
1215 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1216 94bbfece Iustin Pop
    """Get the list of instances.
1217 94bbfece Iustin Pop

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

1220 94bbfece Iustin Pop
    """
1221 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1222 94bbfece Iustin Pop
1223 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1224 a8083063 Iustin Pop
  def GetInstanceList(self):
1225 a8083063 Iustin Pop
    """Get the list of instances.
1226 a8083063 Iustin Pop

1227 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1228 c41eea6e Iustin Pop
        'instance1.example.com']
1229 a8083063 Iustin Pop

1230 a8083063 Iustin Pop
    """
1231 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1232 a8083063 Iustin Pop
1233 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1234 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1235 a8083063 Iustin Pop

1236 a8083063 Iustin Pop
    """
1237 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1238 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1239 a8083063 Iustin Pop
1240 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1241 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1242 94bbfece Iustin Pop

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

1245 94bbfece Iustin Pop
    """
1246 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1247 94bbfece Iustin Pop
      return None
1248 94bbfece Iustin Pop
1249 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1250 94bbfece Iustin Pop
1251 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1252 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1253 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1254 a8083063 Iustin Pop

1255 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1256 a8083063 Iustin Pop
    an instance are taken from the live systems.
1257 a8083063 Iustin Pop

1258 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1259 c41eea6e Iustin Pop
        I{instance1.example.com}
1260 a8083063 Iustin Pop

1261 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1262 c41eea6e Iustin Pop
    @return: the instance object
1263 a8083063 Iustin Pop

1264 a8083063 Iustin Pop
    """
1265 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1266 a8083063 Iustin Pop
1267 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1268 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1269 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1270 2674690b Michael Hanselmann

1271 2674690b Michael Hanselmann
    @rtype: frozenset
1272 2674690b Michael Hanselmann

1273 2674690b Michael Hanselmann
    """
1274 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1275 2674690b Michael Hanselmann
    if not instance:
1276 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1277 2674690b Michael Hanselmann
1278 2674690b Michael Hanselmann
    if primary_only:
1279 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1280 2674690b Michael Hanselmann
    else:
1281 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1282 2674690b Michael Hanselmann
1283 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1284 2674690b Michael Hanselmann
                     for node_name in nodes)
1285 2674690b Michael Hanselmann
1286 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1287 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1288 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1289 71333cb9 Iustin Pop

1290 71333cb9 Iustin Pop
    @param instances: list of instance names
1291 71333cb9 Iustin Pop
    @rtype: list
1292 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1293 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1294 71333cb9 Iustin Pop
        node, while keeping the original order
1295 71333cb9 Iustin Pop

1296 71333cb9 Iustin Pop
    """
1297 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1298 71333cb9 Iustin Pop
1299 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1300 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1301 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1302 0b2de758 Iustin Pop

1303 0b2de758 Iustin Pop
    @rtype: dict
1304 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1305 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1306 0b2de758 Iustin Pop

1307 0b2de758 Iustin Pop
    """
1308 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1309 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1310 0b2de758 Iustin Pop
    return my_dict
1311 0b2de758 Iustin Pop
1312 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1313 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1314 a8083063 Iustin Pop
    """Add a node to the configuration.
1315 a8083063 Iustin Pop

1316 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1317 c41eea6e Iustin Pop
    @param node: a Node instance
1318 a8083063 Iustin Pop

1319 a8083063 Iustin Pop
    """
1320 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1321 d8470559 Michael Hanselmann
1322 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1323 430b923c Iustin Pop
1324 b989e85d Iustin Pop
    node.serial_no = 1
1325 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1326 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1327 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1328 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1329 a8083063 Iustin Pop
    self._WriteConfig()
1330 a8083063 Iustin Pop
1331 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1332 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1333 a8083063 Iustin Pop
    """Remove a node from the configuration.
1334 a8083063 Iustin Pop

1335 a8083063 Iustin Pop
    """
1336 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1337 d8470559 Michael Hanselmann
1338 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1339 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1340 a8083063 Iustin Pop
1341 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1342 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1343 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1344 a8083063 Iustin Pop
    self._WriteConfig()
1345 a8083063 Iustin Pop
1346 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1347 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1348 a8083063 Iustin Pop

1349 a8083063 Iustin Pop
    """
1350 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1351 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1352 a8083063 Iustin Pop
1353 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1354 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1355 a8083063 Iustin Pop

1356 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1357 c41eea6e Iustin Pop
    held.
1358 f78ede4e Guido Trotter

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

1361 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1362 c41eea6e Iustin Pop
    @return: the node object
1363 a8083063 Iustin Pop

1364 a8083063 Iustin Pop
    """
1365 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1366 a8083063 Iustin Pop
      return None
1367 a8083063 Iustin Pop
1368 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1369 a8083063 Iustin Pop
1370 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1371 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1372 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1373 f78ede4e Guido Trotter

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

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

1378 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1379 c41eea6e Iustin Pop
    @return: the node object
1380 f78ede4e Guido Trotter

1381 f78ede4e Guido Trotter
    """
1382 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1383 f78ede4e Guido Trotter
1384 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1385 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1386 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1387 8bf9e9a5 Iustin Pop

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

1390 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1391 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1392 8bf9e9a5 Iustin Pop

1393 8bf9e9a5 Iustin Pop
    """
1394 8bf9e9a5 Iustin Pop
    pri = []
1395 8bf9e9a5 Iustin Pop
    sec = []
1396 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1397 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1398 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1399 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1400 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1401 8bf9e9a5 Iustin Pop
    return (pri, sec)
1402 8bf9e9a5 Iustin Pop
1403 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1404 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1405 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1406 c71b049c Michael Hanselmann

1407 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1408 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1409 c71b049c Michael Hanselmann
    @rtype: frozenset
1410 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1411 c71b049c Michael Hanselmann

1412 c71b049c Michael Hanselmann
    """
1413 c71b049c Michael Hanselmann
    if primary_only:
1414 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1415 c71b049c Michael Hanselmann
    else:
1416 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1417 c71b049c Michael Hanselmann
1418 c71b049c Michael Hanselmann
    return frozenset(inst.name
1419 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1420 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1421 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1422 c71b049c Michael Hanselmann
1423 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1424 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1425 a8083063 Iustin Pop

1426 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1427 c41eea6e Iustin Pop
    held.
1428 c41eea6e Iustin Pop

1429 c41eea6e Iustin Pop
    @rtype: list
1430 f78ede4e Guido Trotter

1431 a8083063 Iustin Pop
    """
1432 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1433 a8083063 Iustin Pop
1434 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1435 f78ede4e Guido Trotter
  def GetNodeList(self):
1436 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1437 f78ede4e Guido Trotter

1438 f78ede4e Guido Trotter
    """
1439 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1440 f78ede4e Guido Trotter
1441 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1442 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1443 94a02bb5 Iustin Pop

1444 94a02bb5 Iustin Pop
    """
1445 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1446 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1447 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1448 94a02bb5 Iustin Pop
1449 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1450 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1451 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1452 6819dc49 Iustin Pop

1453 6819dc49 Iustin Pop
    """
1454 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1455 6819dc49 Iustin Pop
1456 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1457 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1458 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1459 075b62ca Iustin Pop

1460 075b62ca Iustin Pop
    """
1461 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1462 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1463 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1464 075b62ca Iustin Pop
1465 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1466 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1467 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1468 8bf9e9a5 Iustin Pop

1469 8bf9e9a5 Iustin Pop
    """
1470 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1471 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1472 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1473 8bf9e9a5 Iustin Pop
1474 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1475 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1476 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1477 f5eaa3c1 Iustin Pop

1478 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1479 f5eaa3c1 Iustin Pop
    @rtype: list
1480 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1481 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1482 f5eaa3c1 Iustin Pop
        order
1483 f5eaa3c1 Iustin Pop

1484 f5eaa3c1 Iustin Pop
    """
1485 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1486 f5eaa3c1 Iustin Pop
1487 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1488 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1489 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1490 d65e5776 Iustin Pop

1491 d65e5776 Iustin Pop
    @rtype: dict
1492 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1493 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1494 d65e5776 Iustin Pop

1495 d65e5776 Iustin Pop
    """
1496 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1497 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1498 d65e5776 Iustin Pop
    return my_dict
1499 d65e5776 Iustin Pop
1500 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1501 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1502 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1503 9d5b1371 Michael Hanselmann

1504 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1505 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1506 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1507 9d5b1371 Michael Hanselmann

1508 9d5b1371 Michael Hanselmann
    """
1509 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1510 9d5b1371 Michael Hanselmann
1511 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1512 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1513 ec0292f1 Iustin Pop

1514 23f06b2b Iustin Pop
    @type exceptions: list
1515 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1516 ec0292f1 Iustin Pop
    @rtype: tuple
1517 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1518 ec0292f1 Iustin Pop

1519 ec0292f1 Iustin Pop
    """
1520 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1521 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1522 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1523 23f06b2b Iustin Pop
        continue
1524 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1525 ec0292f1 Iustin Pop
        mc_max += 1
1526 ec0292f1 Iustin Pop
      if node.master_candidate:
1527 ec0292f1 Iustin Pop
        mc_now += 1
1528 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1529 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1530 ec0292f1 Iustin Pop
1531 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1532 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1533 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1534 ec0292f1 Iustin Pop

1535 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1536 ec0292f1 Iustin Pop

1537 23f06b2b Iustin Pop
    @type exceptions: list
1538 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1539 ec0292f1 Iustin Pop
    @rtype: tuple
1540 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1541 ec0292f1 Iustin Pop

1542 ec0292f1 Iustin Pop
    """
1543 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1544 ec0292f1 Iustin Pop
1545 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1546 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1547 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1548 ec0292f1 Iustin Pop

1549 44485f49 Guido Trotter
    @type exceptions: list
1550 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1551 ec0292f1 Iustin Pop
    @rtype: list
1552 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1553 ec0292f1 Iustin Pop

1554 ec0292f1 Iustin Pop
    """
1555 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1556 ec0292f1 Iustin Pop
    mod_list = []
1557 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1558 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1559 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1560 ec0292f1 Iustin Pop
      for name in node_list:
1561 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1562 ec0292f1 Iustin Pop
          break
1563 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1564 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1565 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1566 ec0292f1 Iustin Pop
          continue
1567 ee513a66 Iustin Pop
        mod_list.append(node)
1568 ec0292f1 Iustin Pop
        node.master_candidate = True
1569 ec0292f1 Iustin Pop
        node.serial_no += 1
1570 ec0292f1 Iustin Pop
        mc_now += 1
1571 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1572 ec0292f1 Iustin Pop
        # this should not happen
1573 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1574 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1575 ec0292f1 Iustin Pop
      if mod_list:
1576 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1577 ec0292f1 Iustin Pop
        self._WriteConfig()
1578 ec0292f1 Iustin Pop
1579 ec0292f1 Iustin Pop
    return mod_list
1580 ec0292f1 Iustin Pop
1581 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1582 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1583 190e3cb6 Guido Trotter

1584 190e3cb6 Guido Trotter
    """
1585 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1586 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1587 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1588 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1589 190e3cb6 Guido Trotter
      # is not found anymore.
1590 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1591 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1592 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1593 190e3cb6 Guido Trotter
1594 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1595 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1596 190e3cb6 Guido Trotter

1597 190e3cb6 Guido Trotter
    """
1598 f936c153 Iustin Pop
    nodegroup = node.group
1599 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1600 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1601 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1602 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1603 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1604 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1605 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1606 190e3cb6 Guido Trotter
    else:
1607 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1608 190e3cb6 Guido Trotter
1609 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
1610 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
1611 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
1612 54c31fd3 Michael Hanselmann

1613 54c31fd3 Michael Hanselmann
    @type mods: list of tuples; (node name, new group UUID)
1614 1d4930b9 Michael Hanselmann
    @param mods: Node membership modifications
1615 54c31fd3 Michael Hanselmann

1616 54c31fd3 Michael Hanselmann
    """
1617 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
1618 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
1619 54c31fd3 Michael Hanselmann
1620 54c31fd3 Michael Hanselmann
    resmod = []
1621 54c31fd3 Michael Hanselmann
1622 54c31fd3 Michael Hanselmann
    # Try to resolve names/UUIDs first
1623 54c31fd3 Michael Hanselmann
    for (node_name, new_group_uuid) in mods:
1624 54c31fd3 Michael Hanselmann
      try:
1625 54c31fd3 Michael Hanselmann
        node = nodes[node_name]
1626 54c31fd3 Michael Hanselmann
      except KeyError:
1627 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find node '%s'" % node_name)
1628 54c31fd3 Michael Hanselmann
1629 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
1630 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
1631 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
1632 54c31fd3 Michael Hanselmann
                      node_name, node.group)
1633 54c31fd3 Michael Hanselmann
        continue
1634 54c31fd3 Michael Hanselmann
1635 54c31fd3 Michael Hanselmann
      # Try to find current group of node
1636 54c31fd3 Michael Hanselmann
      try:
1637 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
1638 54c31fd3 Michael Hanselmann
      except KeyError:
1639 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
1640 54c31fd3 Michael Hanselmann
                                        node.group)
1641 54c31fd3 Michael Hanselmann
1642 54c31fd3 Michael Hanselmann
      # Try to find new group for node
1643 54c31fd3 Michael Hanselmann
      try:
1644 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
1645 54c31fd3 Michael Hanselmann
      except KeyError:
1646 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
1647 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
1648 54c31fd3 Michael Hanselmann
1649 54c31fd3 Michael Hanselmann
      assert node.name in old_group.members, \
1650 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
1651 54c31fd3 Michael Hanselmann
         " old group '%s'" % (node.name, old_group.uuid))
1652 54c31fd3 Michael Hanselmann
      assert node.name not in new_group.members, \
1653 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
1654 54c31fd3 Michael Hanselmann
         " its new group '%s'" % (node.name, new_group.uuid))
1655 54c31fd3 Michael Hanselmann
1656 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
1657 54c31fd3 Michael Hanselmann
1658 54c31fd3 Michael Hanselmann
    # Apply changes
1659 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
1660 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
1661 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
1662 54c31fd3 Michael Hanselmann
1663 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
1664 54c31fd3 Michael Hanselmann
1665 54c31fd3 Michael Hanselmann
      # Update members of involved groups
1666 54c31fd3 Michael Hanselmann
      if node.name in old_group.members:
1667 54c31fd3 Michael Hanselmann
        old_group.members.remove(node.name)
1668 54c31fd3 Michael Hanselmann
      if node.name not in new_group.members:
1669 54c31fd3 Michael Hanselmann
        new_group.members.append(node.name)
1670 54c31fd3 Michael Hanselmann
1671 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
1672 54c31fd3 Michael Hanselmann
    now = time.time()
1673 54c31fd3 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable-msg=W0142
1674 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
1675 54c31fd3 Michael Hanselmann
      obj.mtime = now
1676 54c31fd3 Michael Hanselmann
1677 54c31fd3 Michael Hanselmann
    # Force ssconf update
1678 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1679 54c31fd3 Michael Hanselmann
1680 54c31fd3 Michael Hanselmann
    self._WriteConfig()
1681 54c31fd3 Michael Hanselmann
1682 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1683 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1684 a8083063 Iustin Pop

1685 a8083063 Iustin Pop
    """
1686 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1687 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1688 a8083063 Iustin Pop
1689 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1690 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1691 76d5d3a3 Iustin Pop

1692 76d5d3a3 Iustin Pop
    """
1693 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1694 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1695 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1696 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1697 76d5d3a3 Iustin Pop
1698 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1699 a8083063 Iustin Pop
    """Read the config data from disk.
1700 a8083063 Iustin Pop

1701 a8083063 Iustin Pop
    """
1702 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1703 13998ef2 Michael Hanselmann
1704 a8083063 Iustin Pop
    try:
1705 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1706 13998ef2 Michael Hanselmann
    except Exception, err:
1707 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1708 5b263ed7 Michael Hanselmann
1709 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1710 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1711 5b263ed7 Michael Hanselmann
1712 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1713 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1714 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1715 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1716 90d726a8 Iustin Pop
1717 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
1718 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
1719 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
1720 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
1721 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
1722 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
1723 eb180fe2 Iustin Pop
1724 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1725 90d726a8 Iustin Pop
    data.UpgradeConfig()
1726 90d726a8 Iustin Pop
1727 a8083063 Iustin Pop
    self._config_data = data
1728 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1729 0779e3aa Iustin Pop
    # ssconf update
1730 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1731 a8083063 Iustin Pop
1732 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1733 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1734 76d5d3a3 Iustin Pop
1735 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
1736 bd407597 Iustin Pop
1737 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1738 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1739 76d5d3a3 Iustin Pop

1740 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1741 76d5d3a3 Iustin Pop
    whole configuration, etc.
1742 76d5d3a3 Iustin Pop

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

1749 76d5d3a3 Iustin Pop
    """
1750 76d5d3a3 Iustin Pop
    modified = False
1751 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1752 76d5d3a3 Iustin Pop
      if item.uuid is None:
1753 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1754 76d5d3a3 Iustin Pop
        modified = True
1755 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
1756 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
1757 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
1758 75cf411a Adeodato Simo
                                            members=[])
1759 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
1760 f9e81396 Guido Trotter
      modified = True
1761 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
1762 f936c153 Iustin Pop
      if not node.group:
1763 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
1764 190e3cb6 Guido Trotter
        modified = True
1765 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
1766 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
1767 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
1768 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
1769 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
1770 76d5d3a3 Iustin Pop
    if modified:
1771 76d5d3a3 Iustin Pop
      self._WriteConfig()
1772 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1773 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1774 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1775 4fae38c5 Guido Trotter
1776 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1777 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1778 a8083063 Iustin Pop

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

1782 a8083063 Iustin Pop
    """
1783 a8083063 Iustin Pop
    if self._offline:
1784 a8083063 Iustin Pop
      return True
1785 a4eae71f Michael Hanselmann
1786 a8083063 Iustin Pop
    bad = False
1787 a8083063 Iustin Pop
1788 6a5b8b4b Iustin Pop
    node_list = []
1789 6a5b8b4b Iustin Pop
    addr_list = []
1790 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1791 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1792 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1793 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1794 6b294c53 Iustin Pop
    # in between
1795 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1796 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1797 6a5b8b4b Iustin Pop
        continue
1798 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1799 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1800 6a5b8b4b Iustin Pop
        continue
1801 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1802 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1803 6b294c53 Iustin Pop
1804 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1805 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1806 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1807 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1808 1b54fc6c Guido Trotter
      if msg:
1809 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1810 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1811 1b54fc6c Guido Trotter
        logging.error(msg)
1812 a4eae71f Michael Hanselmann
1813 a4eae71f Michael Hanselmann
        if feedback_fn:
1814 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1815 a4eae71f Michael Hanselmann
1816 a8083063 Iustin Pop
        bad = True
1817 a4eae71f Michael Hanselmann
1818 a8083063 Iustin Pop
    return not bad
1819 a8083063 Iustin Pop
1820 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1821 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1822 a8083063 Iustin Pop

1823 a8083063 Iustin Pop
    """
1824 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
1825 a4eae71f Michael Hanselmann
1826 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
1827 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
1828 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
1829 d2231b8c Iustin Pop
    # recovery to the user
1830 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1831 4a89c54a Iustin Pop
    if config_errors:
1832 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
1833 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
1834 d2231b8c Iustin Pop
      logging.critical(errmsg)
1835 d2231b8c Iustin Pop
      if feedback_fn:
1836 d2231b8c Iustin Pop
        feedback_fn(errmsg)
1837 d2231b8c Iustin Pop
1838 a8083063 Iustin Pop
    if destination is None:
1839 a8083063 Iustin Pop
      destination = self._cfg_file
1840 a8083063 Iustin Pop
    self._BumpSerialNo()
1841 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1842 13998ef2 Michael Hanselmann
1843 e60c73a1 René Nussbaumer
    getents = self._getents()
1844 bd407597 Iustin Pop
    try:
1845 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
1846 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
1847 bd407597 Iustin Pop
    except errors.LockError:
1848 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
1849 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
1850 bd407597 Iustin Pop
                                      " update")
1851 bd407597 Iustin Pop
    try:
1852 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
1853 bd407597 Iustin Pop
    finally:
1854 bd407597 Iustin Pop
      os.close(fd)
1855 13998ef2 Michael Hanselmann
1856 14e15659 Iustin Pop
    self.write_count += 1
1857 3d3a04bc Iustin Pop
1858 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
1859 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
1860 a8083063 Iustin Pop
1861 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
1862 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
1863 d9a855f1 Michael Hanselmann
      if not self._offline:
1864 cd34faf2 Michael Hanselmann
        result = rpc.RpcRunner.call_write_ssconf_files(
1865 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
1866 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
1867 a4eae71f Michael Hanselmann
1868 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
1869 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
1870 e1e75d00 Iustin Pop
          if msg:
1871 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
1872 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
1873 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
1874 a4eae71f Michael Hanselmann
1875 a4eae71f Michael Hanselmann
            if feedback_fn:
1876 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
1877 a4eae71f Michael Hanselmann
1878 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
1879 54d1a06e Michael Hanselmann
1880 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
1881 054596f0 Iustin Pop
    """Return the values needed by ssconf.
1882 054596f0 Iustin Pop

1883 054596f0 Iustin Pop
    @rtype: dict
1884 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1885 054596f0 Iustin Pop
        associated value
1886 054596f0 Iustin Pop

1887 054596f0 Iustin Pop
    """
1888 a3316e4a Iustin Pop
    fn = "\n".join
1889 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1890 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1891 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1892 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1893 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1894 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1895 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1896 a3316e4a Iustin Pop
1897 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1898 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1899 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1900 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1901 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1902 8113a52e Luca Bigliardi
                     if node.master_candidate)
1903 a3316e4a Iustin Pop
    node_data = fn(node_names)
1904 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1905 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1906 f56618e0 Iustin Pop
1907 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1908 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1909 4f7a6a10 Iustin Pop
1910 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
1911 4f7a6a10 Iustin Pop
1912 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
1913 0fbae49a Balazs Lecz
1914 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
1915 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
1916 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
1917 6f076453 Guido Trotter
1918 2afc9238 Iustin Pop
    ssconf_values = {
1919 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1920 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1921 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1922 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
1923 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1924 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1925 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1926 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1927 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1928 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1929 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1930 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1931 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1932 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1933 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
1934 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1935 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1936 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
1937 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
1938 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
1939 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
1940 03d1dba2 Michael Hanselmann
      }
1941 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
1942 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
1943 2afc9238 Iustin Pop
    if bad_values:
1944 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
1945 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
1946 2afc9238 Iustin Pop
                                      " values: %s" % err)
1947 2afc9238 Iustin Pop
    return ssconf_values
1948 03d1dba2 Michael Hanselmann
1949 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1950 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
1951 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
1952 d367b66c Manuel Franceschini

1953 d367b66c Manuel Franceschini
    """
1954 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
1955 d367b66c Manuel Franceschini
1956 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1957 a8083063 Iustin Pop
  def GetVGName(self):
1958 a8083063 Iustin Pop
    """Return the volume group name.
1959 a8083063 Iustin Pop

1960 a8083063 Iustin Pop
    """
1961 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1962 a8083063 Iustin Pop
1963 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1964 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1965 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1966 89ff8e15 Manuel Franceschini

1967 89ff8e15 Manuel Franceschini
    """
1968 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1969 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1970 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1971 89ff8e15 Manuel Franceschini
1972 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1973 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
1974 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
1975 9e33896b Luca Bigliardi

1976 9e33896b Luca Bigliardi
    """
1977 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
1978 9e33896b Luca Bigliardi
1979 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
1980 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
1981 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
1982 9e33896b Luca Bigliardi

1983 9e33896b Luca Bigliardi
    """
1984 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
1985 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
1986 9e33896b Luca Bigliardi
    self._WriteConfig()
1987 9e33896b Luca Bigliardi
1988 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1989 a8083063 Iustin Pop
  def GetMACPrefix(self):
1990 a8083063 Iustin Pop
    """Return the mac prefix.
1991 a8083063 Iustin Pop

1992 a8083063 Iustin Pop
    """
1993 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1994 62779dd0 Iustin Pop
1995 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1996 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1997 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1998 62779dd0 Iustin Pop

1999 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2000 c41eea6e Iustin Pop
    @return: the cluster object
2001 62779dd0 Iustin Pop

2002 62779dd0 Iustin Pop
    """
2003 62779dd0 Iustin Pop
    return self._config_data.cluster
2004 e00fb268 Iustin Pop
2005 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2006 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2007 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2008 51cb1581 Luca Bigliardi

2009 51cb1581 Luca Bigliardi
    """
2010 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2011 51cb1581 Luca Bigliardi
2012 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2013 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
2014 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2015 e00fb268 Iustin Pop

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

2022 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2023 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2024 c41eea6e Iustin Pop
        the cluster
2025 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2026 c41eea6e Iustin Pop

2027 e00fb268 Iustin Pop
    """
2028 e00fb268 Iustin Pop
    if self._config_data is None:
2029 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2030 3ecf6786 Iustin Pop
                                   " cannot save.")
2031 f34901f8 Iustin Pop
    update_serial = False
2032 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2033 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2034 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2035 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2036 f34901f8 Iustin Pop
      update_serial = True
2037 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2038 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2039 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2040 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2041 e00fb268 Iustin Pop
    else:
2042 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2043 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2044 e00fb268 Iustin Pop
    if not test:
2045 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2046 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2047 f34901f8 Iustin Pop
    target.serial_no += 1
2048 d693c864 Iustin Pop
    target.mtime = now = time.time()
2049 f34901f8 Iustin Pop
2050 cff4c037 Iustin Pop
    if update_serial:
2051 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2052 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2053 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2054 b989e85d Iustin Pop
2055 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2056 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
2057 61cf6b5e Iustin Pop
2058 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2059 73064714 Guido Trotter
2060 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2061 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2062 73064714 Guido Trotter
    """Drop per-execution-context reservations
2063 73064714 Guido Trotter

2064 73064714 Guido Trotter
    """
2065 d8aee57e Iustin Pop
    for rm in self._all_rms:
2066 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)