Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 75cf411a

History | View | Annotate | Download (59 kB)

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

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

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

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

32 a8083063 Iustin Pop
"""
33 a8083063 Iustin Pop
34 d367b66c Manuel Franceschini
# pylint: disable-msg=R0904
35 d367b66c Manuel Franceschini
# R0904: Too many public methods
36 d367b66c Manuel Franceschini
37 a8083063 Iustin Pop
import os
38 a8083063 Iustin Pop
import random
39 d8470559 Michael Hanselmann
import logging
40 d693c864 Iustin Pop
import time
41 a8083063 Iustin Pop
42 a8083063 Iustin Pop
from ganeti import errors
43 f78ede4e Guido Trotter
from ganeti import locking
44 a8083063 Iustin Pop
from ganeti import utils
45 a8083063 Iustin Pop
from ganeti import constants
46 a8083063 Iustin Pop
from ganeti import rpc
47 a8083063 Iustin Pop
from ganeti import objects
48 8d14b30d Iustin Pop
from ganeti import serializer
49 0fbae49a Balazs Lecz
from ganeti import uidpool
50 a744b676 Manuel Franceschini
from ganeti import netutils
51 e60c73a1 René Nussbaumer
from ganeti import runtime
52 243cdbcc Michael Hanselmann
53 243cdbcc Michael Hanselmann
54 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
55 f78ede4e Guido Trotter
56 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
57 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
58 4fae38c5 Guido Trotter
59 f78ede4e Guido Trotter
60 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
61 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
62 c41eea6e Iustin Pop

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

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

68 c41eea6e Iustin Pop
  """
69 5b263ed7 Michael Hanselmann
  if data.version != constants.CONFIG_VERSION:
70 4b63dc7a Iustin Pop
    raise errors.ConfigVersionMismatch(constants.CONFIG_VERSION, data.version)
71 a8083063 Iustin Pop
72 319856a9 Michael Hanselmann
73 013da361 Guido Trotter
class TemporaryReservationManager:
74 013da361 Guido Trotter
  """A temporary resource reservation manager.
75 013da361 Guido Trotter

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

906 e11a1b77 Adeodato Simo
    @type group_uuid: string
907 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
908 e11a1b77 Adeodato Simo

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

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

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

947 648e4196 Guido Trotter
    @type uuid: string
948 648e4196 Guido Trotter
    @param uuid: group UUID
949 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
950 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
951 648e4196 Guido Trotter

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

962 5768e6a6 René Nussbaumer
    @type uuid: string
963 5768e6a6 René Nussbaumer
    @param uuid: group UUID
964 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
965 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
966 5768e6a6 René Nussbaumer

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

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

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

988 a8083063 Iustin Pop
    This should be used after creating a new instance.
989 a8083063 Iustin Pop

990 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
991 c41eea6e Iustin Pop
    @param instance: the instance object
992 c41eea6e Iustin Pop

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

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

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

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

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

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

1069 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1070 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1071 fc95f88f Iustin Pop
    rename.
1072 fc95f88f Iustin Pop

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

1099 a8083063 Iustin Pop
    """
1100 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
1101 a8083063 Iustin Pop
1102 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1103 94bbfece Iustin Pop
    """Get the list of instances.
1104 94bbfece Iustin Pop

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

1107 94bbfece Iustin Pop
    """
1108 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1109 94bbfece Iustin Pop
1110 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1111 a8083063 Iustin Pop
  def GetInstanceList(self):
1112 a8083063 Iustin Pop
    """Get the list of instances.
1113 a8083063 Iustin Pop

1114 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1115 c41eea6e Iustin Pop
        'instance1.example.com']
1116 a8083063 Iustin Pop

1117 a8083063 Iustin Pop
    """
1118 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1119 a8083063 Iustin Pop
1120 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1121 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1122 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1123 a8083063 Iustin Pop

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

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

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

1144 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1145 a8083063 Iustin Pop
    an instance are taken from the live systems.
1146 a8083063 Iustin Pop

1147 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1148 c41eea6e Iustin Pop
        I{instance1.example.com}
1149 a8083063 Iustin Pop

1150 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1151 c41eea6e Iustin Pop
    @return: the instance object
1152 a8083063 Iustin Pop

1153 a8083063 Iustin Pop
    """
1154 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1155 a8083063 Iustin Pop
1156 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1157 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1158 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1159 0b2de758 Iustin Pop

1160 0b2de758 Iustin Pop
    @rtype: dict
1161 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1162 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1163 0b2de758 Iustin Pop

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

1173 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1174 c41eea6e Iustin Pop
    @param node: a Node instance
1175 a8083063 Iustin Pop

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

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

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

1215 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1216 c41eea6e Iustin Pop
    held.
1217 f78ede4e Guido Trotter

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

1220 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1221 c41eea6e Iustin Pop
    @return: the node object
1222 a8083063 Iustin Pop

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

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

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

1237 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1238 c41eea6e Iustin Pop
    @return: the node object
1239 f78ede4e Guido Trotter

1240 f78ede4e Guido Trotter
    """
1241 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1242 f78ede4e Guido Trotter
1243 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1244 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1245 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1246 8bf9e9a5 Iustin Pop

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

1249 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1250 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1251 8bf9e9a5 Iustin Pop

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

1265 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1266 c41eea6e Iustin Pop
    held.
1267 c41eea6e Iustin Pop

1268 c41eea6e Iustin Pop
    @rtype: list
1269 f78ede4e Guido Trotter

1270 a8083063 Iustin Pop
    """
1271 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1272 a8083063 Iustin Pop
1273 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1274 f78ede4e Guido Trotter
  def GetNodeList(self):
1275 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1276 f78ede4e Guido Trotter

1277 f78ede4e Guido Trotter
    """
1278 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1279 f78ede4e Guido Trotter
1280 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1281 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1282 94a02bb5 Iustin Pop

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

1292 6819dc49 Iustin Pop
    """
1293 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1294 6819dc49 Iustin Pop
1295 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1296 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1297 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1298 8bf9e9a5 Iustin Pop

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

1308 d65e5776 Iustin Pop
    @rtype: dict
1309 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1310 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1311 d65e5776 Iustin Pop

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

1320 23f06b2b Iustin Pop
    @type exceptions: list
1321 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1322 ec0292f1 Iustin Pop
    @rtype: tuple
1323 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1324 ec0292f1 Iustin Pop

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

1341 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1342 ec0292f1 Iustin Pop

1343 23f06b2b Iustin Pop
    @type exceptions: list
1344 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1345 ec0292f1 Iustin Pop
    @rtype: tuple
1346 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1347 ec0292f1 Iustin Pop

1348 ec0292f1 Iustin Pop
    """
1349 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1350 ec0292f1 Iustin Pop
1351 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1352 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1353 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1354 ec0292f1 Iustin Pop

1355 44485f49 Guido Trotter
    @type exceptions: list
1356 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1357 ec0292f1 Iustin Pop
    @rtype: list
1358 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1359 ec0292f1 Iustin Pop

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

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

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

1418 a8083063 Iustin Pop
    """
1419 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1420 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1421 a8083063 Iustin Pop
1422 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1423 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1424 76d5d3a3 Iustin Pop

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

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

1473 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1474 76d5d3a3 Iustin Pop
    whole configuration, etc.
1475 76d5d3a3 Iustin Pop

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

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

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

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

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

1616 054596f0 Iustin Pop
    @rtype: dict
1617 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1618 054596f0 Iustin Pop
        associated value
1619 054596f0 Iustin Pop

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

1678 d367b66c Manuel Franceschini
    """
1679 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
1680 d367b66c Manuel Franceschini
1681 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1682 a8083063 Iustin Pop
  def GetVGName(self):
1683 a8083063 Iustin Pop
    """Return the volume group name.
1684 a8083063 Iustin Pop

1685 a8083063 Iustin Pop
    """
1686 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1687 a8083063 Iustin Pop
1688 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1689 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1690 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1691 89ff8e15 Manuel Franceschini

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

1701 9e33896b Luca Bigliardi
    """
1702 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
1703 9e33896b Luca Bigliardi
1704 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
1705 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
1706 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
1707 9e33896b Luca Bigliardi

1708 9e33896b Luca Bigliardi
    """
1709 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
1710 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
1711 9e33896b Luca Bigliardi
    self._WriteConfig()
1712 9e33896b Luca Bigliardi
1713 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1714 a8083063 Iustin Pop
  def GetMACPrefix(self):
1715 a8083063 Iustin Pop
    """Return the mac prefix.
1716 a8083063 Iustin Pop

1717 a8083063 Iustin Pop
    """
1718 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1719 62779dd0 Iustin Pop
1720 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1721 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1722 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1723 62779dd0 Iustin Pop

1724 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1725 c41eea6e Iustin Pop
    @return: the cluster object
1726 62779dd0 Iustin Pop

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

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

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

1747 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1748 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1749 c41eea6e Iustin Pop
        the cluster
1750 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1751 c41eea6e Iustin Pop

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

1789 73064714 Guido Trotter
    """
1790 d8aee57e Iustin Pop
    for rm in self._all_rms:
1791 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)