Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ bfbbc223

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

502 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
503 4a89c54a Iustin Pop

504 4a89c54a Iustin Pop
    @rtype: list
505 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
506 4a89c54a Iustin Pop
        configuration errors
507 4a89c54a Iustin Pop

508 4a89c54a Iustin Pop
    """
509 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
510 4a89c54a Iustin Pop
511 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
512 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
513 a8083063 Iustin Pop

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

516 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
517 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
518 a8083063 Iustin Pop
    node.
519 a8083063 Iustin Pop

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

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

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

555 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
556 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
557 f78ede4e Guido Trotter
    node.
558 f78ede4e Guido Trotter

559 f78ede4e Guido Trotter
    """
560 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
561 f78ede4e Guido Trotter
562 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
563 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
564 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
565 b2fddf63 Iustin Pop

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

577 264bb3c5 Michael Hanselmann
    """
578 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
579 264bb3c5 Michael Hanselmann
580 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
581 a8083063 Iustin Pop
  def AllocatePort(self):
582 a8083063 Iustin Pop
    """Allocate a port.
583 a8083063 Iustin Pop

584 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
585 b2fddf63 Iustin Pop
    default port range (and in this case we increase
586 b2fddf63 Iustin Pop
    highest_used_port).
587 a8083063 Iustin Pop

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

606 4a89c54a Iustin Pop
    @rtype: (dict, list)
607 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
608 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
609 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
610 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
611 4a89c54a Iustin Pop
        should raise an exception
612 a81c53c9 Iustin Pop

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

646 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
647 6d2e83d5 Iustin Pop

648 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
649 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
650 6d2e83d5 Iustin Pop
        an empty list).
651 6d2e83d5 Iustin Pop

652 6d2e83d5 Iustin Pop
    """
653 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
654 4a89c54a Iustin Pop
    if duplicates:
655 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
656 4a89c54a Iustin Pop
                                      str(duplicates))
657 4a89c54a Iustin Pop
    return d_map
658 6d2e83d5 Iustin Pop
659 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
660 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
661 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
662 a81c53c9 Iustin Pop

663 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
664 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
665 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
666 a81c53c9 Iustin Pop
    order as the passed nodes.
667 a81c53c9 Iustin Pop

668 32388e6d Iustin Pop
    @type instance: string
669 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
670 32388e6d Iustin Pop

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

718 a81c53c9 Iustin Pop
    @type instance: string
719 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
720 a81c53c9 Iustin Pop
                     released
721 a81c53c9 Iustin Pop

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

733 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
734 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
735 61cf6b5e Iustin Pop
    functions.
736 61cf6b5e Iustin Pop

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

739 61cf6b5e Iustin Pop
    @type instance: string
740 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
741 61cf6b5e Iustin Pop
                     released
742 61cf6b5e Iustin Pop

743 61cf6b5e Iustin Pop
    """
744 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
745 61cf6b5e Iustin Pop
746 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
747 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
748 4a8b186a Michael Hanselmann
    """Get the configuration version.
749 4a8b186a Michael Hanselmann

750 4a8b186a Michael Hanselmann
    @return: Config version
751 4a8b186a Michael Hanselmann

752 4a8b186a Michael Hanselmann
    """
753 4a8b186a Michael Hanselmann
    return self._config_data.version
754 4a8b186a Michael Hanselmann
755 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
756 4a8b186a Michael Hanselmann
  def GetClusterName(self):
757 4a8b186a Michael Hanselmann
    """Get cluster name.
758 4a8b186a Michael Hanselmann

759 4a8b186a Michael Hanselmann
    @return: Cluster name
760 4a8b186a Michael Hanselmann

761 4a8b186a Michael Hanselmann
    """
762 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
763 4a8b186a Michael Hanselmann
764 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
765 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
766 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
767 4a8b186a Michael Hanselmann

768 4a8b186a Michael Hanselmann
    @return: Master hostname
769 4a8b186a Michael Hanselmann

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

777 4a8b186a Michael Hanselmann
    @return: Master IP
778 4a8b186a Michael Hanselmann

779 4a8b186a Michael Hanselmann
    """
780 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
781 4a8b186a Michael Hanselmann
782 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
783 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
784 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
785 4a8b186a Michael Hanselmann

786 4a8b186a Michael Hanselmann
    """
787 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
788 4a8b186a Michael Hanselmann
789 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
790 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
791 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
792 4a8b186a Michael Hanselmann

793 4a8b186a Michael Hanselmann
    """
794 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
795 4a8b186a Michael Hanselmann
796 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
797 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
798 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
799 4a8b186a Michael Hanselmann

800 4a8b186a Michael Hanselmann
    """
801 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
802 4a8b186a Michael Hanselmann
803 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
804 a8083063 Iustin Pop
  def GetHostKey(self):
805 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
806 a8083063 Iustin Pop

807 c41eea6e Iustin Pop
    @rtype: string
808 c41eea6e Iustin Pop
    @return: the rsa hostkey
809 a8083063 Iustin Pop

810 a8083063 Iustin Pop
    """
811 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
812 a8083063 Iustin Pop
813 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
814 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
815 a8083063 Iustin Pop
    """Add an instance to the config.
816 a8083063 Iustin Pop

817 a8083063 Iustin Pop
    This should be used after creating a new instance.
818 a8083063 Iustin Pop

819 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
820 c41eea6e Iustin Pop
    @param instance: the instance object
821 c41eea6e Iustin Pop

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

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

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

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

880 6a408fb2 Iustin Pop
    """
881 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
882 6a408fb2 Iustin Pop
883 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
884 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
885 a8083063 Iustin Pop
    """Remove the instance from the configuration.
886 a8083063 Iustin Pop

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

898 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
899 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
900 fc95f88f Iustin Pop
    rename.
901 fc95f88f Iustin Pop

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

925 a8083063 Iustin Pop
    """
926 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
927 a8083063 Iustin Pop
928 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
929 94bbfece Iustin Pop
    """Get the list of instances.
930 94bbfece Iustin Pop

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

933 94bbfece Iustin Pop
    """
934 94bbfece Iustin Pop
    return self._config_data.instances.keys()
935 94bbfece Iustin Pop
936 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
937 a8083063 Iustin Pop
  def GetInstanceList(self):
938 a8083063 Iustin Pop
    """Get the list of instances.
939 a8083063 Iustin Pop

940 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
941 c41eea6e Iustin Pop
        'instance1.example.com']
942 a8083063 Iustin Pop

943 a8083063 Iustin Pop
    """
944 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
945 a8083063 Iustin Pop
946 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
947 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
948 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
949 a8083063 Iustin Pop

950 a8083063 Iustin Pop
    """
951 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
952 bcdf16d7 Guido Trotter
                                    self._config_data.instances.keys(),
953 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
954 a8083063 Iustin Pop
955 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
956 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
957 94bbfece Iustin Pop

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

960 94bbfece Iustin Pop
    """
961 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
962 94bbfece Iustin Pop
      return None
963 94bbfece Iustin Pop
964 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
965 94bbfece Iustin Pop
966 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
967 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
968 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
969 a8083063 Iustin Pop

970 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
971 a8083063 Iustin Pop
    an instance are taken from the live systems.
972 a8083063 Iustin Pop

973 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
974 c41eea6e Iustin Pop
        I{instance1.example.com}
975 a8083063 Iustin Pop

976 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
977 c41eea6e Iustin Pop
    @return: the instance object
978 a8083063 Iustin Pop

979 a8083063 Iustin Pop
    """
980 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
981 a8083063 Iustin Pop
982 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
983 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
984 0b2de758 Iustin Pop
    """Get the configuration of all instances.
985 0b2de758 Iustin Pop

986 0b2de758 Iustin Pop
    @rtype: dict
987 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
988 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
989 0b2de758 Iustin Pop

990 0b2de758 Iustin Pop
    """
991 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
992 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
993 0b2de758 Iustin Pop
    return my_dict
994 0b2de758 Iustin Pop
995 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
996 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
997 a8083063 Iustin Pop
    """Add a node to the configuration.
998 a8083063 Iustin Pop

999 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1000 c41eea6e Iustin Pop
    @param node: a Node instance
1001 a8083063 Iustin Pop

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

1017 a8083063 Iustin Pop
    """
1018 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1019 d8470559 Michael Hanselmann
1020 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1021 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1022 a8083063 Iustin Pop
1023 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
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, shared=1)
1028 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1029 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1030 a8083063 Iustin Pop

1031 a8083063 Iustin Pop
    """
1032 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1033 bcdf16d7 Guido Trotter
                                    self._config_data.nodes.keys(),
1034 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1035 a8083063 Iustin Pop
1036 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1037 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1038 a8083063 Iustin Pop

1039 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1040 c41eea6e Iustin Pop
    held.
1041 f78ede4e Guido Trotter

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

1044 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1045 c41eea6e Iustin Pop
    @return: the node object
1046 a8083063 Iustin Pop

1047 a8083063 Iustin Pop
    """
1048 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1049 a8083063 Iustin Pop
      return None
1050 a8083063 Iustin Pop
1051 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1052 a8083063 Iustin Pop
1053 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1054 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1055 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1056 f78ede4e Guido Trotter

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

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

1061 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1062 c41eea6e Iustin Pop
    @return: the node object
1063 f78ede4e Guido Trotter

1064 f78ede4e Guido Trotter
    """
1065 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1066 f78ede4e Guido Trotter
1067 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1068 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1069 a8083063 Iustin Pop

1070 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1071 c41eea6e Iustin Pop
    held.
1072 c41eea6e Iustin Pop

1073 c41eea6e Iustin Pop
    @rtype: list
1074 f78ede4e Guido Trotter

1075 a8083063 Iustin Pop
    """
1076 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1077 a8083063 Iustin Pop
1078 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1079 f78ede4e Guido Trotter
  def GetNodeList(self):
1080 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1081 f78ede4e Guido Trotter

1082 f78ede4e Guido Trotter
    """
1083 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1084 f78ede4e Guido Trotter
1085 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1086 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1087 94a02bb5 Iustin Pop

1088 94a02bb5 Iustin Pop
    """
1089 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1090 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1091 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1092 94a02bb5 Iustin Pop
1093 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1094 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1095 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1096 6819dc49 Iustin Pop

1097 6819dc49 Iustin Pop
    """
1098 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1099 6819dc49 Iustin Pop
1100 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1101 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1102 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1103 d65e5776 Iustin Pop

1104 d65e5776 Iustin Pop
    @rtype: dict
1105 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1106 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1107 d65e5776 Iustin Pop

1108 d65e5776 Iustin Pop
    """
1109 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1110 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1111 d65e5776 Iustin Pop
    return my_dict
1112 d65e5776 Iustin Pop
1113 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1114 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1115 ec0292f1 Iustin Pop

1116 23f06b2b Iustin Pop
    @type exceptions: list
1117 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1118 ec0292f1 Iustin Pop
    @rtype: tuple
1119 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1120 ec0292f1 Iustin Pop

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

1137 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1138 ec0292f1 Iustin Pop

1139 23f06b2b Iustin Pop
    @type exceptions: list
1140 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1141 ec0292f1 Iustin Pop
    @rtype: tuple
1142 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1143 ec0292f1 Iustin Pop

1144 ec0292f1 Iustin Pop
    """
1145 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1146 ec0292f1 Iustin Pop
1147 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1148 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1149 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1150 ec0292f1 Iustin Pop

1151 44485f49 Guido Trotter
    @type exceptions: list
1152 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1153 ec0292f1 Iustin Pop
    @rtype: list
1154 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1155 ec0292f1 Iustin Pop

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

1186 a8083063 Iustin Pop
    """
1187 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1188 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1189 a8083063 Iustin Pop
1190 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1191 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1192 76d5d3a3 Iustin Pop

1193 76d5d3a3 Iustin Pop
    """
1194 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1195 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1196 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1197 76d5d3a3 Iustin Pop
1198 a8083063 Iustin Pop
  def _OpenConfig(self):
1199 a8083063 Iustin Pop
    """Read the config data from disk.
1200 a8083063 Iustin Pop

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

1231 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1232 76d5d3a3 Iustin Pop
    whole configuration, etc.
1233 76d5d3a3 Iustin Pop

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

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

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

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

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

1346 054596f0 Iustin Pop
    @rtype: dict
1347 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1348 054596f0 Iustin Pop
        associated value
1349 054596f0 Iustin Pop

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

1398 a8083063 Iustin Pop
    """
1399 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1400 a8083063 Iustin Pop
1401 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1402 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1403 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1404 89ff8e15 Manuel Franceschini

1405 89ff8e15 Manuel Franceschini
    """
1406 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1407 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1408 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1409 89ff8e15 Manuel Franceschini
1410 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1411 a8083063 Iustin Pop
  def GetMACPrefix(self):
1412 a8083063 Iustin Pop
    """Return the mac prefix.
1413 a8083063 Iustin Pop

1414 a8083063 Iustin Pop
    """
1415 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1416 62779dd0 Iustin Pop
1417 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1418 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1419 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1420 62779dd0 Iustin Pop

1421 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1422 c41eea6e Iustin Pop
    @return: the cluster object
1423 62779dd0 Iustin Pop

1424 62779dd0 Iustin Pop
    """
1425 62779dd0 Iustin Pop
    return self._config_data.cluster
1426 e00fb268 Iustin Pop
1427 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1428 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1429 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1430 e00fb268 Iustin Pop

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

1437 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1438 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1439 c41eea6e Iustin Pop
        the cluster
1440 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1441 c41eea6e Iustin Pop

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

1477 73064714 Guido Trotter
    """
1478 d8aee57e Iustin Pop
    for rm in self._all_rms:
1479 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)