Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ aa29e95f

History | View | Annotate | Download (59.2 kB)

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

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

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

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

32 a8083063 Iustin Pop
"""
33 a8083063 Iustin Pop
34 d367b66c Manuel Franceschini
# pylint: disable-msg=R0904
35 d367b66c Manuel Franceschini
# R0904: Too many public methods
36 d367b66c Manuel Franceschini
37 a8083063 Iustin Pop
import os
38 a8083063 Iustin Pop
import random
39 d8470559 Michael Hanselmann
import logging
40 d693c864 Iustin Pop
import time
41 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 a8083063 Iustin Pop
class ConfigWriter:
129 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
130 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

540 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
541 4a89c54a Iustin Pop

542 4a89c54a Iustin Pop
    @rtype: list
543 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
544 4a89c54a Iustin Pop
        configuration errors
545 4a89c54a Iustin Pop

546 4a89c54a Iustin Pop
    """
547 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
548 4a89c54a Iustin Pop
549 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
550 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
551 a8083063 Iustin Pop

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

554 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
555 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
556 a8083063 Iustin Pop
    node.
557 a8083063 Iustin Pop

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

560 a8083063 Iustin Pop
    """
561 a8083063 Iustin Pop
    if disk.children:
562 a8083063 Iustin Pop
      for child in disk.children:
563 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
564 a8083063 Iustin Pop
565 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
566 a8083063 Iustin Pop
      return
567 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
568 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
569 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
570 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
571 3ecf6786 Iustin Pop
                                        node_name)
572 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
573 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
574 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
575 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
576 a8083063 Iustin Pop
                                        " for %s" % str(disk))
577 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
578 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
579 a8083063 Iustin Pop
      if pnode == node_name:
580 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
581 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
582 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
583 a8083063 Iustin Pop
    else:
584 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
585 a8083063 Iustin Pop
    return
586 a8083063 Iustin Pop
587 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
588 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
589 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
590 f78ede4e Guido Trotter

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

593 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
594 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
595 f78ede4e Guido Trotter
    node.
596 f78ede4e Guido Trotter

597 f78ede4e Guido Trotter
    """
598 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
599 f78ede4e Guido Trotter
600 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
601 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
602 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
603 b2fddf63 Iustin Pop

604 b2fddf63 Iustin Pop
    """
605 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
606 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
607 264bb3c5 Michael Hanselmann
608 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
609 264bb3c5 Michael Hanselmann
    self._WriteConfig()
610 264bb3c5 Michael Hanselmann
611 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
612 b2fddf63 Iustin Pop
  def GetPortList(self):
613 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
614 264bb3c5 Michael Hanselmann

615 264bb3c5 Michael Hanselmann
    """
616 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
617 264bb3c5 Michael Hanselmann
618 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
619 a8083063 Iustin Pop
  def AllocatePort(self):
620 a8083063 Iustin Pop
    """Allocate a port.
621 a8083063 Iustin Pop

622 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
623 b2fddf63 Iustin Pop
    default port range (and in this case we increase
624 b2fddf63 Iustin Pop
    highest_used_port).
625 a8083063 Iustin Pop

626 a8083063 Iustin Pop
    """
627 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
628 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
629 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
630 264bb3c5 Michael Hanselmann
    else:
631 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
632 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
633 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
634 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
635 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
636 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
637 a8083063 Iustin Pop
638 a8083063 Iustin Pop
    self._WriteConfig()
639 a8083063 Iustin Pop
    return port
640 a8083063 Iustin Pop
641 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
642 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
643 a81c53c9 Iustin Pop

644 4a89c54a Iustin Pop
    @rtype: (dict, list)
645 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
646 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
647 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
648 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
649 4a89c54a Iustin Pop
        should raise an exception
650 a81c53c9 Iustin Pop

651 a81c53c9 Iustin Pop
    """
652 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
653 4a89c54a Iustin Pop
      duplicates = []
654 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
655 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
656 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
657 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
658 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
659 a81c53c9 Iustin Pop
          if port in used[node]:
660 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
661 4a89c54a Iustin Pop
          else:
662 4a89c54a Iustin Pop
            used[node][port] = instance_name
663 a81c53c9 Iustin Pop
      if disk.children:
664 a81c53c9 Iustin Pop
        for child in disk.children:
665 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
666 4a89c54a Iustin Pop
      return duplicates
667 a81c53c9 Iustin Pop
668 4a89c54a Iustin Pop
    duplicates = []
669 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
670 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
671 79b26a7a Iustin Pop
      for disk in instance.disks:
672 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
673 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
674 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
675 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
676 4a89c54a Iustin Pop
      else:
677 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
678 4a89c54a Iustin Pop
    return my_dict, duplicates
679 a81c53c9 Iustin Pop
680 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
681 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
682 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
683 6d2e83d5 Iustin Pop

684 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
685 6d2e83d5 Iustin Pop

686 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
687 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
688 6d2e83d5 Iustin Pop
        an empty list).
689 6d2e83d5 Iustin Pop

690 6d2e83d5 Iustin Pop
    """
691 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
692 4a89c54a Iustin Pop
    if duplicates:
693 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
694 4a89c54a Iustin Pop
                                      str(duplicates))
695 4a89c54a Iustin Pop
    return d_map
696 6d2e83d5 Iustin Pop
697 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
698 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
699 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
700 a81c53c9 Iustin Pop

701 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
702 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
703 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
704 a81c53c9 Iustin Pop
    order as the passed nodes.
705 a81c53c9 Iustin Pop

706 32388e6d Iustin Pop
    @type instance: string
707 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
708 32388e6d Iustin Pop

709 a81c53c9 Iustin Pop
    """
710 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
711 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
712 32388e6d Iustin Pop
713 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
714 4a89c54a Iustin Pop
    if duplicates:
715 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
716 4a89c54a Iustin Pop
                                      str(duplicates))
717 a81c53c9 Iustin Pop
    result = []
718 a81c53c9 Iustin Pop
    for nname in nodes:
719 a81c53c9 Iustin Pop
      ndata = d_map[nname]
720 a81c53c9 Iustin Pop
      if not ndata:
721 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
722 a81c53c9 Iustin Pop
        result.append(0)
723 a81c53c9 Iustin Pop
        ndata[0] = instance
724 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
725 a81c53c9 Iustin Pop
        continue
726 a81c53c9 Iustin Pop
      keys = ndata.keys()
727 a81c53c9 Iustin Pop
      keys.sort()
728 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
729 a81c53c9 Iustin Pop
      if ffree is None:
730 a81c53c9 Iustin Pop
        # return the next minor
731 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
732 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
733 a81c53c9 Iustin Pop
      else:
734 a81c53c9 Iustin Pop
        minor = ffree
735 4a89c54a Iustin Pop
      # double-check minor against current instances
736 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
737 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
738 4a89c54a Iustin Pop
              " already allocated to instance %s" %
739 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
740 a81c53c9 Iustin Pop
      ndata[minor] = instance
741 4a89c54a Iustin Pop
      # double-check minor against reservation
742 4a89c54a Iustin Pop
      r_key = (nname, minor)
743 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
744 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
745 4a89c54a Iustin Pop
              " reserved for instance %s" %
746 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
747 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
748 4a89c54a Iustin Pop
      result.append(minor)
749 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
750 a81c53c9 Iustin Pop
                  nodes, result)
751 a81c53c9 Iustin Pop
    return result
752 a81c53c9 Iustin Pop
753 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
754 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
755 a81c53c9 Iustin Pop

756 a81c53c9 Iustin Pop
    @type instance: string
757 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
758 a81c53c9 Iustin Pop
                     released
759 a81c53c9 Iustin Pop

760 a81c53c9 Iustin Pop
    """
761 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
762 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
763 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
764 a81c53c9 Iustin Pop
      if name == instance:
765 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
766 a81c53c9 Iustin Pop
767 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
768 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
769 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
770 61cf6b5e Iustin Pop

771 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
772 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
773 61cf6b5e Iustin Pop
    functions.
774 61cf6b5e Iustin Pop

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

777 61cf6b5e Iustin Pop
    @type instance: string
778 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
779 61cf6b5e Iustin Pop
                     released
780 61cf6b5e Iustin Pop

781 61cf6b5e Iustin Pop
    """
782 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
783 61cf6b5e Iustin Pop
784 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
785 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
786 4a8b186a Michael Hanselmann
    """Get the configuration version.
787 4a8b186a Michael Hanselmann

788 4a8b186a Michael Hanselmann
    @return: Config version
789 4a8b186a Michael Hanselmann

790 4a8b186a Michael Hanselmann
    """
791 4a8b186a Michael Hanselmann
    return self._config_data.version
792 4a8b186a Michael Hanselmann
793 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
794 4a8b186a Michael Hanselmann
  def GetClusterName(self):
795 4a8b186a Michael Hanselmann
    """Get cluster name.
796 4a8b186a Michael Hanselmann

797 4a8b186a Michael Hanselmann
    @return: Cluster name
798 4a8b186a Michael Hanselmann

799 4a8b186a Michael Hanselmann
    """
800 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
801 4a8b186a Michael Hanselmann
802 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
803 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
804 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
805 4a8b186a Michael Hanselmann

806 4a8b186a Michael Hanselmann
    @return: Master hostname
807 4a8b186a Michael Hanselmann

808 4a8b186a Michael Hanselmann
    """
809 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
810 4a8b186a Michael Hanselmann
811 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
812 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
813 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
814 4a8b186a Michael Hanselmann

815 4a8b186a Michael Hanselmann
    @return: Master IP
816 4a8b186a Michael Hanselmann

817 4a8b186a Michael Hanselmann
    """
818 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
819 4a8b186a Michael Hanselmann
820 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
821 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
822 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
823 4a8b186a Michael Hanselmann

824 4a8b186a Michael Hanselmann
    """
825 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
826 4a8b186a Michael Hanselmann
827 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
828 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
829 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
830 4a8b186a Michael Hanselmann

831 4a8b186a Michael Hanselmann
    """
832 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
833 4a8b186a Michael Hanselmann
834 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
835 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
836 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
837 4a8b186a Michael Hanselmann

838 4a8b186a Michael Hanselmann
    """
839 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
840 4a8b186a Michael Hanselmann
841 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
842 a8083063 Iustin Pop
  def GetHostKey(self):
843 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
844 a8083063 Iustin Pop

845 c41eea6e Iustin Pop
    @rtype: string
846 c41eea6e Iustin Pop
    @return: the rsa hostkey
847 a8083063 Iustin Pop

848 a8083063 Iustin Pop
    """
849 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
850 a8083063 Iustin Pop
851 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
852 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
853 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
854 bf4af505 Apollon Oikonomopoulos

855 bf4af505 Apollon Oikonomopoulos
    """
856 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
857 bf4af505 Apollon Oikonomopoulos
858 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
859 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
860 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
861 868a98ca Manuel Franceschini

862 868a98ca Manuel Franceschini
    @return: primary ip family
863 868a98ca Manuel Franceschini

864 868a98ca Manuel Franceschini
    """
865 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
866 868a98ca Manuel Franceschini
867 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
868 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
869 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
870 e11a1b77 Adeodato Simo

871 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
872 90e99856 Adeodato Simo
    according to their default values.
873 90e99856 Adeodato Simo

874 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
875 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
876 e11a1b77 Adeodato Simo
    @type ec_id: string
877 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
878 e11a1b77 Adeodato Simo
    @type check_uuid: bool
879 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
880 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
881 e11a1b77 Adeodato Simo
                       configuration already
882 e11a1b77 Adeodato Simo

883 e11a1b77 Adeodato Simo
    """
884 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
885 e11a1b77 Adeodato Simo
    self._WriteConfig()
886 e11a1b77 Adeodato Simo
887 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
888 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
889 e11a1b77 Adeodato Simo

890 e11a1b77 Adeodato Simo
    """
891 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
892 e11a1b77 Adeodato Simo
893 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
894 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
895 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
896 e11a1b77 Adeodato Simo
    if check_uuid:
897 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
898 e11a1b77 Adeodato Simo
899 e11a1b77 Adeodato Simo
    group.serial_no = 1
900 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
901 90e99856 Adeodato Simo
    group.UpgradeConfig()
902 e11a1b77 Adeodato Simo
903 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
904 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
905 e11a1b77 Adeodato Simo
906 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
907 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
908 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
909 e11a1b77 Adeodato Simo

910 e11a1b77 Adeodato Simo
    @type group_uuid: string
911 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
912 e11a1b77 Adeodato Simo

913 e11a1b77 Adeodato Simo
    """
914 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
915 e11a1b77 Adeodato Simo
916 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
917 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
918 e11a1b77 Adeodato Simo
919 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
920 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
921 e11a1b77 Adeodato Simo
    self._WriteConfig()
922 e11a1b77 Adeodato Simo
923 eaa98a04 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
924 eaa98a04 Guido Trotter
  def LookupNodeGroup(self, target):
925 412b3531 Guido Trotter
    """Lookup a node group's UUID.
926 eaa98a04 Guido Trotter

927 eaa98a04 Guido Trotter
    @type target: string or None
928 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
929 eaa98a04 Guido Trotter
    @rtype: string
930 412b3531 Guido Trotter
    @return: nodegroup UUID
931 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
932 eaa98a04 Guido Trotter

933 eaa98a04 Guido Trotter
    """
934 eaa98a04 Guido Trotter
    if target is None:
935 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
936 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
937 eaa98a04 Guido Trotter
                                   " group must be specified explicitely.")
938 eaa98a04 Guido Trotter
      else:
939 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
940 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
941 eaa98a04 Guido Trotter
      return target
942 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
943 eaa98a04 Guido Trotter
      if nodegroup.name == target:
944 eaa98a04 Guido Trotter
        return nodegroup.uuid
945 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
946 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
947 eaa98a04 Guido Trotter
948 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
949 648e4196 Guido Trotter
    """Lookup a node group.
950 648e4196 Guido Trotter

951 648e4196 Guido Trotter
    @type uuid: string
952 648e4196 Guido Trotter
    @param uuid: group UUID
953 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
954 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
955 648e4196 Guido Trotter

956 648e4196 Guido Trotter
    """
957 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
958 648e4196 Guido Trotter
      return None
959 648e4196 Guido Trotter
960 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
961 648e4196 Guido Trotter
962 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
963 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
964 5768e6a6 René Nussbaumer
    """Lookup a node group.
965 5768e6a6 René Nussbaumer

966 5768e6a6 René Nussbaumer
    @type uuid: string
967 5768e6a6 René Nussbaumer
    @param uuid: group UUID
968 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
969 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
970 5768e6a6 René Nussbaumer

971 5768e6a6 René Nussbaumer
    """
972 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
973 5768e6a6 René Nussbaumer
974 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
975 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
976 622444e5 Iustin Pop
    """Get the configuration of all node groups.
977 622444e5 Iustin Pop

978 622444e5 Iustin Pop
    """
979 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
980 622444e5 Iustin Pop
981 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
982 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
983 1ac6f2ad Guido Trotter
    """Get a list of node groups.
984 1ac6f2ad Guido Trotter

985 1ac6f2ad Guido Trotter
    """
986 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
987 1ac6f2ad Guido Trotter
988 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
989 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
990 a8083063 Iustin Pop
    """Add an instance to the config.
991 a8083063 Iustin Pop

992 a8083063 Iustin Pop
    This should be used after creating a new instance.
993 a8083063 Iustin Pop

994 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
995 c41eea6e Iustin Pop
    @param instance: the instance object
996 c41eea6e Iustin Pop

997 a8083063 Iustin Pop
    """
998 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
999 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1000 a8083063 Iustin Pop
1001 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1002 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1003 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1004 923b1523 Iustin Pop
1005 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1006 e4640214 Guido Trotter
    for nic in instance.nics:
1007 e4640214 Guido Trotter
      if nic.mac in all_macs:
1008 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1009 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1010 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1011 430b923c Iustin Pop
1012 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1013 e4640214 Guido Trotter
1014 b989e85d Iustin Pop
    instance.serial_no = 1
1015 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1016 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1017 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1018 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1019 a8083063 Iustin Pop
    self._WriteConfig()
1020 a8083063 Iustin Pop
1021 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1022 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1023 430b923c Iustin Pop

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

1027 430b923c Iustin Pop
    """
1028 430b923c Iustin Pop
    if not item.uuid:
1029 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1030 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1031 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1032 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1033 430b923c Iustin Pop
1034 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1035 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1036 a8083063 Iustin Pop

1037 a8083063 Iustin Pop
    """
1038 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
1039 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1040 a8083063 Iustin Pop
1041 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1042 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1043 3ecf6786 Iustin Pop
                                      instance_name)
1044 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1045 0d68c45d Iustin Pop
    if instance.admin_up != status:
1046 0d68c45d Iustin Pop
      instance.admin_up = status
1047 b989e85d Iustin Pop
      instance.serial_no += 1
1048 d693c864 Iustin Pop
      instance.mtime = time.time()
1049 455a3445 Iustin Pop
      self._WriteConfig()
1050 a8083063 Iustin Pop
1051 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1052 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1053 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1054 6a408fb2 Iustin Pop

1055 6a408fb2 Iustin Pop
    """
1056 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
1057 6a408fb2 Iustin Pop
1058 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1059 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1060 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1061 a8083063 Iustin Pop

1062 a8083063 Iustin Pop
    """
1063 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1064 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1065 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1066 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1067 a8083063 Iustin Pop
    self._WriteConfig()
1068 a8083063 Iustin Pop
1069 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1070 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1071 fc95f88f Iustin Pop
    """Rename an instance.
1072 fc95f88f Iustin Pop

1073 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1074 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1075 fc95f88f Iustin Pop
    rename.
1076 fc95f88f Iustin Pop

1077 fc95f88f Iustin Pop
    """
1078 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
1079 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
1080 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
1081 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
1082 fc95f88f Iustin Pop
    inst.name = new_name
1083 b23c4333 Manuel Franceschini
1084 b23c4333 Manuel Franceschini
    for disk in inst.disks:
1085 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1086 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1087 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1088 3721d2fe Guido Trotter
        disk_fname = "disk%s" % disk.iv_name.split("/")[1]
1089 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
1090 c4feafe8 Iustin Pop
                                              utils.PathJoin(file_storage_dir,
1091 c4feafe8 Iustin Pop
                                                             inst.name,
1092 3721d2fe Guido Trotter
                                                             disk_fname))
1093 b23c4333 Manuel Franceschini
1094 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1095 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1096 1fc34c48 Michael Hanselmann
1097 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
1098 fc95f88f Iustin Pop
    self._WriteConfig()
1099 fc95f88f Iustin Pop
1100 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1101 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
1102 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1103 a8083063 Iustin Pop

1104 a8083063 Iustin Pop
    """
1105 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
1106 a8083063 Iustin Pop
1107 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1108 94bbfece Iustin Pop
    """Get the list of instances.
1109 94bbfece Iustin Pop

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

1112 94bbfece Iustin Pop
    """
1113 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1114 94bbfece Iustin Pop
1115 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1116 a8083063 Iustin Pop
  def GetInstanceList(self):
1117 a8083063 Iustin Pop
    """Get the list of instances.
1118 a8083063 Iustin Pop

1119 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1120 c41eea6e Iustin Pop
        'instance1.example.com']
1121 a8083063 Iustin Pop

1122 a8083063 Iustin Pop
    """
1123 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1124 a8083063 Iustin Pop
1125 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1126 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1127 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1128 a8083063 Iustin Pop

1129 a8083063 Iustin Pop
    """
1130 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1131 bcdf16d7 Guido Trotter
                                    self._config_data.instances.keys(),
1132 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1133 a8083063 Iustin Pop
1134 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1135 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1136 94bbfece Iustin Pop

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

1139 94bbfece Iustin Pop
    """
1140 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1141 94bbfece Iustin Pop
      return None
1142 94bbfece Iustin Pop
1143 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1144 94bbfece Iustin Pop
1145 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1146 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1147 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1148 a8083063 Iustin Pop

1149 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1150 a8083063 Iustin Pop
    an instance are taken from the live systems.
1151 a8083063 Iustin Pop

1152 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1153 c41eea6e Iustin Pop
        I{instance1.example.com}
1154 a8083063 Iustin Pop

1155 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1156 c41eea6e Iustin Pop
    @return: the instance object
1157 a8083063 Iustin Pop

1158 a8083063 Iustin Pop
    """
1159 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1160 a8083063 Iustin Pop
1161 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1162 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1163 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1164 0b2de758 Iustin Pop

1165 0b2de758 Iustin Pop
    @rtype: dict
1166 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1167 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1168 0b2de758 Iustin Pop

1169 0b2de758 Iustin Pop
    """
1170 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1171 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1172 0b2de758 Iustin Pop
    return my_dict
1173 0b2de758 Iustin Pop
1174 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1175 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1176 a8083063 Iustin Pop
    """Add a node to the configuration.
1177 a8083063 Iustin Pop

1178 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1179 c41eea6e Iustin Pop
    @param node: a Node instance
1180 a8083063 Iustin Pop

1181 a8083063 Iustin Pop
    """
1182 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1183 d8470559 Michael Hanselmann
1184 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1185 430b923c Iustin Pop
1186 b989e85d Iustin Pop
    node.serial_no = 1
1187 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1188 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1189 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1190 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1191 a8083063 Iustin Pop
    self._WriteConfig()
1192 a8083063 Iustin Pop
1193 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1194 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1195 a8083063 Iustin Pop
    """Remove a node from the configuration.
1196 a8083063 Iustin Pop

1197 a8083063 Iustin Pop
    """
1198 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1199 d8470559 Michael Hanselmann
1200 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1201 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1202 a8083063 Iustin Pop
1203 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1204 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1205 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1206 a8083063 Iustin Pop
    self._WriteConfig()
1207 a8083063 Iustin Pop
1208 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1209 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1210 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1211 a8083063 Iustin Pop

1212 a8083063 Iustin Pop
    """
1213 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1214 bcdf16d7 Guido Trotter
                                    self._config_data.nodes.keys(),
1215 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1216 a8083063 Iustin Pop
1217 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1218 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1219 a8083063 Iustin Pop

1220 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1221 c41eea6e Iustin Pop
    held.
1222 f78ede4e Guido Trotter

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

1225 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1226 c41eea6e Iustin Pop
    @return: the node object
1227 a8083063 Iustin Pop

1228 a8083063 Iustin Pop
    """
1229 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1230 a8083063 Iustin Pop
      return None
1231 a8083063 Iustin Pop
1232 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1233 a8083063 Iustin Pop
1234 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1235 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1236 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1237 f78ede4e Guido Trotter

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

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

1242 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1243 c41eea6e Iustin Pop
    @return: the node object
1244 f78ede4e Guido Trotter

1245 f78ede4e Guido Trotter
    """
1246 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1247 f78ede4e Guido Trotter
1248 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1249 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1250 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1251 8bf9e9a5 Iustin Pop

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

1254 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1255 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1256 8bf9e9a5 Iustin Pop

1257 8bf9e9a5 Iustin Pop
    """
1258 8bf9e9a5 Iustin Pop
    pri = []
1259 8bf9e9a5 Iustin Pop
    sec = []
1260 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1261 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1262 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1263 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1264 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1265 8bf9e9a5 Iustin Pop
    return (pri, sec)
1266 8bf9e9a5 Iustin Pop
1267 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1268 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1269 a8083063 Iustin Pop

1270 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1271 c41eea6e Iustin Pop
    held.
1272 c41eea6e Iustin Pop

1273 c41eea6e Iustin Pop
    @rtype: list
1274 f78ede4e Guido Trotter

1275 a8083063 Iustin Pop
    """
1276 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1277 a8083063 Iustin Pop
1278 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1279 f78ede4e Guido Trotter
  def GetNodeList(self):
1280 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1281 f78ede4e Guido Trotter

1282 f78ede4e Guido Trotter
    """
1283 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1284 f78ede4e Guido Trotter
1285 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1286 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1287 94a02bb5 Iustin Pop

1288 94a02bb5 Iustin Pop
    """
1289 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1290 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1291 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1292 94a02bb5 Iustin Pop
1293 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1294 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1295 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1296 6819dc49 Iustin Pop

1297 6819dc49 Iustin Pop
    """
1298 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1299 6819dc49 Iustin Pop
1300 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1301 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1302 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1303 8bf9e9a5 Iustin Pop

1304 8bf9e9a5 Iustin Pop
    """
1305 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1306 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1307 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1308 8bf9e9a5 Iustin Pop
1309 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1310 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1311 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1312 d65e5776 Iustin Pop

1313 d65e5776 Iustin Pop
    @rtype: dict
1314 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1315 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1316 d65e5776 Iustin Pop

1317 d65e5776 Iustin Pop
    """
1318 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1319 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1320 d65e5776 Iustin Pop
    return my_dict
1321 d65e5776 Iustin Pop
1322 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1323 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1324 ec0292f1 Iustin Pop

1325 23f06b2b Iustin Pop
    @type exceptions: list
1326 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1327 ec0292f1 Iustin Pop
    @rtype: tuple
1328 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1329 ec0292f1 Iustin Pop

1330 ec0292f1 Iustin Pop
    """
1331 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1332 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1333 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1334 23f06b2b Iustin Pop
        continue
1335 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1336 ec0292f1 Iustin Pop
        mc_max += 1
1337 ec0292f1 Iustin Pop
      if node.master_candidate:
1338 ec0292f1 Iustin Pop
        mc_now += 1
1339 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1340 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1341 ec0292f1 Iustin Pop
1342 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1343 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1344 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1345 ec0292f1 Iustin Pop

1346 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1347 ec0292f1 Iustin Pop

1348 23f06b2b Iustin Pop
    @type exceptions: list
1349 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1350 ec0292f1 Iustin Pop
    @rtype: tuple
1351 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1352 ec0292f1 Iustin Pop

1353 ec0292f1 Iustin Pop
    """
1354 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1355 ec0292f1 Iustin Pop
1356 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1357 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1358 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1359 ec0292f1 Iustin Pop

1360 44485f49 Guido Trotter
    @type exceptions: list
1361 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1362 ec0292f1 Iustin Pop
    @rtype: list
1363 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1364 ec0292f1 Iustin Pop

1365 ec0292f1 Iustin Pop
    """
1366 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1367 ec0292f1 Iustin Pop
    mod_list = []
1368 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1369 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1370 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1371 ec0292f1 Iustin Pop
      for name in node_list:
1372 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1373 ec0292f1 Iustin Pop
          break
1374 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1375 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1376 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1377 ec0292f1 Iustin Pop
          continue
1378 ee513a66 Iustin Pop
        mod_list.append(node)
1379 ec0292f1 Iustin Pop
        node.master_candidate = True
1380 ec0292f1 Iustin Pop
        node.serial_no += 1
1381 ec0292f1 Iustin Pop
        mc_now += 1
1382 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1383 ec0292f1 Iustin Pop
        # this should not happen
1384 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1385 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1386 ec0292f1 Iustin Pop
      if mod_list:
1387 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1388 ec0292f1 Iustin Pop
        self._WriteConfig()
1389 ec0292f1 Iustin Pop
1390 ec0292f1 Iustin Pop
    return mod_list
1391 ec0292f1 Iustin Pop
1392 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1393 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1394 190e3cb6 Guido Trotter

1395 190e3cb6 Guido Trotter
    """
1396 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1397 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1398 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1399 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1400 190e3cb6 Guido Trotter
      # is not found anymore.
1401 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1402 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1403 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1404 190e3cb6 Guido Trotter
1405 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1406 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1407 190e3cb6 Guido Trotter

1408 190e3cb6 Guido Trotter
    """
1409 f936c153 Iustin Pop
    nodegroup = node.group
1410 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1411 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1412 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1413 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1414 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1415 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1416 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1417 190e3cb6 Guido Trotter
    else:
1418 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1419 190e3cb6 Guido Trotter
1420 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1421 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1422 a8083063 Iustin Pop

1423 a8083063 Iustin Pop
    """
1424 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1425 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1426 a8083063 Iustin Pop
1427 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1428 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1429 76d5d3a3 Iustin Pop

1430 76d5d3a3 Iustin Pop
    """
1431 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1432 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1433 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1434 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1435 76d5d3a3 Iustin Pop
1436 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1437 a8083063 Iustin Pop
    """Read the config data from disk.
1438 a8083063 Iustin Pop

1439 a8083063 Iustin Pop
    """
1440 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1441 13998ef2 Michael Hanselmann
1442 a8083063 Iustin Pop
    try:
1443 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1444 13998ef2 Michael Hanselmann
    except Exception, err:
1445 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1446 5b263ed7 Michael Hanselmann
1447 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1448 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1449 5b263ed7 Michael Hanselmann
1450 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1451 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1452 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1453 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1454 90d726a8 Iustin Pop
1455 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
1456 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
1457 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
1458 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
1459 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
1460 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
1461 eb180fe2 Iustin Pop
1462 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1463 90d726a8 Iustin Pop
    data.UpgradeConfig()
1464 90d726a8 Iustin Pop
1465 a8083063 Iustin Pop
    self._config_data = data
1466 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1467 0779e3aa Iustin Pop
    # ssconf update
1468 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1469 a8083063 Iustin Pop
1470 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1471 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1472 76d5d3a3 Iustin Pop
1473 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
1474 bd407597 Iustin Pop
1475 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1476 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1477 76d5d3a3 Iustin Pop

1478 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1479 76d5d3a3 Iustin Pop
    whole configuration, etc.
1480 76d5d3a3 Iustin Pop

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

1487 76d5d3a3 Iustin Pop
    """
1488 76d5d3a3 Iustin Pop
    modified = False
1489 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1490 76d5d3a3 Iustin Pop
      if item.uuid is None:
1491 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1492 76d5d3a3 Iustin Pop
        modified = True
1493 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
1494 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
1495 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
1496 75cf411a Adeodato Simo
                                            members=[])
1497 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
1498 f9e81396 Guido Trotter
      modified = True
1499 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
1500 f936c153 Iustin Pop
      if not node.group:
1501 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
1502 190e3cb6 Guido Trotter
        modified = True
1503 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
1504 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
1505 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
1506 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
1507 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
1508 76d5d3a3 Iustin Pop
    if modified:
1509 76d5d3a3 Iustin Pop
      self._WriteConfig()
1510 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1511 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1512 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1513 4fae38c5 Guido Trotter
1514 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1515 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1516 a8083063 Iustin Pop

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

1520 a8083063 Iustin Pop
    """
1521 a8083063 Iustin Pop
    if self._offline:
1522 a8083063 Iustin Pop
      return True
1523 a4eae71f Michael Hanselmann
1524 a8083063 Iustin Pop
    bad = False
1525 a8083063 Iustin Pop
1526 6a5b8b4b Iustin Pop
    node_list = []
1527 6a5b8b4b Iustin Pop
    addr_list = []
1528 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1529 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1530 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1531 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1532 6b294c53 Iustin Pop
    # in between
1533 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1534 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1535 6a5b8b4b Iustin Pop
        continue
1536 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1537 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1538 6a5b8b4b Iustin Pop
        continue
1539 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1540 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1541 6b294c53 Iustin Pop
1542 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1543 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1544 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1545 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1546 1b54fc6c Guido Trotter
      if msg:
1547 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1548 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1549 1b54fc6c Guido Trotter
        logging.error(msg)
1550 a4eae71f Michael Hanselmann
1551 a4eae71f Michael Hanselmann
        if feedback_fn:
1552 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1553 a4eae71f Michael Hanselmann
1554 a8083063 Iustin Pop
        bad = True
1555 a4eae71f Michael Hanselmann
1556 a8083063 Iustin Pop
    return not bad
1557 a8083063 Iustin Pop
1558 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1559 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1560 a8083063 Iustin Pop

1561 a8083063 Iustin Pop
    """
1562 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
1563 a4eae71f Michael Hanselmann
1564 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
1565 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
1566 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
1567 d2231b8c Iustin Pop
    # recovery to the user
1568 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1569 4a89c54a Iustin Pop
    if config_errors:
1570 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
1571 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
1572 d2231b8c Iustin Pop
      logging.critical(errmsg)
1573 d2231b8c Iustin Pop
      if feedback_fn:
1574 d2231b8c Iustin Pop
        feedback_fn(errmsg)
1575 d2231b8c Iustin Pop
1576 a8083063 Iustin Pop
    if destination is None:
1577 a8083063 Iustin Pop
      destination = self._cfg_file
1578 a8083063 Iustin Pop
    self._BumpSerialNo()
1579 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1580 13998ef2 Michael Hanselmann
1581 e60c73a1 René Nussbaumer
    getents = self._getents()
1582 bd407597 Iustin Pop
    try:
1583 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
1584 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
1585 bd407597 Iustin Pop
    except errors.LockError:
1586 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
1587 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
1588 bd407597 Iustin Pop
                                      " update")
1589 bd407597 Iustin Pop
    try:
1590 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
1591 bd407597 Iustin Pop
    finally:
1592 bd407597 Iustin Pop
      os.close(fd)
1593 13998ef2 Michael Hanselmann
1594 14e15659 Iustin Pop
    self.write_count += 1
1595 3d3a04bc Iustin Pop
1596 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
1597 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
1598 a8083063 Iustin Pop
1599 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
1600 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
1601 d9a855f1 Michael Hanselmann
      if not self._offline:
1602 cd34faf2 Michael Hanselmann
        result = rpc.RpcRunner.call_write_ssconf_files(
1603 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
1604 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
1605 a4eae71f Michael Hanselmann
1606 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
1607 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
1608 e1e75d00 Iustin Pop
          if msg:
1609 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
1610 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
1611 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
1612 a4eae71f Michael Hanselmann
1613 a4eae71f Michael Hanselmann
            if feedback_fn:
1614 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
1615 a4eae71f Michael Hanselmann
1616 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
1617 54d1a06e Michael Hanselmann
1618 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
1619 054596f0 Iustin Pop
    """Return the values needed by ssconf.
1620 054596f0 Iustin Pop

1621 054596f0 Iustin Pop
    @rtype: dict
1622 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1623 054596f0 Iustin Pop
        associated value
1624 054596f0 Iustin Pop

1625 054596f0 Iustin Pop
    """
1626 a3316e4a Iustin Pop
    fn = "\n".join
1627 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1628 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1629 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1630 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1631 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1632 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1633 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1634 a3316e4a Iustin Pop
1635 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1636 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1637 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1638 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1639 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1640 8113a52e Luca Bigliardi
                     if node.master_candidate)
1641 a3316e4a Iustin Pop
    node_data = fn(node_names)
1642 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1643 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1644 f56618e0 Iustin Pop
1645 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1646 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1647 4f7a6a10 Iustin Pop
1648 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
1649 4f7a6a10 Iustin Pop
1650 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
1651 0fbae49a Balazs Lecz
1652 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
1653 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
1654 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
1655 6f076453 Guido Trotter
1656 03d1dba2 Michael Hanselmann
    return {
1657 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1658 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1659 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1660 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1661 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1662 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1663 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1664 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1665 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1666 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1667 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1668 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1669 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1670 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
1671 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1672 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1673 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
1674 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
1675 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
1676 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
1677 03d1dba2 Michael Hanselmann
      }
1678 03d1dba2 Michael Hanselmann
1679 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1680 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
1681 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
1682 d367b66c Manuel Franceschini

1683 d367b66c Manuel Franceschini
    """
1684 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
1685 d367b66c Manuel Franceschini
1686 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1687 a8083063 Iustin Pop
  def GetVGName(self):
1688 a8083063 Iustin Pop
    """Return the volume group name.
1689 a8083063 Iustin Pop

1690 a8083063 Iustin Pop
    """
1691 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1692 a8083063 Iustin Pop
1693 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1694 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1695 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1696 89ff8e15 Manuel Franceschini

1697 89ff8e15 Manuel Franceschini
    """
1698 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1699 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1700 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1701 89ff8e15 Manuel Franceschini
1702 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1703 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
1704 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
1705 9e33896b Luca Bigliardi

1706 9e33896b Luca Bigliardi
    """
1707 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
1708 9e33896b Luca Bigliardi
1709 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
1710 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
1711 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
1712 9e33896b Luca Bigliardi

1713 9e33896b Luca Bigliardi
    """
1714 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
1715 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
1716 9e33896b Luca Bigliardi
    self._WriteConfig()
1717 9e33896b Luca Bigliardi
1718 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1719 a8083063 Iustin Pop
  def GetMACPrefix(self):
1720 a8083063 Iustin Pop
    """Return the mac prefix.
1721 a8083063 Iustin Pop

1722 a8083063 Iustin Pop
    """
1723 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1724 62779dd0 Iustin Pop
1725 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1726 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1727 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1728 62779dd0 Iustin Pop

1729 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1730 c41eea6e Iustin Pop
    @return: the cluster object
1731 62779dd0 Iustin Pop

1732 62779dd0 Iustin Pop
    """
1733 62779dd0 Iustin Pop
    return self._config_data.cluster
1734 e00fb268 Iustin Pop
1735 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1736 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
1737 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
1738 51cb1581 Luca Bigliardi

1739 51cb1581 Luca Bigliardi
    """
1740 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
1741 51cb1581 Luca Bigliardi
1742 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1743 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1744 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1745 e00fb268 Iustin Pop

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

1752 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1753 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1754 c41eea6e Iustin Pop
        the cluster
1755 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1756 c41eea6e Iustin Pop

1757 e00fb268 Iustin Pop
    """
1758 e00fb268 Iustin Pop
    if self._config_data is None:
1759 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1760 3ecf6786 Iustin Pop
                                   " cannot save.")
1761 f34901f8 Iustin Pop
    update_serial = False
1762 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1763 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1764 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1765 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1766 f34901f8 Iustin Pop
      update_serial = True
1767 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1768 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1769 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
1770 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
1771 e00fb268 Iustin Pop
    else:
1772 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1773 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1774 e00fb268 Iustin Pop
    if not test:
1775 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1776 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1777 f34901f8 Iustin Pop
    target.serial_no += 1
1778 d693c864 Iustin Pop
    target.mtime = now = time.time()
1779 f34901f8 Iustin Pop
1780 cff4c037 Iustin Pop
    if update_serial:
1781 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1782 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1783 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1784 b989e85d Iustin Pop
1785 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1786 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1787 61cf6b5e Iustin Pop
1788 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
1789 73064714 Guido Trotter
1790 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
1791 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
1792 73064714 Guido Trotter
    """Drop per-execution-context reservations
1793 73064714 Guido Trotter

1794 73064714 Guido Trotter
    """
1795 d8aee57e Iustin Pop
    for rm in self._all_rms:
1796 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)