Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 69f0340a

History | View | Annotate | Download (68 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 075b62ca Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 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 a8083063 Iustin Pop
42 a8083063 Iustin Pop
from ganeti import errors
43 f78ede4e Guido Trotter
from ganeti import locking
44 a8083063 Iustin Pop
from ganeti import utils
45 a8083063 Iustin Pop
from ganeti import constants
46 a8083063 Iustin Pop
from ganeti import rpc
47 a8083063 Iustin Pop
from ganeti import objects
48 8d14b30d Iustin Pop
from ganeti import serializer
49 0fbae49a Balazs Lecz
from ganeti import uidpool
50 a744b676 Manuel Franceschini
from ganeti import netutils
51 e60c73a1 René Nussbaumer
from ganeti import runtime
52 243cdbcc Michael Hanselmann
53 243cdbcc Michael Hanselmann
54 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
55 f78ede4e Guido Trotter
56 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
57 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
58 4fae38c5 Guido Trotter
59 f78ede4e Guido Trotter
60 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
61 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
62 c41eea6e Iustin Pop

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

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

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

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

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

111 013da361 Guido Trotter
    """
112 013da361 Guido Trotter
    assert callable(generate_one_fn)
113 013da361 Guido Trotter
114 013da361 Guido Trotter
    all_elems = self.GetReserved()
115 013da361 Guido Trotter
    all_elems.update(existing)
116 013da361 Guido Trotter
    retries = 64
117 013da361 Guido Trotter
    while retries > 0:
118 013da361 Guido Trotter
      new_resource = generate_one_fn()
119 013da361 Guido Trotter
      if new_resource is not None and new_resource not in all_elems:
120 013da361 Guido Trotter
        break
121 013da361 Guido Trotter
    else:
122 013da361 Guido Trotter
      raise errors.ConfigurationError("Not able generate new resource"
123 013da361 Guido Trotter
                                      " (last tried: %s)" % new_resource)
124 013da361 Guido Trotter
    self.Reserve(ec_id, new_resource)
125 013da361 Guido Trotter
    return new_resource
126 013da361 Guido Trotter
127 013da361 Guido Trotter
128 fe698b38 Michael Hanselmann
def _MatchNameComponentIgnoreCase(short_name, names):
129 3a93eebb Michael Hanselmann
  """Wrapper around L{utils.text.MatchNameComponent}.
130 fe698b38 Michael Hanselmann

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

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

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

173 b2acdbdc Michael Hanselmann
    """
174 b2acdbdc Michael Hanselmann
    return rpc.ConfigRunner(self._context, address_list)
175 b2acdbdc Michael Hanselmann
176 b2acdbdc Michael Hanselmann
  def SetContext(self, context):
177 b2acdbdc Michael Hanselmann
    """Sets Ganeti context.
178 b2acdbdc Michael Hanselmann

179 b2acdbdc Michael Hanselmann
    """
180 b2acdbdc Michael Hanselmann
    self._context = context
181 b2acdbdc Michael Hanselmann
182 a8083063 Iustin Pop
  # this method needs to be static, so that we can call it on the class
183 a8083063 Iustin Pop
  @staticmethod
184 a8083063 Iustin Pop
  def IsCluster():
185 a8083063 Iustin Pop
    """Check if the cluster is configured.
186 a8083063 Iustin Pop

187 a8083063 Iustin Pop
    """
188 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
189 a8083063 Iustin Pop
190 36b66e6e Guido Trotter
  def _GenerateOneMAC(self):
191 36b66e6e Guido Trotter
    """Generate one mac address
192 36b66e6e Guido Trotter

193 36b66e6e Guido Trotter
    """
194 36b66e6e Guido Trotter
    prefix = self._config_data.cluster.mac_prefix
195 36b66e6e Guido Trotter
    byte1 = random.randrange(0, 256)
196 36b66e6e Guido Trotter
    byte2 = random.randrange(0, 256)
197 36b66e6e Guido Trotter
    byte3 = random.randrange(0, 256)
198 36b66e6e Guido Trotter
    mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
199 36b66e6e Guido Trotter
    return mac
200 36b66e6e Guido Trotter
201 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
202 5768e6a6 René Nussbaumer
  def GetNdParams(self, node):
203 5768e6a6 René Nussbaumer
    """Get the node params populated with cluster defaults.
204 5768e6a6 René Nussbaumer

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

209 5768e6a6 René Nussbaumer
    """
210 5768e6a6 René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
211 5768e6a6 René Nussbaumer
    return self._config_data.cluster.FillND(node, nodegroup)
212 5768e6a6 René Nussbaumer
213 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
214 36b66e6e Guido Trotter
  def GenerateMAC(self, ec_id):
215 a8083063 Iustin Pop
    """Generate a MAC for an instance.
216 a8083063 Iustin Pop

217 a8083063 Iustin Pop
    This should check the current instances for duplicates.
218 a8083063 Iustin Pop

219 a8083063 Iustin Pop
    """
220 36b66e6e Guido Trotter
    existing = self._AllMACs()
221 36b66e6e Guido Trotter
    return self._temporary_ids.Generate(existing, self._GenerateOneMAC, ec_id)
222 a8083063 Iustin Pop
223 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
224 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
225 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
226 1862d460 Alexander Schreiber

227 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
228 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
229 1862d460 Alexander Schreiber

230 1862d460 Alexander Schreiber
    """
231 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
232 36b66e6e Guido Trotter
    if mac in all_macs:
233 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
234 36b66e6e Guido Trotter
    else:
235 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
236 1862d460 Alexander Schreiber
237 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
238 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
239 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
240 d8aee57e Iustin Pop

241 d8aee57e Iustin Pop
    @type lv_name: string
242 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
243 d8aee57e Iustin Pop

244 d8aee57e Iustin Pop
    """
245 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
246 d8aee57e Iustin Pop
    if lv_name in all_lvs:
247 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
248 d8aee57e Iustin Pop
    else:
249 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
250 d8aee57e Iustin Pop
251 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
252 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
253 f9518d38 Iustin Pop
    """Generate a DRBD secret.
254 f9518d38 Iustin Pop

255 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
256 f9518d38 Iustin Pop

257 f9518d38 Iustin Pop
    """
258 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
259 afa1386e Guido Trotter
                                            utils.GenerateSecret,
260 afa1386e Guido Trotter
                                            ec_id)
261 8d9c3bef Michael Hanselmann
262 34e54ebc Iustin Pop
  def _AllLVs(self):
263 923b1523 Iustin Pop
    """Compute the list of all LVs.
264 923b1523 Iustin Pop

265 923b1523 Iustin Pop
    """
266 923b1523 Iustin Pop
    lvnames = set()
267 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
268 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
269 923b1523 Iustin Pop
      for lv_list in node_data.values():
270 923b1523 Iustin Pop
        lvnames.update(lv_list)
271 923b1523 Iustin Pop
    return lvnames
272 923b1523 Iustin Pop
273 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
274 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
275 34e54ebc Iustin Pop

276 34e54ebc Iustin Pop
    @type include_temporary: boolean
277 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
278 34e54ebc Iustin Pop
    @rtype: set
279 34e54ebc Iustin Pop
    @return: a set of IDs
280 34e54ebc Iustin Pop

281 34e54ebc Iustin Pop
    """
282 34e54ebc Iustin Pop
    existing = set()
283 34e54ebc Iustin Pop
    if include_temporary:
284 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
285 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
286 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
287 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
288 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
289 34e54ebc Iustin Pop
    return existing
290 34e54ebc Iustin Pop
291 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
292 430b923c Iustin Pop
    """Generate an unique UUID.
293 923b1523 Iustin Pop

294 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
295 923b1523 Iustin Pop
    duplicates.
296 923b1523 Iustin Pop

297 c41eea6e Iustin Pop
    @rtype: string
298 c41eea6e Iustin Pop
    @return: the unique id
299 923b1523 Iustin Pop

300 923b1523 Iustin Pop
    """
301 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
302 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
303 923b1523 Iustin Pop
304 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
305 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
306 430b923c Iustin Pop
    """Generate an unique ID.
307 430b923c Iustin Pop

308 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
309 430b923c Iustin Pop

310 4fae38c5 Guido Trotter
    @type ec_id: string
311 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
312 34d657ba Iustin Pop

313 34d657ba Iustin Pop
    """
314 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
315 34d657ba Iustin Pop
316 a8083063 Iustin Pop
  def _AllMACs(self):
317 a8083063 Iustin Pop
    """Return all MACs present in the config.
318 a8083063 Iustin Pop

319 c41eea6e Iustin Pop
    @rtype: list
320 c41eea6e Iustin Pop
    @return: the list of all MACs
321 c41eea6e Iustin Pop

322 a8083063 Iustin Pop
    """
323 a8083063 Iustin Pop
    result = []
324 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
325 a8083063 Iustin Pop
      for nic in instance.nics:
326 a8083063 Iustin Pop
        result.append(nic.mac)
327 a8083063 Iustin Pop
328 a8083063 Iustin Pop
    return result
329 a8083063 Iustin Pop
330 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
331 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
332 f9518d38 Iustin Pop

333 c41eea6e Iustin Pop
    @rtype: list
334 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
335 c41eea6e Iustin Pop

336 f9518d38 Iustin Pop
    """
337 f9518d38 Iustin Pop
    def helper(disk, result):
338 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
339 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
340 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
341 f9518d38 Iustin Pop
      if disk.children:
342 f9518d38 Iustin Pop
        for child in disk.children:
343 f9518d38 Iustin Pop
          helper(child, result)
344 f9518d38 Iustin Pop
345 f9518d38 Iustin Pop
    result = []
346 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
347 f9518d38 Iustin Pop
      for disk in instance.disks:
348 f9518d38 Iustin Pop
        helper(disk, result)
349 f9518d38 Iustin Pop
350 f9518d38 Iustin Pop
    return result
351 f9518d38 Iustin Pop
352 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
353 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
354 4b98ac29 Iustin Pop

355 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
356 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
357 4b98ac29 Iustin Pop
    @type l_ids: list
358 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
359 4b98ac29 Iustin Pop
    @type p_ids: list
360 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
361 4b98ac29 Iustin Pop
    @rtype: list
362 4b98ac29 Iustin Pop
    @return: a list of error messages
363 4b98ac29 Iustin Pop

364 4b98ac29 Iustin Pop
    """
365 4b98ac29 Iustin Pop
    result = []
366 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
367 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
368 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
369 25ae22e4 Iustin Pop
      else:
370 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
371 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
372 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
373 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
374 25ae22e4 Iustin Pop
      else:
375 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
376 4b98ac29 Iustin Pop
377 4b98ac29 Iustin Pop
    if disk.children:
378 4b98ac29 Iustin Pop
      for child in disk.children:
379 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
380 4b98ac29 Iustin Pop
    return result
381 4b98ac29 Iustin Pop
382 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
383 a8efbb40 Iustin Pop
    """Verify function.
384 a8efbb40 Iustin Pop

385 4a89c54a Iustin Pop
    @rtype: list
386 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
387 4a89c54a Iustin Pop
        configuration errors
388 4a89c54a Iustin Pop

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

605 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
606 4a89c54a Iustin Pop

607 4a89c54a Iustin Pop
    @rtype: list
608 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
609 4a89c54a Iustin Pop
        configuration errors
610 4a89c54a Iustin Pop

611 4a89c54a Iustin Pop
    """
612 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
613 4a89c54a Iustin Pop
614 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
615 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
616 a8083063 Iustin Pop

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

619 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
620 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
621 a8083063 Iustin Pop
    node.
622 a8083063 Iustin Pop

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

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

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

658 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
659 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
660 f78ede4e Guido Trotter
    node.
661 f78ede4e Guido Trotter

662 f78ede4e Guido Trotter
    """
663 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
664 f78ede4e Guido Trotter
665 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
666 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
667 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
668 b2fddf63 Iustin Pop

669 b2fddf63 Iustin Pop
    """
670 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
671 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
672 264bb3c5 Michael Hanselmann
673 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
674 264bb3c5 Michael Hanselmann
    self._WriteConfig()
675 264bb3c5 Michael Hanselmann
676 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
677 b2fddf63 Iustin Pop
  def GetPortList(self):
678 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
679 264bb3c5 Michael Hanselmann

680 264bb3c5 Michael Hanselmann
    """
681 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
682 264bb3c5 Michael Hanselmann
683 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
684 a8083063 Iustin Pop
  def AllocatePort(self):
685 a8083063 Iustin Pop
    """Allocate a port.
686 a8083063 Iustin Pop

687 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
688 b2fddf63 Iustin Pop
    default port range (and in this case we increase
689 b2fddf63 Iustin Pop
    highest_used_port).
690 a8083063 Iustin Pop

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

709 4a89c54a Iustin Pop
    @rtype: (dict, list)
710 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
711 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
712 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
713 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
714 4a89c54a Iustin Pop
        should raise an exception
715 a81c53c9 Iustin Pop

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

749 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
750 6d2e83d5 Iustin Pop

751 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
752 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
753 6d2e83d5 Iustin Pop
        an empty list).
754 6d2e83d5 Iustin Pop

755 6d2e83d5 Iustin Pop
    """
756 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
757 4a89c54a Iustin Pop
    if duplicates:
758 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
759 4a89c54a Iustin Pop
                                      str(duplicates))
760 4a89c54a Iustin Pop
    return d_map
761 6d2e83d5 Iustin Pop
762 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
763 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
764 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
765 a81c53c9 Iustin Pop

766 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
767 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
768 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
769 a81c53c9 Iustin Pop
    order as the passed nodes.
770 a81c53c9 Iustin Pop

771 32388e6d Iustin Pop
    @type instance: string
772 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
773 32388e6d Iustin Pop

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

821 a81c53c9 Iustin Pop
    @type instance: string
822 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
823 a81c53c9 Iustin Pop
                     released
824 a81c53c9 Iustin Pop

825 a81c53c9 Iustin Pop
    """
826 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
827 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
828 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
829 a81c53c9 Iustin Pop
      if name == instance:
830 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
831 a81c53c9 Iustin Pop
832 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
833 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
834 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
835 61cf6b5e Iustin Pop

836 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
837 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
838 61cf6b5e Iustin Pop
    functions.
839 61cf6b5e Iustin Pop

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

842 61cf6b5e Iustin Pop
    @type instance: string
843 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
844 61cf6b5e Iustin Pop
                     released
845 61cf6b5e Iustin Pop

846 61cf6b5e Iustin Pop
    """
847 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
848 61cf6b5e Iustin Pop
849 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
850 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
851 4a8b186a Michael Hanselmann
    """Get the configuration version.
852 4a8b186a Michael Hanselmann

853 4a8b186a Michael Hanselmann
    @return: Config version
854 4a8b186a Michael Hanselmann

855 4a8b186a Michael Hanselmann
    """
856 4a8b186a Michael Hanselmann
    return self._config_data.version
857 4a8b186a Michael Hanselmann
858 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
859 4a8b186a Michael Hanselmann
  def GetClusterName(self):
860 4a8b186a Michael Hanselmann
    """Get cluster name.
861 4a8b186a Michael Hanselmann

862 4a8b186a Michael Hanselmann
    @return: Cluster name
863 4a8b186a Michael Hanselmann

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

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

873 4a8b186a Michael Hanselmann
    """
874 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
875 4a8b186a Michael Hanselmann
876 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
877 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
878 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
879 4a8b186a Michael Hanselmann

880 4a8b186a Michael Hanselmann
    @return: Master IP
881 4a8b186a Michael Hanselmann

882 4a8b186a Michael Hanselmann
    """
883 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
884 4a8b186a Michael Hanselmann
885 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
886 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
887 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
888 4a8b186a Michael Hanselmann

889 4a8b186a Michael Hanselmann
    """
890 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
891 4a8b186a Michael Hanselmann
892 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
893 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
894 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
895 5a8648eb Andrea Spadaccini

896 5a8648eb Andrea Spadaccini
    """
897 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
898 5a8648eb Andrea Spadaccini
899 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
900 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
901 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
902 33be7576 Andrea Spadaccini

903 33be7576 Andrea Spadaccini
    """
904 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
905 33be7576 Andrea Spadaccini
906 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
907 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
908 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
909 4a8b186a Michael Hanselmann

910 4a8b186a Michael Hanselmann
    """
911 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
912 4a8b186a Michael Hanselmann
913 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
914 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
915 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
916 4b97f902 Apollon Oikonomopoulos

917 4b97f902 Apollon Oikonomopoulos
    """
918 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
919 4b97f902 Apollon Oikonomopoulos
920 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
921 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
922 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
923 4a8b186a Michael Hanselmann

924 4a8b186a Michael Hanselmann
    """
925 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
926 4a8b186a Michael Hanselmann
927 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
928 a8083063 Iustin Pop
  def GetHostKey(self):
929 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
930 a8083063 Iustin Pop

931 c41eea6e Iustin Pop
    @rtype: string
932 c41eea6e Iustin Pop
    @return: the rsa hostkey
933 a8083063 Iustin Pop

934 a8083063 Iustin Pop
    """
935 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
936 a8083063 Iustin Pop
937 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
938 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
939 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
940 bf4af505 Apollon Oikonomopoulos

941 bf4af505 Apollon Oikonomopoulos
    """
942 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
943 bf4af505 Apollon Oikonomopoulos
944 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
945 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
946 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
947 868a98ca Manuel Franceschini

948 868a98ca Manuel Franceschini
    @return: primary ip family
949 868a98ca Manuel Franceschini

950 868a98ca Manuel Franceschini
    """
951 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
952 868a98ca Manuel Franceschini
953 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
954 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
955 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
956 c9f4b8e6 Andrea Spadaccini

957 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
958 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
959 c9f4b8e6 Andrea Spadaccini

960 c9f4b8e6 Andrea Spadaccini
    """
961 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
962 c79198a0 Andrea Spadaccini
    result = objects.MasterNetworkParameters(name=cluster.master_node,
963 c79198a0 Andrea Spadaccini
      ip=cluster.master_ip,
964 c79198a0 Andrea Spadaccini
      netmask=cluster.master_netmask,
965 c79198a0 Andrea Spadaccini
      netdev=cluster.master_netdev,
966 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
967 c9f4b8e6 Andrea Spadaccini
968 f9d20654 Andrea Spadaccini
    return result
969 f9d20654 Andrea Spadaccini
970 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
971 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
972 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
973 e11a1b77 Adeodato Simo

974 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
975 90e99856 Adeodato Simo
    according to their default values.
976 90e99856 Adeodato Simo

977 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
978 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
979 e11a1b77 Adeodato Simo
    @type ec_id: string
980 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
981 e11a1b77 Adeodato Simo
    @type check_uuid: bool
982 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
983 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
984 e11a1b77 Adeodato Simo
                       configuration already
985 e11a1b77 Adeodato Simo

986 e11a1b77 Adeodato Simo
    """
987 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
988 e11a1b77 Adeodato Simo
    self._WriteConfig()
989 e11a1b77 Adeodato Simo
990 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
991 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
992 e11a1b77 Adeodato Simo

993 e11a1b77 Adeodato Simo
    """
994 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
995 e11a1b77 Adeodato Simo
996 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
997 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
998 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
999 e11a1b77 Adeodato Simo
    if check_uuid:
1000 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
1001 e11a1b77 Adeodato Simo
1002 18ffc0fe Stephen Shirley
    try:
1003 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
1004 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
1005 18ffc0fe Stephen Shirley
      pass
1006 18ffc0fe Stephen Shirley
    else:
1007 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
1008 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
1009 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
1010 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
1011 18ffc0fe Stephen Shirley
1012 e11a1b77 Adeodato Simo
    group.serial_no = 1
1013 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
1014 90e99856 Adeodato Simo
    group.UpgradeConfig()
1015 e11a1b77 Adeodato Simo
1016 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
1017 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1018 e11a1b77 Adeodato Simo
1019 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1020 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
1021 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
1022 e11a1b77 Adeodato Simo

1023 e11a1b77 Adeodato Simo
    @type group_uuid: string
1024 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1025 e11a1b77 Adeodato Simo

1026 e11a1b77 Adeodato Simo
    """
1027 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1028 e11a1b77 Adeodato Simo
1029 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1030 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1031 e11a1b77 Adeodato Simo
1032 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1033 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1034 0389c42a Stephen Shirley
1035 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1036 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1037 e11a1b77 Adeodato Simo
    self._WriteConfig()
1038 e11a1b77 Adeodato Simo
1039 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1040 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1041 eaa98a04 Guido Trotter

1042 eaa98a04 Guido Trotter
    @type target: string or None
1043 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1044 eaa98a04 Guido Trotter
    @rtype: string
1045 412b3531 Guido Trotter
    @return: nodegroup UUID
1046 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1047 eaa98a04 Guido Trotter

1048 eaa98a04 Guido Trotter
    """
1049 eaa98a04 Guido Trotter
    if target is None:
1050 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1051 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1052 eaa98a04 Guido Trotter
                                   " group must be specified explicitely.")
1053 eaa98a04 Guido Trotter
      else:
1054 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1055 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1056 eaa98a04 Guido Trotter
      return target
1057 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1058 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1059 eaa98a04 Guido Trotter
        return nodegroup.uuid
1060 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1061 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1062 eaa98a04 Guido Trotter
1063 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1064 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1065 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1066 e85d8982 Stephen Shirley

1067 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1068 e85d8982 Stephen Shirley

1069 e85d8982 Stephen Shirley
    @type target: string or None
1070 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1071 e85d8982 Stephen Shirley
    @rtype: string
1072 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1073 e85d8982 Stephen Shirley

1074 e85d8982 Stephen Shirley
    """
1075 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1076 e85d8982 Stephen Shirley
1077 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1078 648e4196 Guido Trotter
    """Lookup a node group.
1079 648e4196 Guido Trotter

1080 648e4196 Guido Trotter
    @type uuid: string
1081 648e4196 Guido Trotter
    @param uuid: group UUID
1082 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1083 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1084 648e4196 Guido Trotter

1085 648e4196 Guido Trotter
    """
1086 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1087 648e4196 Guido Trotter
      return None
1088 648e4196 Guido Trotter
1089 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1090 648e4196 Guido Trotter
1091 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1092 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1093 5768e6a6 René Nussbaumer
    """Lookup a node group.
1094 5768e6a6 René Nussbaumer

1095 5768e6a6 René Nussbaumer
    @type uuid: string
1096 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1097 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1098 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1099 5768e6a6 René Nussbaumer

1100 5768e6a6 René Nussbaumer
    """
1101 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1102 5768e6a6 René Nussbaumer
1103 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1104 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1105 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1106 622444e5 Iustin Pop

1107 622444e5 Iustin Pop
    """
1108 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1109 622444e5 Iustin Pop
1110 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1111 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1112 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1113 1ac6f2ad Guido Trotter

1114 1ac6f2ad Guido Trotter
    """
1115 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1116 1ac6f2ad Guido Trotter
1117 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1118 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1119 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1120 dac81741 Michael Hanselmann

1121 dac81741 Michael Hanselmann
    """
1122 dac81741 Michael Hanselmann
    ngfn = lambda node_name: self._UnlockedGetNodeInfo(node_name).group
1123 dac81741 Michael Hanselmann
    return frozenset(member_name
1124 dac81741 Michael Hanselmann
                     for node_name in nodes
1125 dac81741 Michael Hanselmann
                     for member_name in
1126 dac81741 Michael Hanselmann
                       self._UnlockedGetNodeGroup(ngfn(node_name)).members)
1127 dac81741 Michael Hanselmann
1128 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1129 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1130 a8083063 Iustin Pop
    """Add an instance to the config.
1131 a8083063 Iustin Pop

1132 a8083063 Iustin Pop
    This should be used after creating a new instance.
1133 a8083063 Iustin Pop

1134 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1135 c41eea6e Iustin Pop
    @param instance: the instance object
1136 c41eea6e Iustin Pop

1137 a8083063 Iustin Pop
    """
1138 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1139 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1140 a8083063 Iustin Pop
1141 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1142 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1143 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1144 923b1523 Iustin Pop
1145 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1146 e4640214 Guido Trotter
    for nic in instance.nics:
1147 e4640214 Guido Trotter
      if nic.mac in all_macs:
1148 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1149 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1150 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1151 430b923c Iustin Pop
1152 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1153 e4640214 Guido Trotter
1154 b989e85d Iustin Pop
    instance.serial_no = 1
1155 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1156 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1157 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1158 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1159 a8083063 Iustin Pop
    self._WriteConfig()
1160 a8083063 Iustin Pop
1161 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1162 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1163 430b923c Iustin Pop

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

1167 430b923c Iustin Pop
    """
1168 430b923c Iustin Pop
    if not item.uuid:
1169 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1170 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1171 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1172 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1173 430b923c Iustin Pop
1174 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1175 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1176 a8083063 Iustin Pop

1177 a8083063 Iustin Pop
    """
1178 2e04d454 Agata Murawska
    assert status in constants.ADMINST_ALL, \
1179 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1180 a8083063 Iustin Pop
1181 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1182 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1183 3ecf6786 Iustin Pop
                                      instance_name)
1184 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1185 9ca8a7c5 Agata Murawska
    if instance.admin_state != status:
1186 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1187 b989e85d Iustin Pop
      instance.serial_no += 1
1188 d693c864 Iustin Pop
      instance.mtime = time.time()
1189 455a3445 Iustin Pop
      self._WriteConfig()
1190 a8083063 Iustin Pop
1191 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1192 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1193 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1194 6a408fb2 Iustin Pop

1195 6a408fb2 Iustin Pop
    """
1196 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_UP)
1197 57de31c0 Agata Murawska
1198 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1199 57de31c0 Agata Murawska
  def MarkInstanceOffline(self, instance_name):
1200 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1201 57de31c0 Agata Murawska

1202 57de31c0 Agata Murawska
    """
1203 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_OFFLINE)
1204 6a408fb2 Iustin Pop
1205 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1206 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1207 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1208 a8083063 Iustin Pop

1209 a8083063 Iustin Pop
    """
1210 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1211 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1212 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1213 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1214 a8083063 Iustin Pop
    self._WriteConfig()
1215 a8083063 Iustin Pop
1216 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1217 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1218 fc95f88f Iustin Pop
    """Rename an instance.
1219 fc95f88f Iustin Pop

1220 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1221 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1222 fc95f88f Iustin Pop
    rename.
1223 fc95f88f Iustin Pop

1224 fc95f88f Iustin Pop
    """
1225 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
1226 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
1227 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
1228 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
1229 fc95f88f Iustin Pop
    inst.name = new_name
1230 b23c4333 Manuel Franceschini
1231 b23c4333 Manuel Franceschini
    for disk in inst.disks:
1232 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1233 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1234 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1235 3721d2fe Guido Trotter
        disk_fname = "disk%s" % disk.iv_name.split("/")[1]
1236 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
1237 c4feafe8 Iustin Pop
                                              utils.PathJoin(file_storage_dir,
1238 c4feafe8 Iustin Pop
                                                             inst.name,
1239 3721d2fe Guido Trotter
                                                             disk_fname))
1240 b23c4333 Manuel Franceschini
1241 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1242 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1243 1fc34c48 Michael Hanselmann
1244 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
1245 fc95f88f Iustin Pop
    self._WriteConfig()
1246 fc95f88f Iustin Pop
1247 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1248 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
1249 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1250 a8083063 Iustin Pop

1251 a8083063 Iustin Pop
    """
1252 2e04d454 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_DOWN)
1253 a8083063 Iustin Pop
1254 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1255 94bbfece Iustin Pop
    """Get the list of instances.
1256 94bbfece Iustin Pop

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

1259 94bbfece Iustin Pop
    """
1260 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1261 94bbfece Iustin Pop
1262 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1263 a8083063 Iustin Pop
  def GetInstanceList(self):
1264 a8083063 Iustin Pop
    """Get the list of instances.
1265 a8083063 Iustin Pop

1266 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1267 c41eea6e Iustin Pop
        'instance1.example.com']
1268 a8083063 Iustin Pop

1269 a8083063 Iustin Pop
    """
1270 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1271 a8083063 Iustin Pop
1272 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1273 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1274 a8083063 Iustin Pop

1275 a8083063 Iustin Pop
    """
1276 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1277 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1278 a8083063 Iustin Pop
1279 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1280 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1281 94bbfece Iustin Pop

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

1284 94bbfece Iustin Pop
    """
1285 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1286 94bbfece Iustin Pop
      return None
1287 94bbfece Iustin Pop
1288 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1289 94bbfece Iustin Pop
1290 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1291 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1292 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1293 a8083063 Iustin Pop

1294 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1295 a8083063 Iustin Pop
    an instance are taken from the live systems.
1296 a8083063 Iustin Pop

1297 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1298 c41eea6e Iustin Pop
        I{instance1.example.com}
1299 a8083063 Iustin Pop

1300 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1301 c41eea6e Iustin Pop
    @return: the instance object
1302 a8083063 Iustin Pop

1303 a8083063 Iustin Pop
    """
1304 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1305 a8083063 Iustin Pop
1306 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1307 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1308 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1309 2674690b Michael Hanselmann

1310 2674690b Michael Hanselmann
    @rtype: frozenset
1311 2674690b Michael Hanselmann

1312 2674690b Michael Hanselmann
    """
1313 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1314 2674690b Michael Hanselmann
    if not instance:
1315 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1316 2674690b Michael Hanselmann
1317 2674690b Michael Hanselmann
    if primary_only:
1318 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1319 2674690b Michael Hanselmann
    else:
1320 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1321 2674690b Michael Hanselmann
1322 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1323 2674690b Michael Hanselmann
                     for node_name in nodes)
1324 2674690b Michael Hanselmann
1325 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1326 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1327 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1328 71333cb9 Iustin Pop

1329 71333cb9 Iustin Pop
    @param instances: list of instance names
1330 71333cb9 Iustin Pop
    @rtype: list
1331 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1332 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1333 71333cb9 Iustin Pop
        node, while keeping the original order
1334 71333cb9 Iustin Pop

1335 71333cb9 Iustin Pop
    """
1336 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1337 71333cb9 Iustin Pop
1338 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1339 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1340 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1341 0b2de758 Iustin Pop

1342 0b2de758 Iustin Pop
    @rtype: dict
1343 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1344 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1345 0b2de758 Iustin Pop

1346 0b2de758 Iustin Pop
    """
1347 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1348 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1349 0b2de758 Iustin Pop
    return my_dict
1350 0b2de758 Iustin Pop
1351 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1352 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1353 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1354 cc19798f Michael Hanselmann

1355 cc19798f Michael Hanselmann
    @type filter_fn: callable
1356 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1357 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1358 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1359 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1360 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1361 cc19798f Michael Hanselmann

1362 cc19798f Michael Hanselmann
    """
1363 cc19798f Michael Hanselmann
    return dict((name, inst)
1364 cc19798f Michael Hanselmann
                for (name, inst) in self._config_data.instances.items()
1365 cc19798f Michael Hanselmann
                if filter_fn(inst))
1366 cc19798f Michael Hanselmann
1367 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1368 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1369 a8083063 Iustin Pop
    """Add a node to the configuration.
1370 a8083063 Iustin Pop

1371 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1372 c41eea6e Iustin Pop
    @param node: a Node instance
1373 a8083063 Iustin Pop

1374 a8083063 Iustin Pop
    """
1375 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1376 d8470559 Michael Hanselmann
1377 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1378 430b923c Iustin Pop
1379 b989e85d Iustin Pop
    node.serial_no = 1
1380 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1381 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1382 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1383 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1384 a8083063 Iustin Pop
    self._WriteConfig()
1385 a8083063 Iustin Pop
1386 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1387 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1388 a8083063 Iustin Pop
    """Remove a node from the configuration.
1389 a8083063 Iustin Pop

1390 a8083063 Iustin Pop
    """
1391 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1392 d8470559 Michael Hanselmann
1393 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1394 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1395 a8083063 Iustin Pop
1396 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1397 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1398 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1399 a8083063 Iustin Pop
    self._WriteConfig()
1400 a8083063 Iustin Pop
1401 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1402 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1403 a8083063 Iustin Pop

1404 a8083063 Iustin Pop
    """
1405 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1406 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1407 a8083063 Iustin Pop
1408 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1409 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1410 a8083063 Iustin Pop

1411 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1412 c41eea6e Iustin Pop
    held.
1413 f78ede4e Guido Trotter

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

1416 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1417 c41eea6e Iustin Pop
    @return: the node object
1418 a8083063 Iustin Pop

1419 a8083063 Iustin Pop
    """
1420 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1421 a8083063 Iustin Pop
      return None
1422 a8083063 Iustin Pop
1423 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1424 a8083063 Iustin Pop
1425 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1426 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1427 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1428 f78ede4e Guido Trotter

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

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

1433 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1434 c41eea6e Iustin Pop
    @return: the node object
1435 f78ede4e Guido Trotter

1436 f78ede4e Guido Trotter
    """
1437 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1438 f78ede4e Guido Trotter
1439 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1440 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1441 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1442 8bf9e9a5 Iustin Pop

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

1445 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1446 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1447 8bf9e9a5 Iustin Pop

1448 8bf9e9a5 Iustin Pop
    """
1449 8bf9e9a5 Iustin Pop
    pri = []
1450 8bf9e9a5 Iustin Pop
    sec = []
1451 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1452 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1453 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1454 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1455 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1456 8bf9e9a5 Iustin Pop
    return (pri, sec)
1457 8bf9e9a5 Iustin Pop
1458 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1459 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1460 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1461 c71b049c Michael Hanselmann

1462 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1463 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1464 c71b049c Michael Hanselmann
    @rtype: frozenset
1465 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1466 c71b049c Michael Hanselmann

1467 c71b049c Michael Hanselmann
    """
1468 c71b049c Michael Hanselmann
    if primary_only:
1469 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1470 c71b049c Michael Hanselmann
    else:
1471 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1472 c71b049c Michael Hanselmann
1473 c71b049c Michael Hanselmann
    return frozenset(inst.name
1474 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1475 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1476 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1477 c71b049c Michael Hanselmann
1478 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1479 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1480 a8083063 Iustin Pop

1481 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1482 c41eea6e Iustin Pop
    held.
1483 c41eea6e Iustin Pop

1484 c41eea6e Iustin Pop
    @rtype: list
1485 f78ede4e Guido Trotter

1486 a8083063 Iustin Pop
    """
1487 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1488 a8083063 Iustin Pop
1489 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1490 f78ede4e Guido Trotter
  def GetNodeList(self):
1491 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1492 f78ede4e Guido Trotter

1493 f78ede4e Guido Trotter
    """
1494 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1495 f78ede4e Guido Trotter
1496 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1497 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1498 94a02bb5 Iustin Pop

1499 94a02bb5 Iustin Pop
    """
1500 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1501 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1502 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1503 94a02bb5 Iustin Pop
1504 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1505 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1506 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1507 6819dc49 Iustin Pop

1508 6819dc49 Iustin Pop
    """
1509 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1510 6819dc49 Iustin Pop
1511 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1512 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1513 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1514 075b62ca Iustin Pop

1515 075b62ca Iustin Pop
    """
1516 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1517 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1518 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1519 075b62ca Iustin Pop
1520 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1521 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1522 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1523 8bf9e9a5 Iustin Pop

1524 8bf9e9a5 Iustin Pop
    """
1525 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1526 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1527 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1528 8bf9e9a5 Iustin Pop
1529 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1530 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1531 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1532 f5eaa3c1 Iustin Pop

1533 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1534 f5eaa3c1 Iustin Pop
    @rtype: list
1535 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1536 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1537 f5eaa3c1 Iustin Pop
        order
1538 f5eaa3c1 Iustin Pop

1539 f5eaa3c1 Iustin Pop
    """
1540 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1541 f5eaa3c1 Iustin Pop
1542 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1543 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1544 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1545 d65e5776 Iustin Pop

1546 d65e5776 Iustin Pop
    @rtype: dict
1547 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1548 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1549 d65e5776 Iustin Pop

1550 d65e5776 Iustin Pop
    """
1551 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1552 ee14d800 Michael Hanselmann
1553 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1554 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1555 ee14d800 Michael Hanselmann

1556 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1557 ee14d800 Michael Hanselmann

1558 ee14d800 Michael Hanselmann
    """
1559 ee14d800 Michael Hanselmann
    return dict([(node, self._UnlockedGetNodeInfo(node))
1560 ee14d800 Michael Hanselmann
                 for node in self._UnlockedGetNodeList()])
1561 d65e5776 Iustin Pop
1562 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1563 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1564 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1565 9d5b1371 Michael Hanselmann

1566 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1567 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1568 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1569 9d5b1371 Michael Hanselmann

1570 9d5b1371 Michael Hanselmann
    """
1571 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1572 9d5b1371 Michael Hanselmann
1573 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1574 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1575 ec0292f1 Iustin Pop

1576 23f06b2b Iustin Pop
    @type exceptions: list
1577 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1578 ec0292f1 Iustin Pop
    @rtype: tuple
1579 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1580 ec0292f1 Iustin Pop

1581 ec0292f1 Iustin Pop
    """
1582 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1583 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1584 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1585 23f06b2b Iustin Pop
        continue
1586 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1587 ec0292f1 Iustin Pop
        mc_max += 1
1588 ec0292f1 Iustin Pop
      if node.master_candidate:
1589 ec0292f1 Iustin Pop
        mc_now += 1
1590 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1591 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1592 ec0292f1 Iustin Pop
1593 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1594 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1595 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1596 ec0292f1 Iustin Pop

1597 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1598 ec0292f1 Iustin Pop

1599 23f06b2b Iustin Pop
    @type exceptions: list
1600 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1601 ec0292f1 Iustin Pop
    @rtype: tuple
1602 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1603 ec0292f1 Iustin Pop

1604 ec0292f1 Iustin Pop
    """
1605 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1606 ec0292f1 Iustin Pop
1607 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1608 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1609 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1610 ec0292f1 Iustin Pop

1611 44485f49 Guido Trotter
    @type exceptions: list
1612 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1613 ec0292f1 Iustin Pop
    @rtype: list
1614 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1615 ec0292f1 Iustin Pop

1616 ec0292f1 Iustin Pop
    """
1617 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1618 ec0292f1 Iustin Pop
    mod_list = []
1619 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1620 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1621 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1622 ec0292f1 Iustin Pop
      for name in node_list:
1623 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1624 ec0292f1 Iustin Pop
          break
1625 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1626 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1627 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1628 ec0292f1 Iustin Pop
          continue
1629 ee513a66 Iustin Pop
        mod_list.append(node)
1630 ec0292f1 Iustin Pop
        node.master_candidate = True
1631 ec0292f1 Iustin Pop
        node.serial_no += 1
1632 ec0292f1 Iustin Pop
        mc_now += 1
1633 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1634 ec0292f1 Iustin Pop
        # this should not happen
1635 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1636 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1637 ec0292f1 Iustin Pop
      if mod_list:
1638 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1639 ec0292f1 Iustin Pop
        self._WriteConfig()
1640 ec0292f1 Iustin Pop
1641 ec0292f1 Iustin Pop
    return mod_list
1642 ec0292f1 Iustin Pop
1643 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1644 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1645 190e3cb6 Guido Trotter

1646 190e3cb6 Guido Trotter
    """
1647 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1648 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1649 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1650 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1651 190e3cb6 Guido Trotter
      # is not found anymore.
1652 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1653 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1654 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1655 190e3cb6 Guido Trotter
1656 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1657 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1658 190e3cb6 Guido Trotter

1659 190e3cb6 Guido Trotter
    """
1660 f936c153 Iustin Pop
    nodegroup = node.group
1661 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1662 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1663 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1664 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1665 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1666 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1667 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1668 190e3cb6 Guido Trotter
    else:
1669 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1670 190e3cb6 Guido Trotter
1671 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1672 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1673 a8083063 Iustin Pop

1674 a8083063 Iustin Pop
    """
1675 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1676 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1677 a8083063 Iustin Pop
1678 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1679 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1680 76d5d3a3 Iustin Pop

1681 76d5d3a3 Iustin Pop
    """
1682 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1683 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1684 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1685 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1686 76d5d3a3 Iustin Pop
1687 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1688 a8083063 Iustin Pop
    """Read the config data from disk.
1689 a8083063 Iustin Pop

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

1729 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1730 76d5d3a3 Iustin Pop
    whole configuration, etc.
1731 76d5d3a3 Iustin Pop

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

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

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

1771 a8083063 Iustin Pop
    """
1772 a8083063 Iustin Pop
    if self._offline:
1773 a8083063 Iustin Pop
      return True
1774 a4eae71f Michael Hanselmann
1775 a8083063 Iustin Pop
    bad = False
1776 a8083063 Iustin Pop
1777 6a5b8b4b Iustin Pop
    node_list = []
1778 6a5b8b4b Iustin Pop
    addr_list = []
1779 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1780 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1781 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1782 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1783 6b294c53 Iustin Pop
    # in between
1784 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1785 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1786 6a5b8b4b Iustin Pop
        continue
1787 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1788 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1789 6a5b8b4b Iustin Pop
        continue
1790 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1791 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1792 6b294c53 Iustin Pop
1793 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
1794 415a7304 Michael Hanselmann
    result = \
1795 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
1796 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1797 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1798 1b54fc6c Guido Trotter
      if msg:
1799 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1800 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1801 1b54fc6c Guido Trotter
        logging.error(msg)
1802 a4eae71f Michael Hanselmann
1803 a4eae71f Michael Hanselmann
        if feedback_fn:
1804 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1805 a4eae71f Michael Hanselmann
1806 a8083063 Iustin Pop
        bad = True
1807 a4eae71f Michael Hanselmann
1808 a8083063 Iustin Pop
    return not bad
1809 a8083063 Iustin Pop
1810 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1811 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1812 a8083063 Iustin Pop

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

1873 054596f0 Iustin Pop
    @rtype: dict
1874 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1875 054596f0 Iustin Pop
        associated value
1876 054596f0 Iustin Pop

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

1944 d367b66c Manuel Franceschini
    """
1945 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
1946 d367b66c Manuel Franceschini
1947 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1948 a8083063 Iustin Pop
  def GetVGName(self):
1949 a8083063 Iustin Pop
    """Return the volume group name.
1950 a8083063 Iustin Pop

1951 a8083063 Iustin Pop
    """
1952 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1953 a8083063 Iustin Pop
1954 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1955 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1956 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1957 89ff8e15 Manuel Franceschini

1958 89ff8e15 Manuel Franceschini
    """
1959 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1960 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1961 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1962 89ff8e15 Manuel Franceschini
1963 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1964 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
1965 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
1966 9e33896b Luca Bigliardi

1967 9e33896b Luca Bigliardi
    """
1968 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
1969 9e33896b Luca Bigliardi
1970 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
1971 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
1972 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
1973 9e33896b Luca Bigliardi

1974 9e33896b Luca Bigliardi
    """
1975 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
1976 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
1977 9e33896b Luca Bigliardi
    self._WriteConfig()
1978 9e33896b Luca Bigliardi
1979 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1980 a8083063 Iustin Pop
  def GetMACPrefix(self):
1981 a8083063 Iustin Pop
    """Return the mac prefix.
1982 a8083063 Iustin Pop

1983 a8083063 Iustin Pop
    """
1984 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1985 62779dd0 Iustin Pop
1986 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1987 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1988 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1989 62779dd0 Iustin Pop

1990 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1991 c41eea6e Iustin Pop
    @return: the cluster object
1992 62779dd0 Iustin Pop

1993 62779dd0 Iustin Pop
    """
1994 62779dd0 Iustin Pop
    return self._config_data.cluster
1995 e00fb268 Iustin Pop
1996 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1997 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
1998 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
1999 51cb1581 Luca Bigliardi

2000 51cb1581 Luca Bigliardi
    """
2001 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2002 51cb1581 Luca Bigliardi
2003 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2004 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
2005 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2006 e00fb268 Iustin Pop

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

2013 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2014 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2015 c41eea6e Iustin Pop
        the cluster
2016 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2017 c41eea6e Iustin Pop

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

2055 73064714 Guido Trotter
    """
2056 d8aee57e Iustin Pop
    for rm in self._all_rms:
2057 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)