Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ f38ea602

History | View | Annotate | Download (49.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 a8083063 Iustin Pop
import os
35 a8083063 Iustin Pop
import random
36 d8470559 Michael Hanselmann
import logging
37 d693c864 Iustin Pop
import time
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
from ganeti import errors
40 f78ede4e Guido Trotter
from ganeti import locking
41 a8083063 Iustin Pop
from ganeti import utils
42 a8083063 Iustin Pop
from ganeti import constants
43 a8083063 Iustin Pop
from ganeti import rpc
44 a8083063 Iustin Pop
from ganeti import objects
45 8d14b30d Iustin Pop
from ganeti import serializer
46 0fbae49a Balazs Lecz
from ganeti import uidpool
47 a744b676 Manuel Franceschini
from ganeti import netutils
48 243cdbcc Michael Hanselmann
49 243cdbcc Michael Hanselmann
50 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
51 f78ede4e Guido Trotter
52 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
53 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
54 4fae38c5 Guido Trotter
55 f78ede4e Guido Trotter
56 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
57 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
58 c41eea6e Iustin Pop

59 c41eea6e Iustin Pop
  This only verifies the version of the configuration.
60 c41eea6e Iustin Pop

61 c41eea6e Iustin Pop
  @raise errors.ConfigurationError: if the version differs from what
62 c41eea6e Iustin Pop
      we expect
63 c41eea6e Iustin Pop

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

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

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

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

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

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

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

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

181 a8083063 Iustin Pop
    This should check the current instances for duplicates.
182 a8083063 Iustin Pop

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

191 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
192 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
193 1862d460 Alexander Schreiber

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

205 d8aee57e Iustin Pop
    @type lv_name: string
206 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
207 d8aee57e Iustin Pop

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

219 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
220 f9518d38 Iustin Pop

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

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

240 34e54ebc Iustin Pop
    @type include_temporary: boolean
241 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
242 34e54ebc Iustin Pop
    @rtype: set
243 34e54ebc Iustin Pop
    @return: a set of IDs
244 34e54ebc Iustin Pop

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

258 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
259 923b1523 Iustin Pop
    duplicates.
260 923b1523 Iustin Pop

261 c41eea6e Iustin Pop
    @rtype: string
262 c41eea6e Iustin Pop
    @return: the unique id
263 923b1523 Iustin Pop

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

272 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
273 430b923c Iustin Pop

274 4fae38c5 Guido Trotter
    @type ec_id: string
275 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
276 34d657ba Iustin Pop

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

283 c41eea6e Iustin Pop
    @rtype: list
284 c41eea6e Iustin Pop
    @return: the list of all MACs
285 c41eea6e Iustin Pop

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

297 c41eea6e Iustin Pop
    @rtype: list
298 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
299 c41eea6e Iustin Pop

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

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

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

349 4a89c54a Iustin Pop
    @rtype: list
350 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
351 4a89c54a Iustin Pop
        configuration errors
352 4a89c54a Iustin Pop

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

509 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
510 4a89c54a Iustin Pop

511 4a89c54a Iustin Pop
    @rtype: list
512 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
513 4a89c54a Iustin Pop
        configuration errors
514 4a89c54a Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

653 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
654 6d2e83d5 Iustin Pop

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

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

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

675 32388e6d Iustin Pop
    @type instance: string
676 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
677 32388e6d Iustin Pop

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

725 a81c53c9 Iustin Pop
    @type instance: string
726 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
727 a81c53c9 Iustin Pop
                     released
728 a81c53c9 Iustin Pop

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

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

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

746 61cf6b5e Iustin Pop
    @type instance: string
747 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
748 61cf6b5e Iustin Pop
                     released
749 61cf6b5e Iustin Pop

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

757 4a8b186a Michael Hanselmann
    @return: Config version
758 4a8b186a Michael Hanselmann

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

766 4a8b186a Michael Hanselmann
    @return: Cluster name
767 4a8b186a Michael Hanselmann

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

775 4a8b186a Michael Hanselmann
    @return: Master hostname
776 4a8b186a Michael Hanselmann

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

784 4a8b186a Michael Hanselmann
    @return: Master IP
785 4a8b186a Michael Hanselmann

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

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

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

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

814 c41eea6e Iustin Pop
    @rtype: string
815 c41eea6e Iustin Pop
    @return: the rsa hostkey
816 a8083063 Iustin Pop

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

824 bf4af505 Apollon Oikonomopoulos
    """
825 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
826 bf4af505 Apollon Oikonomopoulos
827 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
828 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
829 a8083063 Iustin Pop
    """Add an instance to the config.
830 a8083063 Iustin Pop

831 a8083063 Iustin Pop
    This should be used after creating a new instance.
832 a8083063 Iustin Pop

833 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
834 c41eea6e Iustin Pop
    @param instance: the instance object
835 c41eea6e Iustin Pop

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

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

866 430b923c Iustin Pop
    """
867 430b923c Iustin Pop
    if not item.uuid:
868 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
869 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
870 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
871 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
872 430b923c Iustin Pop
873 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
874 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
875 a8083063 Iustin Pop

876 a8083063 Iustin Pop
    """
877 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
878 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
879 a8083063 Iustin Pop
880 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
881 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
882 3ecf6786 Iustin Pop
                                      instance_name)
883 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
884 0d68c45d Iustin Pop
    if instance.admin_up != status:
885 0d68c45d Iustin Pop
      instance.admin_up = status
886 b989e85d Iustin Pop
      instance.serial_no += 1
887 d693c864 Iustin Pop
      instance.mtime = time.time()
888 455a3445 Iustin Pop
      self._WriteConfig()
889 a8083063 Iustin Pop
890 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
891 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
892 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
893 6a408fb2 Iustin Pop

894 6a408fb2 Iustin Pop
    """
895 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
896 6a408fb2 Iustin Pop
897 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
898 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
899 a8083063 Iustin Pop
    """Remove the instance from the configuration.
900 a8083063 Iustin Pop

901 a8083063 Iustin Pop
    """
902 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
903 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
904 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
905 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
906 a8083063 Iustin Pop
    self._WriteConfig()
907 a8083063 Iustin Pop
908 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
909 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
910 fc95f88f Iustin Pop
    """Rename an instance.
911 fc95f88f Iustin Pop

912 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
913 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
914 fc95f88f Iustin Pop
    rename.
915 fc95f88f Iustin Pop

916 fc95f88f Iustin Pop
    """
917 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
918 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
919 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
920 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
921 fc95f88f Iustin Pop
    inst.name = new_name
922 b23c4333 Manuel Franceschini
923 b23c4333 Manuel Franceschini
    for disk in inst.disks:
924 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
925 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
926 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
927 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
928 c4feafe8 Iustin Pop
                                              utils.PathJoin(file_storage_dir,
929 c4feafe8 Iustin Pop
                                                             inst.name,
930 c4feafe8 Iustin Pop
                                                             disk.iv_name))
931 b23c4333 Manuel Franceschini
932 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
933 fc95f88f Iustin Pop
    self._WriteConfig()
934 fc95f88f Iustin Pop
935 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
936 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
937 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
938 a8083063 Iustin Pop

939 a8083063 Iustin Pop
    """
940 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
941 a8083063 Iustin Pop
942 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
943 94bbfece Iustin Pop
    """Get the list of instances.
944 94bbfece Iustin Pop

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

947 94bbfece Iustin Pop
    """
948 94bbfece Iustin Pop
    return self._config_data.instances.keys()
949 94bbfece Iustin Pop
950 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
951 a8083063 Iustin Pop
  def GetInstanceList(self):
952 a8083063 Iustin Pop
    """Get the list of instances.
953 a8083063 Iustin Pop

954 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
955 c41eea6e Iustin Pop
        'instance1.example.com']
956 a8083063 Iustin Pop

957 a8083063 Iustin Pop
    """
958 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
959 a8083063 Iustin Pop
960 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
961 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
962 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
963 a8083063 Iustin Pop

964 a8083063 Iustin Pop
    """
965 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
966 bcdf16d7 Guido Trotter
                                    self._config_data.instances.keys(),
967 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
968 a8083063 Iustin Pop
969 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
970 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
971 94bbfece Iustin Pop

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

974 94bbfece Iustin Pop
    """
975 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
976 94bbfece Iustin Pop
      return None
977 94bbfece Iustin Pop
978 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
979 94bbfece Iustin Pop
980 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
981 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
982 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
983 a8083063 Iustin Pop

984 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
985 a8083063 Iustin Pop
    an instance are taken from the live systems.
986 a8083063 Iustin Pop

987 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
988 c41eea6e Iustin Pop
        I{instance1.example.com}
989 a8083063 Iustin Pop

990 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
991 c41eea6e Iustin Pop
    @return: the instance object
992 a8083063 Iustin Pop

993 a8083063 Iustin Pop
    """
994 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
995 a8083063 Iustin Pop
996 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
997 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
998 0b2de758 Iustin Pop
    """Get the configuration of all instances.
999 0b2de758 Iustin Pop

1000 0b2de758 Iustin Pop
    @rtype: dict
1001 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1002 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1003 0b2de758 Iustin Pop

1004 0b2de758 Iustin Pop
    """
1005 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1006 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1007 0b2de758 Iustin Pop
    return my_dict
1008 0b2de758 Iustin Pop
1009 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1010 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1011 a8083063 Iustin Pop
    """Add a node to the configuration.
1012 a8083063 Iustin Pop

1013 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1014 c41eea6e Iustin Pop
    @param node: a Node instance
1015 a8083063 Iustin Pop

1016 a8083063 Iustin Pop
    """
1017 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1018 d8470559 Michael Hanselmann
1019 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1020 430b923c Iustin Pop
1021 b989e85d Iustin Pop
    node.serial_no = 1
1022 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1023 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1024 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1025 a8083063 Iustin Pop
    self._WriteConfig()
1026 a8083063 Iustin Pop
1027 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1028 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1029 a8083063 Iustin Pop
    """Remove a node from the configuration.
1030 a8083063 Iustin Pop

1031 a8083063 Iustin Pop
    """
1032 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1033 d8470559 Michael Hanselmann
1034 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1035 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1036 a8083063 Iustin Pop
1037 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1038 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1039 a8083063 Iustin Pop
    self._WriteConfig()
1040 a8083063 Iustin Pop
1041 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1042 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1043 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1044 a8083063 Iustin Pop

1045 a8083063 Iustin Pop
    """
1046 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1047 bcdf16d7 Guido Trotter
                                    self._config_data.nodes.keys(),
1048 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1049 a8083063 Iustin Pop
1050 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1051 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1052 a8083063 Iustin Pop

1053 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1054 c41eea6e Iustin Pop
    held.
1055 f78ede4e Guido Trotter

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

1058 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1059 c41eea6e Iustin Pop
    @return: the node object
1060 a8083063 Iustin Pop

1061 a8083063 Iustin Pop
    """
1062 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1063 a8083063 Iustin Pop
      return None
1064 a8083063 Iustin Pop
1065 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1066 a8083063 Iustin Pop
1067 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1068 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1069 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1070 f78ede4e Guido Trotter

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

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

1075 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1076 c41eea6e Iustin Pop
    @return: the node object
1077 f78ede4e Guido Trotter

1078 f78ede4e Guido Trotter
    """
1079 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1080 f78ede4e Guido Trotter
1081 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1082 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1083 a8083063 Iustin Pop

1084 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1085 c41eea6e Iustin Pop
    held.
1086 c41eea6e Iustin Pop

1087 c41eea6e Iustin Pop
    @rtype: list
1088 f78ede4e Guido Trotter

1089 a8083063 Iustin Pop
    """
1090 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1091 a8083063 Iustin Pop
1092 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1093 f78ede4e Guido Trotter
  def GetNodeList(self):
1094 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1095 f78ede4e Guido Trotter

1096 f78ede4e Guido Trotter
    """
1097 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1098 f78ede4e Guido Trotter
1099 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1100 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1101 94a02bb5 Iustin Pop

1102 94a02bb5 Iustin Pop
    """
1103 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1104 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1105 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1106 94a02bb5 Iustin Pop
1107 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1108 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1109 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1110 6819dc49 Iustin Pop

1111 6819dc49 Iustin Pop
    """
1112 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1113 6819dc49 Iustin Pop
1114 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1115 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1116 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1117 d65e5776 Iustin Pop

1118 d65e5776 Iustin Pop
    @rtype: dict
1119 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1120 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1121 d65e5776 Iustin Pop

1122 d65e5776 Iustin Pop
    """
1123 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1124 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1125 d65e5776 Iustin Pop
    return my_dict
1126 d65e5776 Iustin Pop
1127 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1128 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1129 ec0292f1 Iustin Pop

1130 23f06b2b Iustin Pop
    @type exceptions: list
1131 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1132 ec0292f1 Iustin Pop
    @rtype: tuple
1133 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1134 ec0292f1 Iustin Pop

1135 ec0292f1 Iustin Pop
    """
1136 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1137 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1138 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1139 23f06b2b Iustin Pop
        continue
1140 5bf07049 Iustin Pop
      if not (node.offline or node.drained):
1141 ec0292f1 Iustin Pop
        mc_max += 1
1142 ec0292f1 Iustin Pop
      if node.master_candidate:
1143 ec0292f1 Iustin Pop
        mc_now += 1
1144 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1145 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1146 ec0292f1 Iustin Pop
1147 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1148 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1149 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1150 ec0292f1 Iustin Pop

1151 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1152 ec0292f1 Iustin Pop

1153 23f06b2b Iustin Pop
    @type exceptions: list
1154 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1155 ec0292f1 Iustin Pop
    @rtype: tuple
1156 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1157 ec0292f1 Iustin Pop

1158 ec0292f1 Iustin Pop
    """
1159 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1160 ec0292f1 Iustin Pop
1161 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1162 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1163 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1164 ec0292f1 Iustin Pop

1165 44485f49 Guido Trotter
    @type exceptions: list
1166 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1167 ec0292f1 Iustin Pop
    @rtype: list
1168 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1169 ec0292f1 Iustin Pop

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

1200 a8083063 Iustin Pop
    """
1201 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1202 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1203 a8083063 Iustin Pop
1204 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1205 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1206 76d5d3a3 Iustin Pop

1207 76d5d3a3 Iustin Pop
    """
1208 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1209 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1210 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1211 76d5d3a3 Iustin Pop
1212 a8083063 Iustin Pop
  def _OpenConfig(self):
1213 a8083063 Iustin Pop
    """Read the config data from disk.
1214 a8083063 Iustin Pop

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

1245 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1246 76d5d3a3 Iustin Pop
    whole configuration, etc.
1247 76d5d3a3 Iustin Pop

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

1254 76d5d3a3 Iustin Pop
    """
1255 76d5d3a3 Iustin Pop
    modified = False
1256 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1257 76d5d3a3 Iustin Pop
      if item.uuid is None:
1258 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1259 76d5d3a3 Iustin Pop
        modified = True
1260 76d5d3a3 Iustin Pop
    if modified:
1261 76d5d3a3 Iustin Pop
      self._WriteConfig()
1262 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1263 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1264 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1265 4fae38c5 Guido Trotter
1266 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1267 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1268 a8083063 Iustin Pop

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

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

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

1362 054596f0 Iustin Pop
    @rtype: dict
1363 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1364 054596f0 Iustin Pop
        associated value
1365 054596f0 Iustin Pop

1366 054596f0 Iustin Pop
    """
1367 a3316e4a Iustin Pop
    fn = "\n".join
1368 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1369 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1370 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1371 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1372 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1373 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1374 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1375 a3316e4a Iustin Pop
1376 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1377 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1378 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1379 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1380 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1381 8113a52e Luca Bigliardi
                     if node.master_candidate)
1382 a3316e4a Iustin Pop
    node_data = fn(node_names)
1383 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1384 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1385 f56618e0 Iustin Pop
1386 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1387 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1388 4f7a6a10 Iustin Pop
1389 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
1390 4f7a6a10 Iustin Pop
1391 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
1392 0fbae49a Balazs Lecz
1393 03d1dba2 Michael Hanselmann
    return {
1394 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1395 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1396 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1397 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1398 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1399 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1400 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1401 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1402 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1403 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1404 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1405 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1406 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1407 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1408 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1409 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
1410 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
1411 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
1412 03d1dba2 Michael Hanselmann
      }
1413 03d1dba2 Michael Hanselmann
1414 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1415 a8083063 Iustin Pop
  def GetVGName(self):
1416 a8083063 Iustin Pop
    """Return the volume group name.
1417 a8083063 Iustin Pop

1418 a8083063 Iustin Pop
    """
1419 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1420 a8083063 Iustin Pop
1421 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1422 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1423 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1424 89ff8e15 Manuel Franceschini

1425 89ff8e15 Manuel Franceschini
    """
1426 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1427 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1428 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1429 89ff8e15 Manuel Franceschini
1430 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1431 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
1432 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
1433 9e33896b Luca Bigliardi

1434 9e33896b Luca Bigliardi
    """
1435 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
1436 9e33896b Luca Bigliardi
1437 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
1438 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
1439 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
1440 9e33896b Luca Bigliardi

1441 9e33896b Luca Bigliardi
    """
1442 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
1443 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
1444 9e33896b Luca Bigliardi
    self._WriteConfig()
1445 9e33896b Luca Bigliardi
1446 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1447 a8083063 Iustin Pop
  def GetMACPrefix(self):
1448 a8083063 Iustin Pop
    """Return the mac prefix.
1449 a8083063 Iustin Pop

1450 a8083063 Iustin Pop
    """
1451 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1452 62779dd0 Iustin Pop
1453 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1454 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1455 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1456 62779dd0 Iustin Pop

1457 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1458 c41eea6e Iustin Pop
    @return: the cluster object
1459 62779dd0 Iustin Pop

1460 62779dd0 Iustin Pop
    """
1461 62779dd0 Iustin Pop
    return self._config_data.cluster
1462 e00fb268 Iustin Pop
1463 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1464 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
1465 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
1466 51cb1581 Luca Bigliardi

1467 51cb1581 Luca Bigliardi
    """
1468 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
1469 51cb1581 Luca Bigliardi
1470 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1471 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1472 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1473 e00fb268 Iustin Pop

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

1480 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1481 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1482 c41eea6e Iustin Pop
        the cluster
1483 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1484 c41eea6e Iustin Pop

1485 e00fb268 Iustin Pop
    """
1486 e00fb268 Iustin Pop
    if self._config_data is None:
1487 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1488 3ecf6786 Iustin Pop
                                   " cannot save.")
1489 f34901f8 Iustin Pop
    update_serial = False
1490 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1491 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1492 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1493 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1494 f34901f8 Iustin Pop
      update_serial = True
1495 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1496 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1497 e00fb268 Iustin Pop
    else:
1498 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1499 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1500 e00fb268 Iustin Pop
    if not test:
1501 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1502 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1503 f34901f8 Iustin Pop
    target.serial_no += 1
1504 d693c864 Iustin Pop
    target.mtime = now = time.time()
1505 f34901f8 Iustin Pop
1506 cff4c037 Iustin Pop
    if update_serial:
1507 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1508 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1509 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1510 b989e85d Iustin Pop
1511 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1512 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1513 61cf6b5e Iustin Pop
1514 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
1515 73064714 Guido Trotter
1516 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
1517 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
1518 73064714 Guido Trotter
    """Drop per-execution-context reservations
1519 73064714 Guido Trotter

1520 73064714 Guido Trotter
    """
1521 d8aee57e Iustin Pop
    for rm in self._all_rms:
1522 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)