Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ b43dcc5a

History | View | Annotate | Download (49.8 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 243cdbcc Michael Hanselmann
52 243cdbcc Michael Hanselmann
53 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
54 f78ede4e Guido Trotter
55 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
56 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
57 4fae38c5 Guido Trotter
58 f78ede4e Guido Trotter
59 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
60 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
61 c41eea6e Iustin Pop

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

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

67 c41eea6e Iustin Pop
  """
68 5b263ed7 Michael Hanselmann
  if data.version != constants.CONFIG_VERSION:
69 243cdbcc Michael Hanselmann
    raise errors.ConfigurationError("Cluster configuration version"
70 243cdbcc Michael Hanselmann
                                    " mismatch, got %s instead of %s" %
71 5b263ed7 Michael Hanselmann
                                    (data.version,
72 243cdbcc Michael Hanselmann
                                     constants.CONFIG_VERSION))
73 a8083063 Iustin Pop
74 319856a9 Michael Hanselmann
75 013da361 Guido Trotter
class TemporaryReservationManager:
76 013da361 Guido Trotter
  """A temporary resource reservation manager.
77 013da361 Guido Trotter

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

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

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

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

136 098c0958 Michael Hanselmann
  """
137 a8083063 Iustin Pop
  def __init__(self, cfg_file=None, offline=False):
138 14e15659 Iustin Pop
    self.write_count = 0
139 f78ede4e Guido Trotter
    self._lock = _config_lock
140 a8083063 Iustin Pop
    self._config_data = None
141 a8083063 Iustin Pop
    self._offline = offline
142 a8083063 Iustin Pop
    if cfg_file is None:
143 a8083063 Iustin Pop
      self._cfg_file = constants.CLUSTER_CONF_FILE
144 a8083063 Iustin Pop
    else:
145 a8083063 Iustin Pop
      self._cfg_file = cfg_file
146 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 3d3a04bc Iustin Pop
    self._OpenConfig()
160 a8083063 Iustin Pop
161 a8083063 Iustin Pop
  # this method needs to be static, so that we can call it on the class
162 a8083063 Iustin Pop
  @staticmethod
163 a8083063 Iustin Pop
  def IsCluster():
164 a8083063 Iustin Pop
    """Check if the cluster is configured.
165 a8083063 Iustin Pop

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

172 36b66e6e Guido Trotter
    """
173 36b66e6e Guido Trotter
    prefix = self._config_data.cluster.mac_prefix
174 36b66e6e Guido Trotter
    byte1 = random.randrange(0, 256)
175 36b66e6e Guido Trotter
    byte2 = random.randrange(0, 256)
176 36b66e6e Guido Trotter
    byte3 = random.randrange(0, 256)
177 36b66e6e Guido Trotter
    mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
178 36b66e6e Guido Trotter
    return mac
179 36b66e6e Guido Trotter
180 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
181 36b66e6e Guido Trotter
  def GenerateMAC(self, ec_id):
182 a8083063 Iustin Pop
    """Generate a MAC for an instance.
183 a8083063 Iustin Pop

184 a8083063 Iustin Pop
    This should check the current instances for duplicates.
185 a8083063 Iustin Pop

186 a8083063 Iustin Pop
    """
187 36b66e6e Guido Trotter
    existing = self._AllMACs()
188 36b66e6e Guido Trotter
    return self._temporary_ids.Generate(existing, self._GenerateOneMAC, ec_id)
189 a8083063 Iustin Pop
190 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
191 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
192 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
193 1862d460 Alexander Schreiber

194 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
195 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
196 1862d460 Alexander Schreiber

197 1862d460 Alexander Schreiber
    """
198 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
199 36b66e6e Guido Trotter
    if mac in all_macs:
200 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
201 36b66e6e Guido Trotter
    else:
202 36b66e6e Guido Trotter
      self._temporary_macs.Reserve(mac, ec_id)
203 1862d460 Alexander Schreiber
204 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
205 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
206 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
207 d8aee57e Iustin Pop

208 d8aee57e Iustin Pop
    @type lv_name: string
209 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
210 d8aee57e Iustin Pop

211 d8aee57e Iustin Pop
    """
212 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
213 d8aee57e Iustin Pop
    if lv_name in all_lvs:
214 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
215 d8aee57e Iustin Pop
    else:
216 d8aee57e Iustin Pop
      self._temporary_lvs.Reserve(lv_name, ec_id)
217 d8aee57e Iustin Pop
218 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
219 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
220 f9518d38 Iustin Pop
    """Generate a DRBD secret.
221 f9518d38 Iustin Pop

222 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
223 f9518d38 Iustin Pop

224 f9518d38 Iustin Pop
    """
225 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
226 afa1386e Guido Trotter
                                            utils.GenerateSecret,
227 afa1386e Guido Trotter
                                            ec_id)
228 8d9c3bef Michael Hanselmann
229 34e54ebc Iustin Pop
  def _AllLVs(self):
230 923b1523 Iustin Pop
    """Compute the list of all LVs.
231 923b1523 Iustin Pop

232 923b1523 Iustin Pop
    """
233 923b1523 Iustin Pop
    lvnames = set()
234 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
235 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
236 923b1523 Iustin Pop
      for lv_list in node_data.values():
237 923b1523 Iustin Pop
        lvnames.update(lv_list)
238 923b1523 Iustin Pop
    return lvnames
239 923b1523 Iustin Pop
240 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
241 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
242 34e54ebc Iustin Pop

243 34e54ebc Iustin Pop
    @type include_temporary: boolean
244 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
245 34e54ebc Iustin Pop
    @rtype: set
246 34e54ebc Iustin Pop
    @return: a set of IDs
247 34e54ebc Iustin Pop

248 34e54ebc Iustin Pop
    """
249 34e54ebc Iustin Pop
    existing = set()
250 34e54ebc Iustin Pop
    if include_temporary:
251 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
252 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
253 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
254 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
255 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
256 34e54ebc Iustin Pop
    return existing
257 34e54ebc Iustin Pop
258 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
259 430b923c Iustin Pop
    """Generate an unique UUID.
260 923b1523 Iustin Pop

261 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
262 923b1523 Iustin Pop
    duplicates.
263 923b1523 Iustin Pop

264 c41eea6e Iustin Pop
    @rtype: string
265 c41eea6e Iustin Pop
    @return: the unique id
266 923b1523 Iustin Pop

267 923b1523 Iustin Pop
    """
268 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
269 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
270 923b1523 Iustin Pop
271 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
272 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
273 430b923c Iustin Pop
    """Generate an unique ID.
274 430b923c Iustin Pop

275 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
276 430b923c Iustin Pop

277 4fae38c5 Guido Trotter
    @type ec_id: string
278 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
279 34d657ba Iustin Pop

280 34d657ba Iustin Pop
    """
281 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
282 34d657ba Iustin Pop
283 a8083063 Iustin Pop
  def _AllMACs(self):
284 a8083063 Iustin Pop
    """Return all MACs present in the config.
285 a8083063 Iustin Pop

286 c41eea6e Iustin Pop
    @rtype: list
287 c41eea6e Iustin Pop
    @return: the list of all MACs
288 c41eea6e Iustin Pop

289 a8083063 Iustin Pop
    """
290 a8083063 Iustin Pop
    result = []
291 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
292 a8083063 Iustin Pop
      for nic in instance.nics:
293 a8083063 Iustin Pop
        result.append(nic.mac)
294 a8083063 Iustin Pop
295 a8083063 Iustin Pop
    return result
296 a8083063 Iustin Pop
297 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
298 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
299 f9518d38 Iustin Pop

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

303 f9518d38 Iustin Pop
    """
304 f9518d38 Iustin Pop
    def helper(disk, result):
305 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
306 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
307 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
308 f9518d38 Iustin Pop
      if disk.children:
309 f9518d38 Iustin Pop
        for child in disk.children:
310 f9518d38 Iustin Pop
          helper(child, result)
311 f9518d38 Iustin Pop
312 f9518d38 Iustin Pop
    result = []
313 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
314 f9518d38 Iustin Pop
      for disk in instance.disks:
315 f9518d38 Iustin Pop
        helper(disk, result)
316 f9518d38 Iustin Pop
317 f9518d38 Iustin Pop
    return result
318 f9518d38 Iustin Pop
319 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
320 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
321 4b98ac29 Iustin Pop

322 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
323 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
324 4b98ac29 Iustin Pop
    @type l_ids: list
325 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
326 4b98ac29 Iustin Pop
    @type p_ids: list
327 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
328 4b98ac29 Iustin Pop
    @rtype: list
329 4b98ac29 Iustin Pop
    @return: a list of error messages
330 4b98ac29 Iustin Pop

331 4b98ac29 Iustin Pop
    """
332 4b98ac29 Iustin Pop
    result = []
333 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
334 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
335 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
336 25ae22e4 Iustin Pop
      else:
337 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
338 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
339 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
340 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
341 25ae22e4 Iustin Pop
      else:
342 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
343 4b98ac29 Iustin Pop
344 4b98ac29 Iustin Pop
    if disk.children:
345 4b98ac29 Iustin Pop
      for child in disk.children:
346 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
347 4b98ac29 Iustin Pop
    return result
348 4b98ac29 Iustin Pop
349 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
350 a8efbb40 Iustin Pop
    """Verify function.
351 a8efbb40 Iustin Pop

352 4a89c54a Iustin Pop
    @rtype: list
353 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
354 4a89c54a Iustin Pop
        configuration errors
355 4a89c54a Iustin Pop

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

512 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
513 4a89c54a Iustin Pop

514 4a89c54a Iustin Pop
    @rtype: list
515 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
516 4a89c54a Iustin Pop
        configuration errors
517 4a89c54a Iustin Pop

518 4a89c54a Iustin Pop
    """
519 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
520 4a89c54a Iustin Pop
521 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
522 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
523 a8083063 Iustin Pop

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

526 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
527 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
528 a8083063 Iustin Pop
    node.
529 a8083063 Iustin Pop

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

532 a8083063 Iustin Pop
    """
533 a8083063 Iustin Pop
    if disk.children:
534 a8083063 Iustin Pop
      for child in disk.children:
535 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
536 a8083063 Iustin Pop
537 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
538 a8083063 Iustin Pop
      return
539 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
540 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
541 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
542 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
543 3ecf6786 Iustin Pop
                                        node_name)
544 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
545 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
546 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
547 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
548 a8083063 Iustin Pop
                                        " for %s" % str(disk))
549 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
550 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
551 a8083063 Iustin Pop
      if pnode == node_name:
552 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
553 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
554 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
555 a8083063 Iustin Pop
    else:
556 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
557 a8083063 Iustin Pop
    return
558 a8083063 Iustin Pop
559 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
560 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
561 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
562 f78ede4e Guido Trotter

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

565 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
566 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
567 f78ede4e Guido Trotter
    node.
568 f78ede4e Guido Trotter

569 f78ede4e Guido Trotter
    """
570 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
571 f78ede4e Guido Trotter
572 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
573 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
574 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
575 b2fddf63 Iustin Pop

576 b2fddf63 Iustin Pop
    """
577 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
578 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
579 264bb3c5 Michael Hanselmann
580 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
581 264bb3c5 Michael Hanselmann
    self._WriteConfig()
582 264bb3c5 Michael Hanselmann
583 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
584 b2fddf63 Iustin Pop
  def GetPortList(self):
585 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
586 264bb3c5 Michael Hanselmann

587 264bb3c5 Michael Hanselmann
    """
588 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
589 264bb3c5 Michael Hanselmann
590 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
591 a8083063 Iustin Pop
  def AllocatePort(self):
592 a8083063 Iustin Pop
    """Allocate a port.
593 a8083063 Iustin Pop

594 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
595 b2fddf63 Iustin Pop
    default port range (and in this case we increase
596 b2fddf63 Iustin Pop
    highest_used_port).
597 a8083063 Iustin Pop

598 a8083063 Iustin Pop
    """
599 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
600 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
601 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
602 264bb3c5 Michael Hanselmann
    else:
603 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
604 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
605 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
606 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
607 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
608 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
609 a8083063 Iustin Pop
610 a8083063 Iustin Pop
    self._WriteConfig()
611 a8083063 Iustin Pop
    return port
612 a8083063 Iustin Pop
613 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
614 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
615 a81c53c9 Iustin Pop

616 4a89c54a Iustin Pop
    @rtype: (dict, list)
617 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
618 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
619 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
620 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
621 4a89c54a Iustin Pop
        should raise an exception
622 a81c53c9 Iustin Pop

623 a81c53c9 Iustin Pop
    """
624 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
625 4a89c54a Iustin Pop
      duplicates = []
626 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
627 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
628 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
629 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
630 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
631 a81c53c9 Iustin Pop
          if port in used[node]:
632 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
633 4a89c54a Iustin Pop
          else:
634 4a89c54a Iustin Pop
            used[node][port] = instance_name
635 a81c53c9 Iustin Pop
      if disk.children:
636 a81c53c9 Iustin Pop
        for child in disk.children:
637 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
638 4a89c54a Iustin Pop
      return duplicates
639 a81c53c9 Iustin Pop
640 4a89c54a Iustin Pop
    duplicates = []
641 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
642 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
643 79b26a7a Iustin Pop
      for disk in instance.disks:
644 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
645 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
646 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
647 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
648 4a89c54a Iustin Pop
      else:
649 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
650 4a89c54a Iustin Pop
    return my_dict, duplicates
651 a81c53c9 Iustin Pop
652 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
653 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
654 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
655 6d2e83d5 Iustin Pop

656 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
657 6d2e83d5 Iustin Pop

658 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
659 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
660 6d2e83d5 Iustin Pop
        an empty list).
661 6d2e83d5 Iustin Pop

662 6d2e83d5 Iustin Pop
    """
663 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
664 4a89c54a Iustin Pop
    if duplicates:
665 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
666 4a89c54a Iustin Pop
                                      str(duplicates))
667 4a89c54a Iustin Pop
    return d_map
668 6d2e83d5 Iustin Pop
669 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
670 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
671 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
672 a81c53c9 Iustin Pop

673 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
674 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
675 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
676 a81c53c9 Iustin Pop
    order as the passed nodes.
677 a81c53c9 Iustin Pop

678 32388e6d Iustin Pop
    @type instance: string
679 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
680 32388e6d Iustin Pop

681 a81c53c9 Iustin Pop
    """
682 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
683 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
684 32388e6d Iustin Pop
685 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
686 4a89c54a Iustin Pop
    if duplicates:
687 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
688 4a89c54a Iustin Pop
                                      str(duplicates))
689 a81c53c9 Iustin Pop
    result = []
690 a81c53c9 Iustin Pop
    for nname in nodes:
691 a81c53c9 Iustin Pop
      ndata = d_map[nname]
692 a81c53c9 Iustin Pop
      if not ndata:
693 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
694 a81c53c9 Iustin Pop
        result.append(0)
695 a81c53c9 Iustin Pop
        ndata[0] = instance
696 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
697 a81c53c9 Iustin Pop
        continue
698 a81c53c9 Iustin Pop
      keys = ndata.keys()
699 a81c53c9 Iustin Pop
      keys.sort()
700 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
701 a81c53c9 Iustin Pop
      if ffree is None:
702 a81c53c9 Iustin Pop
        # return the next minor
703 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
704 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
705 a81c53c9 Iustin Pop
      else:
706 a81c53c9 Iustin Pop
        minor = ffree
707 4a89c54a Iustin Pop
      # double-check minor against current instances
708 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
709 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
710 4a89c54a Iustin Pop
              " already allocated to instance %s" %
711 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
712 a81c53c9 Iustin Pop
      ndata[minor] = instance
713 4a89c54a Iustin Pop
      # double-check minor against reservation
714 4a89c54a Iustin Pop
      r_key = (nname, minor)
715 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
716 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
717 4a89c54a Iustin Pop
              " reserved for instance %s" %
718 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
719 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
720 4a89c54a Iustin Pop
      result.append(minor)
721 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
722 a81c53c9 Iustin Pop
                  nodes, result)
723 a81c53c9 Iustin Pop
    return result
724 a81c53c9 Iustin Pop
725 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
726 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
727 a81c53c9 Iustin Pop

728 a81c53c9 Iustin Pop
    @type instance: string
729 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
730 a81c53c9 Iustin Pop
                     released
731 a81c53c9 Iustin Pop

732 a81c53c9 Iustin Pop
    """
733 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
734 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
735 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
736 a81c53c9 Iustin Pop
      if name == instance:
737 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
738 a81c53c9 Iustin Pop
739 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
740 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
741 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
742 61cf6b5e Iustin Pop

743 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
744 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
745 61cf6b5e Iustin Pop
    functions.
746 61cf6b5e Iustin Pop

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

749 61cf6b5e Iustin Pop
    @type instance: string
750 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
751 61cf6b5e Iustin Pop
                     released
752 61cf6b5e Iustin Pop

753 61cf6b5e Iustin Pop
    """
754 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
755 61cf6b5e Iustin Pop
756 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
757 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
758 4a8b186a Michael Hanselmann
    """Get the configuration version.
759 4a8b186a Michael Hanselmann

760 4a8b186a Michael Hanselmann
    @return: Config version
761 4a8b186a Michael Hanselmann

762 4a8b186a Michael Hanselmann
    """
763 4a8b186a Michael Hanselmann
    return self._config_data.version
764 4a8b186a Michael Hanselmann
765 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
766 4a8b186a Michael Hanselmann
  def GetClusterName(self):
767 4a8b186a Michael Hanselmann
    """Get cluster name.
768 4a8b186a Michael Hanselmann

769 4a8b186a Michael Hanselmann
    @return: Cluster name
770 4a8b186a Michael Hanselmann

771 4a8b186a Michael Hanselmann
    """
772 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
773 4a8b186a Michael Hanselmann
774 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
775 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
776 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
777 4a8b186a Michael Hanselmann

778 4a8b186a Michael Hanselmann
    @return: Master hostname
779 4a8b186a Michael Hanselmann

780 4a8b186a Michael Hanselmann
    """
781 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
782 4a8b186a Michael Hanselmann
783 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
784 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
785 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
786 4a8b186a Michael Hanselmann

787 4a8b186a Michael Hanselmann
    @return: Master IP
788 4a8b186a Michael Hanselmann

789 4a8b186a Michael Hanselmann
    """
790 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
791 4a8b186a Michael Hanselmann
792 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
793 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
794 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
795 4a8b186a Michael Hanselmann

796 4a8b186a Michael Hanselmann
    """
797 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
798 4a8b186a Michael Hanselmann
799 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
800 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
801 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
802 4a8b186a Michael Hanselmann

803 4a8b186a Michael Hanselmann
    """
804 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
805 4a8b186a Michael Hanselmann
806 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
807 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
808 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
809 4a8b186a Michael Hanselmann

810 4a8b186a Michael Hanselmann
    """
811 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
812 4a8b186a Michael Hanselmann
813 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
814 a8083063 Iustin Pop
  def GetHostKey(self):
815 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
816 a8083063 Iustin Pop

817 c41eea6e Iustin Pop
    @rtype: string
818 c41eea6e Iustin Pop
    @return: the rsa hostkey
819 a8083063 Iustin Pop

820 a8083063 Iustin Pop
    """
821 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
822 a8083063 Iustin Pop
823 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
824 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
825 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
826 bf4af505 Apollon Oikonomopoulos

827 bf4af505 Apollon Oikonomopoulos
    """
828 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
829 bf4af505 Apollon Oikonomopoulos
830 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
831 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
832 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
833 868a98ca Manuel Franceschini

834 868a98ca Manuel Franceschini
    @return: primary ip family
835 868a98ca Manuel Franceschini

836 868a98ca Manuel Franceschini
    """
837 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
838 868a98ca Manuel Franceschini
839 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
840 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
841 a8083063 Iustin Pop
    """Add an instance to the config.
842 a8083063 Iustin Pop

843 a8083063 Iustin Pop
    This should be used after creating a new instance.
844 a8083063 Iustin Pop

845 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
846 c41eea6e Iustin Pop
    @param instance: the instance object
847 c41eea6e Iustin Pop

848 a8083063 Iustin Pop
    """
849 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
850 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
851 a8083063 Iustin Pop
852 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
853 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
854 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
855 923b1523 Iustin Pop
856 e4640214 Guido Trotter
    all_macs = self._AllMACs()
857 e4640214 Guido Trotter
    for nic in instance.nics:
858 e4640214 Guido Trotter
      if nic.mac in all_macs:
859 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
860 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
861 430b923c Iustin Pop
                                        (instance.name, nic.mac))
862 430b923c Iustin Pop
863 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
864 e4640214 Guido Trotter
865 b989e85d Iustin Pop
    instance.serial_no = 1
866 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
867 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
868 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
869 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
870 a8083063 Iustin Pop
    self._WriteConfig()
871 a8083063 Iustin Pop
872 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
873 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
874 430b923c Iustin Pop

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

878 430b923c Iustin Pop
    """
879 430b923c Iustin Pop
    if not item.uuid:
880 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
881 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
882 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
883 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
884 430b923c Iustin Pop
885 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
886 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
887 a8083063 Iustin Pop

888 a8083063 Iustin Pop
    """
889 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
890 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
891 a8083063 Iustin Pop
892 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
893 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
894 3ecf6786 Iustin Pop
                                      instance_name)
895 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
896 0d68c45d Iustin Pop
    if instance.admin_up != status:
897 0d68c45d Iustin Pop
      instance.admin_up = status
898 b989e85d Iustin Pop
      instance.serial_no += 1
899 d693c864 Iustin Pop
      instance.mtime = time.time()
900 455a3445 Iustin Pop
      self._WriteConfig()
901 a8083063 Iustin Pop
902 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
903 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
904 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
905 6a408fb2 Iustin Pop

906 6a408fb2 Iustin Pop
    """
907 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
908 6a408fb2 Iustin Pop
909 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
910 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
911 a8083063 Iustin Pop
    """Remove the instance from the configuration.
912 a8083063 Iustin Pop

913 a8083063 Iustin Pop
    """
914 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
915 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
916 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
917 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
918 a8083063 Iustin Pop
    self._WriteConfig()
919 a8083063 Iustin Pop
920 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
921 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
922 fc95f88f Iustin Pop
    """Rename an instance.
923 fc95f88f Iustin Pop

924 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
925 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
926 fc95f88f Iustin Pop
    rename.
927 fc95f88f Iustin Pop

928 fc95f88f Iustin Pop
    """
929 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
930 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
931 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
932 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
933 fc95f88f Iustin Pop
    inst.name = new_name
934 b23c4333 Manuel Franceschini
935 b23c4333 Manuel Franceschini
    for disk in inst.disks:
936 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
937 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
938 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
939 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
940 c4feafe8 Iustin Pop
                                              utils.PathJoin(file_storage_dir,
941 c4feafe8 Iustin Pop
                                                             inst.name,
942 c4feafe8 Iustin Pop
                                                             disk.iv_name))
943 b23c4333 Manuel Franceschini
944 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
945 fc95f88f Iustin Pop
    self._WriteConfig()
946 fc95f88f Iustin Pop
947 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
948 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
949 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
950 a8083063 Iustin Pop

951 a8083063 Iustin Pop
    """
952 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
953 a8083063 Iustin Pop
954 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
955 94bbfece Iustin Pop
    """Get the list of instances.
956 94bbfece Iustin Pop

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

959 94bbfece Iustin Pop
    """
960 94bbfece Iustin Pop
    return self._config_data.instances.keys()
961 94bbfece Iustin Pop
962 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
963 a8083063 Iustin Pop
  def GetInstanceList(self):
964 a8083063 Iustin Pop
    """Get the list of instances.
965 a8083063 Iustin Pop

966 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
967 c41eea6e Iustin Pop
        'instance1.example.com']
968 a8083063 Iustin Pop

969 a8083063 Iustin Pop
    """
970 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
971 a8083063 Iustin Pop
972 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
973 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
974 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
975 a8083063 Iustin Pop

976 a8083063 Iustin Pop
    """
977 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
978 bcdf16d7 Guido Trotter
                                    self._config_data.instances.keys(),
979 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
980 a8083063 Iustin Pop
981 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
982 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
983 94bbfece Iustin Pop

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

986 94bbfece Iustin Pop
    """
987 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
988 94bbfece Iustin Pop
      return None
989 94bbfece Iustin Pop
990 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
991 94bbfece Iustin Pop
992 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
993 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
994 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
995 a8083063 Iustin Pop

996 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
997 a8083063 Iustin Pop
    an instance are taken from the live systems.
998 a8083063 Iustin Pop

999 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1000 c41eea6e Iustin Pop
        I{instance1.example.com}
1001 a8083063 Iustin Pop

1002 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1003 c41eea6e Iustin Pop
    @return: the instance object
1004 a8083063 Iustin Pop

1005 a8083063 Iustin Pop
    """
1006 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1007 a8083063 Iustin Pop
1008 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1009 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1010 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1011 0b2de758 Iustin Pop

1012 0b2de758 Iustin Pop
    @rtype: dict
1013 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1014 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1015 0b2de758 Iustin Pop

1016 0b2de758 Iustin Pop
    """
1017 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1018 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1019 0b2de758 Iustin Pop
    return my_dict
1020 0b2de758 Iustin Pop
1021 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1022 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1023 a8083063 Iustin Pop
    """Add a node to the configuration.
1024 a8083063 Iustin Pop

1025 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1026 c41eea6e Iustin Pop
    @param node: a Node instance
1027 a8083063 Iustin Pop

1028 a8083063 Iustin Pop
    """
1029 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1030 d8470559 Michael Hanselmann
1031 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1032 430b923c Iustin Pop
1033 b989e85d Iustin Pop
    node.serial_no = 1
1034 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1035 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1036 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1037 a8083063 Iustin Pop
    self._WriteConfig()
1038 a8083063 Iustin Pop
1039 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1040 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1041 a8083063 Iustin Pop
    """Remove a node from the configuration.
1042 a8083063 Iustin Pop

1043 a8083063 Iustin Pop
    """
1044 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1045 d8470559 Michael Hanselmann
1046 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1047 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1048 a8083063 Iustin Pop
1049 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1050 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1051 a8083063 Iustin Pop
    self._WriteConfig()
1052 a8083063 Iustin Pop
1053 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1054 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1055 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1056 a8083063 Iustin Pop

1057 a8083063 Iustin Pop
    """
1058 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1059 bcdf16d7 Guido Trotter
                                    self._config_data.nodes.keys(),
1060 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1061 a8083063 Iustin Pop
1062 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1063 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1064 a8083063 Iustin Pop

1065 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1066 c41eea6e Iustin Pop
    held.
1067 f78ede4e Guido Trotter

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

1070 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1071 c41eea6e Iustin Pop
    @return: the node object
1072 a8083063 Iustin Pop

1073 a8083063 Iustin Pop
    """
1074 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1075 a8083063 Iustin Pop
      return None
1076 a8083063 Iustin Pop
1077 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1078 a8083063 Iustin Pop
1079 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1080 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1081 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1082 f78ede4e Guido Trotter

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

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

1087 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1088 c41eea6e Iustin Pop
    @return: the node object
1089 f78ede4e Guido Trotter

1090 f78ede4e Guido Trotter
    """
1091 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1092 f78ede4e Guido Trotter
1093 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1094 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1095 a8083063 Iustin Pop

1096 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1097 c41eea6e Iustin Pop
    held.
1098 c41eea6e Iustin Pop

1099 c41eea6e Iustin Pop
    @rtype: list
1100 f78ede4e Guido Trotter

1101 a8083063 Iustin Pop
    """
1102 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1103 a8083063 Iustin Pop
1104 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1105 f78ede4e Guido Trotter
  def GetNodeList(self):
1106 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1107 f78ede4e Guido Trotter

1108 f78ede4e Guido Trotter
    """
1109 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1110 f78ede4e Guido Trotter
1111 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1112 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1113 94a02bb5 Iustin Pop

1114 94a02bb5 Iustin Pop
    """
1115 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1116 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1117 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1118 94a02bb5 Iustin Pop
1119 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1120 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1121 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1122 6819dc49 Iustin Pop

1123 6819dc49 Iustin Pop
    """
1124 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1125 6819dc49 Iustin Pop
1126 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1127 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1128 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1129 d65e5776 Iustin Pop

1130 d65e5776 Iustin Pop
    @rtype: dict
1131 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1132 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1133 d65e5776 Iustin Pop

1134 d65e5776 Iustin Pop
    """
1135 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1136 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1137 d65e5776 Iustin Pop
    return my_dict
1138 d65e5776 Iustin Pop
1139 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1140 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1141 ec0292f1 Iustin Pop

1142 23f06b2b Iustin Pop
    @type exceptions: list
1143 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1144 ec0292f1 Iustin Pop
    @rtype: tuple
1145 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1146 ec0292f1 Iustin Pop

1147 ec0292f1 Iustin Pop
    """
1148 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1149 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1150 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1151 23f06b2b Iustin Pop
        continue
1152 5bf07049 Iustin Pop
      if not (node.offline or node.drained):
1153 ec0292f1 Iustin Pop
        mc_max += 1
1154 ec0292f1 Iustin Pop
      if node.master_candidate:
1155 ec0292f1 Iustin Pop
        mc_now += 1
1156 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1157 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1158 ec0292f1 Iustin Pop
1159 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1160 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1161 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1162 ec0292f1 Iustin Pop

1163 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1164 ec0292f1 Iustin Pop

1165 23f06b2b Iustin Pop
    @type exceptions: list
1166 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1167 ec0292f1 Iustin Pop
    @rtype: tuple
1168 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1169 ec0292f1 Iustin Pop

1170 ec0292f1 Iustin Pop
    """
1171 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1172 ec0292f1 Iustin Pop
1173 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1174 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1175 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1176 ec0292f1 Iustin Pop

1177 44485f49 Guido Trotter
    @type exceptions: list
1178 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1179 ec0292f1 Iustin Pop
    @rtype: list
1180 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1181 ec0292f1 Iustin Pop

1182 ec0292f1 Iustin Pop
    """
1183 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1184 ec0292f1 Iustin Pop
    mod_list = []
1185 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1186 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1187 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1188 ec0292f1 Iustin Pop
      for name in node_list:
1189 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1190 ec0292f1 Iustin Pop
          break
1191 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1192 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1193 44485f49 Guido Trotter
            node.name in exceptions):
1194 ec0292f1 Iustin Pop
          continue
1195 ee513a66 Iustin Pop
        mod_list.append(node)
1196 ec0292f1 Iustin Pop
        node.master_candidate = True
1197 ec0292f1 Iustin Pop
        node.serial_no += 1
1198 ec0292f1 Iustin Pop
        mc_now += 1
1199 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1200 ec0292f1 Iustin Pop
        # this should not happen
1201 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1202 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1203 ec0292f1 Iustin Pop
      if mod_list:
1204 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1205 ec0292f1 Iustin Pop
        self._WriteConfig()
1206 ec0292f1 Iustin Pop
1207 ec0292f1 Iustin Pop
    return mod_list
1208 ec0292f1 Iustin Pop
1209 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1210 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1211 a8083063 Iustin Pop

1212 a8083063 Iustin Pop
    """
1213 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1214 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1215 a8083063 Iustin Pop
1216 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1217 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1218 76d5d3a3 Iustin Pop

1219 76d5d3a3 Iustin Pop
    """
1220 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1221 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1222 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1223 76d5d3a3 Iustin Pop
1224 a8083063 Iustin Pop
  def _OpenConfig(self):
1225 a8083063 Iustin Pop
    """Read the config data from disk.
1226 a8083063 Iustin Pop

1227 a8083063 Iustin Pop
    """
1228 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1229 13998ef2 Michael Hanselmann
1230 a8083063 Iustin Pop
    try:
1231 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1232 13998ef2 Michael Hanselmann
    except Exception, err:
1233 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1234 5b263ed7 Michael Hanselmann
1235 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1236 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1237 5b263ed7 Michael Hanselmann
1238 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1239 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1240 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1241 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1242 90d726a8 Iustin Pop
1243 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1244 90d726a8 Iustin Pop
    data.UpgradeConfig()
1245 90d726a8 Iustin Pop
1246 a8083063 Iustin Pop
    self._config_data = data
1247 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1248 0779e3aa Iustin Pop
    # ssconf update
1249 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1250 a8083063 Iustin Pop
1251 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1252 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1253 76d5d3a3 Iustin Pop
1254 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1255 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1256 76d5d3a3 Iustin Pop

1257 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1258 76d5d3a3 Iustin Pop
    whole configuration, etc.
1259 76d5d3a3 Iustin Pop

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

1266 76d5d3a3 Iustin Pop
    """
1267 76d5d3a3 Iustin Pop
    modified = False
1268 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1269 76d5d3a3 Iustin Pop
      if item.uuid is None:
1270 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1271 76d5d3a3 Iustin Pop
        modified = True
1272 76d5d3a3 Iustin Pop
    if modified:
1273 76d5d3a3 Iustin Pop
      self._WriteConfig()
1274 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1275 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1276 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1277 4fae38c5 Guido Trotter
1278 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1279 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1280 a8083063 Iustin Pop

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

1284 a8083063 Iustin Pop
    """
1285 a8083063 Iustin Pop
    if self._offline:
1286 a8083063 Iustin Pop
      return True
1287 a4eae71f Michael Hanselmann
1288 a8083063 Iustin Pop
    bad = False
1289 a8083063 Iustin Pop
1290 6a5b8b4b Iustin Pop
    node_list = []
1291 6a5b8b4b Iustin Pop
    addr_list = []
1292 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1293 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1294 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1295 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1296 6b294c53 Iustin Pop
    # in between
1297 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1298 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1299 6a5b8b4b Iustin Pop
        continue
1300 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1301 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1302 6a5b8b4b Iustin Pop
        continue
1303 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1304 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1305 6b294c53 Iustin Pop
1306 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1307 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1308 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1309 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1310 1b54fc6c Guido Trotter
      if msg:
1311 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1312 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1313 1b54fc6c Guido Trotter
        logging.error(msg)
1314 a4eae71f Michael Hanselmann
1315 a4eae71f Michael Hanselmann
        if feedback_fn:
1316 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1317 a4eae71f Michael Hanselmann
1318 a8083063 Iustin Pop
        bad = True
1319 a4eae71f Michael Hanselmann
1320 a8083063 Iustin Pop
    return not bad
1321 a8083063 Iustin Pop
1322 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1323 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1324 a8083063 Iustin Pop

1325 a8083063 Iustin Pop
    """
1326 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
1327 a4eae71f Michael Hanselmann
1328 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
1329 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
1330 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
1331 d2231b8c Iustin Pop
    # recovery to the user
1332 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1333 4a89c54a Iustin Pop
    if config_errors:
1334 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
1335 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
1336 d2231b8c Iustin Pop
      logging.critical(errmsg)
1337 d2231b8c Iustin Pop
      if feedback_fn:
1338 d2231b8c Iustin Pop
        feedback_fn(errmsg)
1339 d2231b8c Iustin Pop
1340 a8083063 Iustin Pop
    if destination is None:
1341 a8083063 Iustin Pop
      destination = self._cfg_file
1342 a8083063 Iustin Pop
    self._BumpSerialNo()
1343 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1344 13998ef2 Michael Hanselmann
1345 13998ef2 Michael Hanselmann
    utils.WriteFile(destination, data=txt)
1346 13998ef2 Michael Hanselmann
1347 14e15659 Iustin Pop
    self.write_count += 1
1348 3d3a04bc Iustin Pop
1349 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
1350 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
1351 a8083063 Iustin Pop
1352 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
1353 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
1354 d9a855f1 Michael Hanselmann
      if not self._offline:
1355 cd34faf2 Michael Hanselmann
        result = rpc.RpcRunner.call_write_ssconf_files(
1356 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
1357 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
1358 a4eae71f Michael Hanselmann
1359 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
1360 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
1361 e1e75d00 Iustin Pop
          if msg:
1362 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
1363 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
1364 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
1365 a4eae71f Michael Hanselmann
1366 a4eae71f Michael Hanselmann
            if feedback_fn:
1367 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
1368 a4eae71f Michael Hanselmann
1369 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
1370 54d1a06e Michael Hanselmann
1371 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
1372 054596f0 Iustin Pop
    """Return the values needed by ssconf.
1373 054596f0 Iustin Pop

1374 054596f0 Iustin Pop
    @rtype: dict
1375 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1376 054596f0 Iustin Pop
        associated value
1377 054596f0 Iustin Pop

1378 054596f0 Iustin Pop
    """
1379 a3316e4a Iustin Pop
    fn = "\n".join
1380 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1381 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1382 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1383 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1384 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1385 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1386 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1387 a3316e4a Iustin Pop
1388 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1389 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1390 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1391 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1392 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1393 8113a52e Luca Bigliardi
                     if node.master_candidate)
1394 a3316e4a Iustin Pop
    node_data = fn(node_names)
1395 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1396 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1397 f56618e0 Iustin Pop
1398 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1399 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1400 4f7a6a10 Iustin Pop
1401 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
1402 4f7a6a10 Iustin Pop
1403 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
1404 0fbae49a Balazs Lecz
1405 03d1dba2 Michael Hanselmann
    return {
1406 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1407 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1408 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1409 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1410 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1411 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1412 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1413 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1414 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1415 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1416 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1417 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1418 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1419 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
1420 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1421 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1422 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
1423 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
1424 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
1425 03d1dba2 Michael Hanselmann
      }
1426 03d1dba2 Michael Hanselmann
1427 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1428 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
1429 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
1430 d367b66c Manuel Franceschini

1431 d367b66c Manuel Franceschini
    """
1432 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
1433 d367b66c Manuel Franceschini
1434 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1435 a8083063 Iustin Pop
  def GetVGName(self):
1436 a8083063 Iustin Pop
    """Return the volume group name.
1437 a8083063 Iustin Pop

1438 a8083063 Iustin Pop
    """
1439 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1440 a8083063 Iustin Pop
1441 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1442 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1443 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1444 89ff8e15 Manuel Franceschini

1445 89ff8e15 Manuel Franceschini
    """
1446 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1447 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1448 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1449 89ff8e15 Manuel Franceschini
1450 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1451 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
1452 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
1453 9e33896b Luca Bigliardi

1454 9e33896b Luca Bigliardi
    """
1455 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
1456 9e33896b Luca Bigliardi
1457 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
1458 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
1459 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
1460 9e33896b Luca Bigliardi

1461 9e33896b Luca Bigliardi
    """
1462 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
1463 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
1464 9e33896b Luca Bigliardi
    self._WriteConfig()
1465 9e33896b Luca Bigliardi
1466 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1467 a8083063 Iustin Pop
  def GetMACPrefix(self):
1468 a8083063 Iustin Pop
    """Return the mac prefix.
1469 a8083063 Iustin Pop

1470 a8083063 Iustin Pop
    """
1471 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1472 62779dd0 Iustin Pop
1473 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1474 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1475 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1476 62779dd0 Iustin Pop

1477 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1478 c41eea6e Iustin Pop
    @return: the cluster object
1479 62779dd0 Iustin Pop

1480 62779dd0 Iustin Pop
    """
1481 62779dd0 Iustin Pop
    return self._config_data.cluster
1482 e00fb268 Iustin Pop
1483 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1484 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
1485 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
1486 51cb1581 Luca Bigliardi

1487 51cb1581 Luca Bigliardi
    """
1488 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
1489 51cb1581 Luca Bigliardi
1490 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1491 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1492 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1493 e00fb268 Iustin Pop

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

1500 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1501 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1502 c41eea6e Iustin Pop
        the cluster
1503 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1504 c41eea6e Iustin Pop

1505 e00fb268 Iustin Pop
    """
1506 e00fb268 Iustin Pop
    if self._config_data is None:
1507 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1508 3ecf6786 Iustin Pop
                                   " cannot save.")
1509 f34901f8 Iustin Pop
    update_serial = False
1510 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1511 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1512 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1513 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1514 f34901f8 Iustin Pop
      update_serial = True
1515 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1516 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1517 e00fb268 Iustin Pop
    else:
1518 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1519 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1520 e00fb268 Iustin Pop
    if not test:
1521 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1522 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1523 f34901f8 Iustin Pop
    target.serial_no += 1
1524 d693c864 Iustin Pop
    target.mtime = now = time.time()
1525 f34901f8 Iustin Pop
1526 cff4c037 Iustin Pop
    if update_serial:
1527 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1528 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1529 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1530 b989e85d Iustin Pop
1531 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1532 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1533 61cf6b5e Iustin Pop
1534 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
1535 73064714 Guido Trotter
1536 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
1537 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
1538 73064714 Guido Trotter
    """Drop per-execution-context reservations
1539 73064714 Guido Trotter

1540 73064714 Guido Trotter
    """
1541 d8aee57e Iustin Pop
    for rm in self._all_rms:
1542 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)