Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ bfc2002f

History | View | Annotate | Download (48.2 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 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 243cdbcc Michael Hanselmann
48 243cdbcc Michael Hanselmann
49 f78ede4e Guido Trotter
_config_lock = locking.SharedLock()
50 f78ede4e Guido Trotter
51 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
52 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
53 4fae38c5 Guido Trotter
54 f78ede4e Guido Trotter
55 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
56 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
57 c41eea6e Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

816 a8083063 Iustin Pop
    """
817 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
818 a8083063 Iustin Pop
819 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
820 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
821 a8083063 Iustin Pop
    """Add an instance to the config.
822 a8083063 Iustin Pop

823 a8083063 Iustin Pop
    This should be used after creating a new instance.
824 a8083063 Iustin Pop

825 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
826 c41eea6e Iustin Pop
    @param instance: the instance object
827 c41eea6e Iustin Pop

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

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

858 430b923c Iustin Pop
    """
859 430b923c Iustin Pop
    if not item.uuid:
860 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
861 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
862 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
863 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
864 430b923c Iustin Pop
865 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
866 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
867 a8083063 Iustin Pop

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

886 6a408fb2 Iustin Pop
    """
887 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
888 6a408fb2 Iustin Pop
889 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
890 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
891 a8083063 Iustin Pop
    """Remove the instance from the configuration.
892 a8083063 Iustin Pop

893 a8083063 Iustin Pop
    """
894 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
895 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
896 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
897 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
898 a8083063 Iustin Pop
    self._WriteConfig()
899 a8083063 Iustin Pop
900 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
901 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
902 fc95f88f Iustin Pop
    """Rename an instance.
903 fc95f88f Iustin Pop

904 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
905 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
906 fc95f88f Iustin Pop
    rename.
907 fc95f88f Iustin Pop

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

931 a8083063 Iustin Pop
    """
932 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
933 a8083063 Iustin Pop
934 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
935 94bbfece Iustin Pop
    """Get the list of instances.
936 94bbfece Iustin Pop

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

939 94bbfece Iustin Pop
    """
940 94bbfece Iustin Pop
    return self._config_data.instances.keys()
941 94bbfece Iustin Pop
942 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
943 a8083063 Iustin Pop
  def GetInstanceList(self):
944 a8083063 Iustin Pop
    """Get the list of instances.
945 a8083063 Iustin Pop

946 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
947 c41eea6e Iustin Pop
        'instance1.example.com']
948 a8083063 Iustin Pop

949 a8083063 Iustin Pop
    """
950 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
951 a8083063 Iustin Pop
952 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
953 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
954 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
955 a8083063 Iustin Pop

956 a8083063 Iustin Pop
    """
957 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
958 bcdf16d7 Guido Trotter
                                    self._config_data.instances.keys(),
959 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
960 a8083063 Iustin Pop
961 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
962 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
963 94bbfece Iustin Pop

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

966 94bbfece Iustin Pop
    """
967 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
968 94bbfece Iustin Pop
      return None
969 94bbfece Iustin Pop
970 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
971 94bbfece Iustin Pop
972 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
973 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
974 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
975 a8083063 Iustin Pop

976 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
977 a8083063 Iustin Pop
    an instance are taken from the live systems.
978 a8083063 Iustin Pop

979 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
980 c41eea6e Iustin Pop
        I{instance1.example.com}
981 a8083063 Iustin Pop

982 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
983 c41eea6e Iustin Pop
    @return: the instance object
984 a8083063 Iustin Pop

985 a8083063 Iustin Pop
    """
986 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
987 a8083063 Iustin Pop
988 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
989 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
990 0b2de758 Iustin Pop
    """Get the configuration of all instances.
991 0b2de758 Iustin Pop

992 0b2de758 Iustin Pop
    @rtype: dict
993 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
994 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
995 0b2de758 Iustin Pop

996 0b2de758 Iustin Pop
    """
997 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
998 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
999 0b2de758 Iustin Pop
    return my_dict
1000 0b2de758 Iustin Pop
1001 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1002 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1003 a8083063 Iustin Pop
    """Add a node to the configuration.
1004 a8083063 Iustin Pop

1005 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1006 c41eea6e Iustin Pop
    @param node: a Node instance
1007 a8083063 Iustin Pop

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

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

1037 a8083063 Iustin Pop
    """
1038 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1039 bcdf16d7 Guido Trotter
                                    self._config_data.nodes.keys(),
1040 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1041 a8083063 Iustin Pop
1042 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1043 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1044 a8083063 Iustin Pop

1045 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1046 c41eea6e Iustin Pop
    held.
1047 f78ede4e Guido Trotter

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

1050 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1051 c41eea6e Iustin Pop
    @return: the node object
1052 a8083063 Iustin Pop

1053 a8083063 Iustin Pop
    """
1054 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1055 a8083063 Iustin Pop
      return None
1056 a8083063 Iustin Pop
1057 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1058 a8083063 Iustin Pop
1059 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1060 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1061 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1062 f78ede4e Guido Trotter

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

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

1067 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1068 c41eea6e Iustin Pop
    @return: the node object
1069 f78ede4e Guido Trotter

1070 f78ede4e Guido Trotter
    """
1071 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1072 f78ede4e Guido Trotter
1073 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1074 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1075 a8083063 Iustin Pop

1076 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1077 c41eea6e Iustin Pop
    held.
1078 c41eea6e Iustin Pop

1079 c41eea6e Iustin Pop
    @rtype: list
1080 f78ede4e Guido Trotter

1081 a8083063 Iustin Pop
    """
1082 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1083 a8083063 Iustin Pop
1084 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1085 f78ede4e Guido Trotter
  def GetNodeList(self):
1086 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1087 f78ede4e Guido Trotter

1088 f78ede4e Guido Trotter
    """
1089 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1090 f78ede4e Guido Trotter
1091 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1092 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1093 94a02bb5 Iustin Pop

1094 94a02bb5 Iustin Pop
    """
1095 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1096 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1097 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1098 94a02bb5 Iustin Pop
1099 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1100 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1101 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1102 6819dc49 Iustin Pop

1103 6819dc49 Iustin Pop
    """
1104 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1105 6819dc49 Iustin Pop
1106 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1107 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1108 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1109 d65e5776 Iustin Pop

1110 d65e5776 Iustin Pop
    @rtype: dict
1111 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1112 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1113 d65e5776 Iustin Pop

1114 d65e5776 Iustin Pop
    """
1115 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1116 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1117 d65e5776 Iustin Pop
    return my_dict
1118 d65e5776 Iustin Pop
1119 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1120 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1121 ec0292f1 Iustin Pop

1122 23f06b2b Iustin Pop
    @type exceptions: list
1123 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1124 ec0292f1 Iustin Pop
    @rtype: tuple
1125 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1126 ec0292f1 Iustin Pop

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

1143 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1144 ec0292f1 Iustin Pop

1145 23f06b2b Iustin Pop
    @type exceptions: list
1146 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1147 ec0292f1 Iustin Pop
    @rtype: tuple
1148 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1149 ec0292f1 Iustin Pop

1150 ec0292f1 Iustin Pop
    """
1151 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1152 ec0292f1 Iustin Pop
1153 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1154 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1155 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1156 ec0292f1 Iustin Pop

1157 44485f49 Guido Trotter
    @type exceptions: list
1158 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1159 ec0292f1 Iustin Pop
    @rtype: list
1160 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1161 ec0292f1 Iustin Pop

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

1192 a8083063 Iustin Pop
    """
1193 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1194 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1195 a8083063 Iustin Pop
1196 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1197 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1198 76d5d3a3 Iustin Pop

1199 76d5d3a3 Iustin Pop
    """
1200 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1201 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1202 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1203 76d5d3a3 Iustin Pop
1204 a8083063 Iustin Pop
  def _OpenConfig(self):
1205 a8083063 Iustin Pop
    """Read the config data from disk.
1206 a8083063 Iustin Pop

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

1237 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1238 76d5d3a3 Iustin Pop
    whole configuration, etc.
1239 76d5d3a3 Iustin Pop

1240 76d5d3a3 Iustin Pop
    @warning: this function will call L{_WriteConfig()}, so it needs
1241 76d5d3a3 Iustin Pop
        to either be called with the lock held or from a safe place
1242 76d5d3a3 Iustin Pop
        (the constructor)
1243 76d5d3a3 Iustin Pop

1244 76d5d3a3 Iustin Pop
    """
1245 76d5d3a3 Iustin Pop
    modified = False
1246 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1247 76d5d3a3 Iustin Pop
      if item.uuid is None:
1248 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1249 76d5d3a3 Iustin Pop
        modified = True
1250 76d5d3a3 Iustin Pop
    if modified:
1251 76d5d3a3 Iustin Pop
      self._WriteConfig()
1252 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1253 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1254 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1255 4fae38c5 Guido Trotter
1256 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1257 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1258 a8083063 Iustin Pop

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

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

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

1352 054596f0 Iustin Pop
    @rtype: dict
1353 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1354 054596f0 Iustin Pop
        associated value
1355 054596f0 Iustin Pop

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

1408 a8083063 Iustin Pop
    """
1409 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1410 a8083063 Iustin Pop
1411 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1412 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1413 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1414 89ff8e15 Manuel Franceschini

1415 89ff8e15 Manuel Franceschini
    """
1416 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1417 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1418 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1419 89ff8e15 Manuel Franceschini
1420 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1421 a8083063 Iustin Pop
  def GetMACPrefix(self):
1422 a8083063 Iustin Pop
    """Return the mac prefix.
1423 a8083063 Iustin Pop

1424 a8083063 Iustin Pop
    """
1425 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1426 62779dd0 Iustin Pop
1427 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1428 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1429 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1430 62779dd0 Iustin Pop

1431 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1432 c41eea6e Iustin Pop
    @return: the cluster object
1433 62779dd0 Iustin Pop

1434 62779dd0 Iustin Pop
    """
1435 62779dd0 Iustin Pop
    return self._config_data.cluster
1436 e00fb268 Iustin Pop
1437 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1438 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1439 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1440 e00fb268 Iustin Pop

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

1447 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1448 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1449 c41eea6e Iustin Pop
        the cluster
1450 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1451 c41eea6e Iustin Pop

1452 e00fb268 Iustin Pop
    """
1453 e00fb268 Iustin Pop
    if self._config_data is None:
1454 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1455 3ecf6786 Iustin Pop
                                   " cannot save.")
1456 f34901f8 Iustin Pop
    update_serial = False
1457 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1458 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1459 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1460 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1461 f34901f8 Iustin Pop
      update_serial = True
1462 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1463 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1464 e00fb268 Iustin Pop
    else:
1465 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1466 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1467 e00fb268 Iustin Pop
    if not test:
1468 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1469 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1470 f34901f8 Iustin Pop
    target.serial_no += 1
1471 d693c864 Iustin Pop
    target.mtime = now = time.time()
1472 f34901f8 Iustin Pop
1473 cff4c037 Iustin Pop
    if update_serial:
1474 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1475 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1476 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1477 b989e85d Iustin Pop
1478 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1479 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1480 61cf6b5e Iustin Pop
1481 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
1482 73064714 Guido Trotter
1483 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
1484 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
1485 73064714 Guido Trotter
    """Drop per-execution-context reservations
1486 73064714 Guido Trotter

1487 73064714 Guido Trotter
    """
1488 d8aee57e Iustin Pop
    for rm in self._all_rms:
1489 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)