Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ fa6347d0

History | View | Annotate | Download (63.1 kB)

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

586 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
587 4a89c54a Iustin Pop

588 4a89c54a Iustin Pop
    @rtype: list
589 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
590 4a89c54a Iustin Pop
        configuration errors
591 4a89c54a Iustin Pop

592 4a89c54a Iustin Pop
    """
593 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
594 4a89c54a Iustin Pop
595 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
596 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
597 a8083063 Iustin Pop

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

600 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
601 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
602 a8083063 Iustin Pop
    node.
603 a8083063 Iustin Pop

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

606 a8083063 Iustin Pop
    """
607 a8083063 Iustin Pop
    if disk.children:
608 a8083063 Iustin Pop
      for child in disk.children:
609 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
610 a8083063 Iustin Pop
611 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
612 a8083063 Iustin Pop
      return
613 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
614 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
615 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
616 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
617 3ecf6786 Iustin Pop
                                        node_name)
618 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
619 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
620 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
621 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
622 a8083063 Iustin Pop
                                        " for %s" % str(disk))
623 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
624 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
625 a8083063 Iustin Pop
      if pnode == node_name:
626 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
627 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
628 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
629 a8083063 Iustin Pop
    else:
630 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
631 a8083063 Iustin Pop
    return
632 a8083063 Iustin Pop
633 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
634 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
635 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
636 f78ede4e Guido Trotter

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

639 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
640 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
641 f78ede4e Guido Trotter
    node.
642 f78ede4e Guido Trotter

643 f78ede4e Guido Trotter
    """
644 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
645 f78ede4e Guido Trotter
646 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
647 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
648 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
649 b2fddf63 Iustin Pop

650 b2fddf63 Iustin Pop
    """
651 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
652 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
653 264bb3c5 Michael Hanselmann
654 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
655 264bb3c5 Michael Hanselmann
    self._WriteConfig()
656 264bb3c5 Michael Hanselmann
657 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
658 b2fddf63 Iustin Pop
  def GetPortList(self):
659 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
660 264bb3c5 Michael Hanselmann

661 264bb3c5 Michael Hanselmann
    """
662 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
663 264bb3c5 Michael Hanselmann
664 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
665 a8083063 Iustin Pop
  def AllocatePort(self):
666 a8083063 Iustin Pop
    """Allocate a port.
667 a8083063 Iustin Pop

668 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
669 b2fddf63 Iustin Pop
    default port range (and in this case we increase
670 b2fddf63 Iustin Pop
    highest_used_port).
671 a8083063 Iustin Pop

672 a8083063 Iustin Pop
    """
673 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
674 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
675 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
676 264bb3c5 Michael Hanselmann
    else:
677 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
678 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
679 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
680 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
681 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
682 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
683 a8083063 Iustin Pop
684 a8083063 Iustin Pop
    self._WriteConfig()
685 a8083063 Iustin Pop
    return port
686 a8083063 Iustin Pop
687 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
688 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
689 a81c53c9 Iustin Pop

690 4a89c54a Iustin Pop
    @rtype: (dict, list)
691 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
692 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
693 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
694 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
695 4a89c54a Iustin Pop
        should raise an exception
696 a81c53c9 Iustin Pop

697 a81c53c9 Iustin Pop
    """
698 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
699 4a89c54a Iustin Pop
      duplicates = []
700 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
701 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
702 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
703 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
704 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
705 a81c53c9 Iustin Pop
          if port in used[node]:
706 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
707 4a89c54a Iustin Pop
          else:
708 4a89c54a Iustin Pop
            used[node][port] = instance_name
709 a81c53c9 Iustin Pop
      if disk.children:
710 a81c53c9 Iustin Pop
        for child in disk.children:
711 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
712 4a89c54a Iustin Pop
      return duplicates
713 a81c53c9 Iustin Pop
714 4a89c54a Iustin Pop
    duplicates = []
715 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
716 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
717 79b26a7a Iustin Pop
      for disk in instance.disks:
718 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
719 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
720 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
721 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
722 4a89c54a Iustin Pop
      else:
723 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
724 4a89c54a Iustin Pop
    return my_dict, duplicates
725 a81c53c9 Iustin Pop
726 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
727 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
728 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
729 6d2e83d5 Iustin Pop

730 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
731 6d2e83d5 Iustin Pop

732 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
733 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
734 6d2e83d5 Iustin Pop
        an empty list).
735 6d2e83d5 Iustin Pop

736 6d2e83d5 Iustin Pop
    """
737 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
738 4a89c54a Iustin Pop
    if duplicates:
739 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
740 4a89c54a Iustin Pop
                                      str(duplicates))
741 4a89c54a Iustin Pop
    return d_map
742 6d2e83d5 Iustin Pop
743 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
744 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
745 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
746 a81c53c9 Iustin Pop

747 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
748 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
749 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
750 a81c53c9 Iustin Pop
    order as the passed nodes.
751 a81c53c9 Iustin Pop

752 32388e6d Iustin Pop
    @type instance: string
753 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
754 32388e6d Iustin Pop

755 a81c53c9 Iustin Pop
    """
756 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
757 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
758 32388e6d Iustin Pop
759 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
760 4a89c54a Iustin Pop
    if duplicates:
761 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
762 4a89c54a Iustin Pop
                                      str(duplicates))
763 a81c53c9 Iustin Pop
    result = []
764 a81c53c9 Iustin Pop
    for nname in nodes:
765 a81c53c9 Iustin Pop
      ndata = d_map[nname]
766 a81c53c9 Iustin Pop
      if not ndata:
767 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
768 a81c53c9 Iustin Pop
        result.append(0)
769 a81c53c9 Iustin Pop
        ndata[0] = instance
770 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
771 a81c53c9 Iustin Pop
        continue
772 a81c53c9 Iustin Pop
      keys = ndata.keys()
773 a81c53c9 Iustin Pop
      keys.sort()
774 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
775 a81c53c9 Iustin Pop
      if ffree is None:
776 a81c53c9 Iustin Pop
        # return the next minor
777 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
778 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
779 a81c53c9 Iustin Pop
      else:
780 a81c53c9 Iustin Pop
        minor = ffree
781 4a89c54a Iustin Pop
      # double-check minor against current instances
782 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
783 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
784 4a89c54a Iustin Pop
              " already allocated to instance %s" %
785 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
786 a81c53c9 Iustin Pop
      ndata[minor] = instance
787 4a89c54a Iustin Pop
      # double-check minor against reservation
788 4a89c54a Iustin Pop
      r_key = (nname, minor)
789 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
790 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
791 4a89c54a Iustin Pop
              " reserved for instance %s" %
792 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
793 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
794 4a89c54a Iustin Pop
      result.append(minor)
795 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
796 a81c53c9 Iustin Pop
                  nodes, result)
797 a81c53c9 Iustin Pop
    return result
798 a81c53c9 Iustin Pop
799 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
800 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
801 a81c53c9 Iustin Pop

802 a81c53c9 Iustin Pop
    @type instance: string
803 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
804 a81c53c9 Iustin Pop
                     released
805 a81c53c9 Iustin Pop

806 a81c53c9 Iustin Pop
    """
807 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
808 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
809 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
810 a81c53c9 Iustin Pop
      if name == instance:
811 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
812 a81c53c9 Iustin Pop
813 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
814 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
815 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
816 61cf6b5e Iustin Pop

817 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
818 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
819 61cf6b5e Iustin Pop
    functions.
820 61cf6b5e Iustin Pop

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

823 61cf6b5e Iustin Pop
    @type instance: string
824 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
825 61cf6b5e Iustin Pop
                     released
826 61cf6b5e Iustin Pop

827 61cf6b5e Iustin Pop
    """
828 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
829 61cf6b5e Iustin Pop
830 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
831 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
832 4a8b186a Michael Hanselmann
    """Get the configuration version.
833 4a8b186a Michael Hanselmann

834 4a8b186a Michael Hanselmann
    @return: Config version
835 4a8b186a Michael Hanselmann

836 4a8b186a Michael Hanselmann
    """
837 4a8b186a Michael Hanselmann
    return self._config_data.version
838 4a8b186a Michael Hanselmann
839 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
840 4a8b186a Michael Hanselmann
  def GetClusterName(self):
841 4a8b186a Michael Hanselmann
    """Get cluster name.
842 4a8b186a Michael Hanselmann

843 4a8b186a Michael Hanselmann
    @return: Cluster name
844 4a8b186a Michael Hanselmann

845 4a8b186a Michael Hanselmann
    """
846 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
847 4a8b186a Michael Hanselmann
848 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
849 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
850 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
851 4a8b186a Michael Hanselmann

852 4a8b186a Michael Hanselmann
    @return: Master hostname
853 4a8b186a Michael Hanselmann

854 4a8b186a Michael Hanselmann
    """
855 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
856 4a8b186a Michael Hanselmann
857 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
858 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
859 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
860 4a8b186a Michael Hanselmann

861 4a8b186a Michael Hanselmann
    @return: Master IP
862 4a8b186a Michael Hanselmann

863 4a8b186a Michael Hanselmann
    """
864 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
865 4a8b186a Michael Hanselmann
866 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
867 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
868 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
869 4a8b186a Michael Hanselmann

870 4a8b186a Michael Hanselmann
    """
871 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
872 4a8b186a Michael Hanselmann
873 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
874 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
875 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
876 4a8b186a Michael Hanselmann

877 4a8b186a Michael Hanselmann
    """
878 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
879 4a8b186a Michael Hanselmann
880 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
881 53197381 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
882 53197381 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
883 53197381 Apollon Oikonomopoulos

884 53197381 Apollon Oikonomopoulos
    """
885 53197381 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
886 53197381 Apollon Oikonomopoulos
887 53197381 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
888 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
889 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
890 4a8b186a Michael Hanselmann

891 4a8b186a Michael Hanselmann
    """
892 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
893 4a8b186a Michael Hanselmann
894 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
895 a8083063 Iustin Pop
  def GetHostKey(self):
896 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
897 a8083063 Iustin Pop

898 c41eea6e Iustin Pop
    @rtype: string
899 c41eea6e Iustin Pop
    @return: the rsa hostkey
900 a8083063 Iustin Pop

901 a8083063 Iustin Pop
    """
902 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
903 a8083063 Iustin Pop
904 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
905 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
906 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
907 bf4af505 Apollon Oikonomopoulos

908 bf4af505 Apollon Oikonomopoulos
    """
909 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
910 bf4af505 Apollon Oikonomopoulos
911 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
912 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
913 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
914 868a98ca Manuel Franceschini

915 868a98ca Manuel Franceschini
    @return: primary ip family
916 868a98ca Manuel Franceschini

917 868a98ca Manuel Franceschini
    """
918 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
919 868a98ca Manuel Franceschini
920 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
921 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
922 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
923 e11a1b77 Adeodato Simo

924 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
925 90e99856 Adeodato Simo
    according to their default values.
926 90e99856 Adeodato Simo

927 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
928 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
929 e11a1b77 Adeodato Simo
    @type ec_id: string
930 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
931 e11a1b77 Adeodato Simo
    @type check_uuid: bool
932 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
933 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
934 e11a1b77 Adeodato Simo
                       configuration already
935 e11a1b77 Adeodato Simo

936 e11a1b77 Adeodato Simo
    """
937 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
938 e11a1b77 Adeodato Simo
    self._WriteConfig()
939 e11a1b77 Adeodato Simo
940 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
941 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
942 e11a1b77 Adeodato Simo

943 e11a1b77 Adeodato Simo
    """
944 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
945 e11a1b77 Adeodato Simo
946 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
947 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
948 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
949 e11a1b77 Adeodato Simo
    if check_uuid:
950 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
951 e11a1b77 Adeodato Simo
952 18ffc0fe Stephen Shirley
    try:
953 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
954 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
955 18ffc0fe Stephen Shirley
      pass
956 18ffc0fe Stephen Shirley
    else:
957 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
958 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
959 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
960 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
961 18ffc0fe Stephen Shirley
962 e11a1b77 Adeodato Simo
    group.serial_no = 1
963 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
964 90e99856 Adeodato Simo
    group.UpgradeConfig()
965 e11a1b77 Adeodato Simo
966 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
967 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
968 e11a1b77 Adeodato Simo
969 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
970 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
971 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
972 e11a1b77 Adeodato Simo

973 e11a1b77 Adeodato Simo
    @type group_uuid: string
974 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
975 e11a1b77 Adeodato Simo

976 e11a1b77 Adeodato Simo
    """
977 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
978 e11a1b77 Adeodato Simo
979 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
980 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
981 e11a1b77 Adeodato Simo
982 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
983 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
984 0389c42a Stephen Shirley
985 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
986 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
987 e11a1b77 Adeodato Simo
    self._WriteConfig()
988 e11a1b77 Adeodato Simo
989 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
990 412b3531 Guido Trotter
    """Lookup a node group's UUID.
991 eaa98a04 Guido Trotter

992 eaa98a04 Guido Trotter
    @type target: string or None
993 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
994 eaa98a04 Guido Trotter
    @rtype: string
995 412b3531 Guido Trotter
    @return: nodegroup UUID
996 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
997 eaa98a04 Guido Trotter

998 eaa98a04 Guido Trotter
    """
999 eaa98a04 Guido Trotter
    if target is None:
1000 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1001 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1002 eaa98a04 Guido Trotter
                                   " group must be specified explicitely.")
1003 eaa98a04 Guido Trotter
      else:
1004 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1005 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1006 eaa98a04 Guido Trotter
      return target
1007 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1008 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1009 eaa98a04 Guido Trotter
        return nodegroup.uuid
1010 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1011 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1012 eaa98a04 Guido Trotter
1013 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1014 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1015 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1016 e85d8982 Stephen Shirley

1017 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1018 e85d8982 Stephen Shirley

1019 e85d8982 Stephen Shirley
    @type target: string or None
1020 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1021 e85d8982 Stephen Shirley
    @rtype: string
1022 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1023 e85d8982 Stephen Shirley

1024 e85d8982 Stephen Shirley
    """
1025 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1026 e85d8982 Stephen Shirley
1027 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1028 648e4196 Guido Trotter
    """Lookup a node group.
1029 648e4196 Guido Trotter

1030 648e4196 Guido Trotter
    @type uuid: string
1031 648e4196 Guido Trotter
    @param uuid: group UUID
1032 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1033 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1034 648e4196 Guido Trotter

1035 648e4196 Guido Trotter
    """
1036 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1037 648e4196 Guido Trotter
      return None
1038 648e4196 Guido Trotter
1039 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1040 648e4196 Guido Trotter
1041 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1042 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1043 5768e6a6 René Nussbaumer
    """Lookup a node group.
1044 5768e6a6 René Nussbaumer

1045 5768e6a6 René Nussbaumer
    @type uuid: string
1046 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1047 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1048 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1049 5768e6a6 René Nussbaumer

1050 5768e6a6 René Nussbaumer
    """
1051 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1052 5768e6a6 René Nussbaumer
1053 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1054 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1055 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1056 622444e5 Iustin Pop

1057 622444e5 Iustin Pop
    """
1058 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1059 622444e5 Iustin Pop
1060 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1061 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1062 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1063 1ac6f2ad Guido Trotter

1064 1ac6f2ad Guido Trotter
    """
1065 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1066 1ac6f2ad Guido Trotter
1067 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1068 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1069 a8083063 Iustin Pop
    """Add an instance to the config.
1070 a8083063 Iustin Pop

1071 a8083063 Iustin Pop
    This should be used after creating a new instance.
1072 a8083063 Iustin Pop

1073 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1074 c41eea6e Iustin Pop
    @param instance: the instance object
1075 c41eea6e Iustin Pop

1076 a8083063 Iustin Pop
    """
1077 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1078 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1079 a8083063 Iustin Pop
1080 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1081 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1082 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1083 923b1523 Iustin Pop
1084 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1085 e4640214 Guido Trotter
    for nic in instance.nics:
1086 e4640214 Guido Trotter
      if nic.mac in all_macs:
1087 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1088 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1089 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1090 430b923c Iustin Pop
1091 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1092 e4640214 Guido Trotter
1093 b989e85d Iustin Pop
    instance.serial_no = 1
1094 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1095 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1096 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1097 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1098 a8083063 Iustin Pop
    self._WriteConfig()
1099 a8083063 Iustin Pop
1100 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1101 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1102 430b923c Iustin Pop

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

1106 430b923c Iustin Pop
    """
1107 430b923c Iustin Pop
    if not item.uuid:
1108 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1109 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1110 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1111 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1112 430b923c Iustin Pop
1113 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1114 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1115 a8083063 Iustin Pop

1116 a8083063 Iustin Pop
    """
1117 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
1118 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1119 a8083063 Iustin Pop
1120 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1121 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1122 3ecf6786 Iustin Pop
                                      instance_name)
1123 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1124 0d68c45d Iustin Pop
    if instance.admin_up != status:
1125 0d68c45d Iustin Pop
      instance.admin_up = status
1126 b989e85d Iustin Pop
      instance.serial_no += 1
1127 d693c864 Iustin Pop
      instance.mtime = time.time()
1128 455a3445 Iustin Pop
      self._WriteConfig()
1129 a8083063 Iustin Pop
1130 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1131 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1132 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1133 6a408fb2 Iustin Pop

1134 6a408fb2 Iustin Pop
    """
1135 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
1136 6a408fb2 Iustin Pop
1137 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1138 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1139 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1140 a8083063 Iustin Pop

1141 a8083063 Iustin Pop
    """
1142 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1143 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1144 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1145 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1146 a8083063 Iustin Pop
    self._WriteConfig()
1147 a8083063 Iustin Pop
1148 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1149 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1150 fc95f88f Iustin Pop
    """Rename an instance.
1151 fc95f88f Iustin Pop

1152 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1153 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1154 fc95f88f Iustin Pop
    rename.
1155 fc95f88f Iustin Pop

1156 fc95f88f Iustin Pop
    """
1157 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
1158 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
1159 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
1160 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
1161 fc95f88f Iustin Pop
    inst.name = new_name
1162 b23c4333 Manuel Franceschini
1163 b23c4333 Manuel Franceschini
    for disk in inst.disks:
1164 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1165 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1166 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1167 3721d2fe Guido Trotter
        disk_fname = "disk%s" % disk.iv_name.split("/")[1]
1168 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
1169 c4feafe8 Iustin Pop
                                              utils.PathJoin(file_storage_dir,
1170 c4feafe8 Iustin Pop
                                                             inst.name,
1171 3721d2fe Guido Trotter
                                                             disk_fname))
1172 b23c4333 Manuel Franceschini
1173 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1174 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1175 1fc34c48 Michael Hanselmann
1176 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
1177 fc95f88f Iustin Pop
    self._WriteConfig()
1178 fc95f88f Iustin Pop
1179 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1180 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
1181 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1182 a8083063 Iustin Pop

1183 a8083063 Iustin Pop
    """
1184 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
1185 a8083063 Iustin Pop
1186 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1187 94bbfece Iustin Pop
    """Get the list of instances.
1188 94bbfece Iustin Pop

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

1191 94bbfece Iustin Pop
    """
1192 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1193 94bbfece Iustin Pop
1194 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1195 a8083063 Iustin Pop
  def GetInstanceList(self):
1196 a8083063 Iustin Pop
    """Get the list of instances.
1197 a8083063 Iustin Pop

1198 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1199 c41eea6e Iustin Pop
        'instance1.example.com']
1200 a8083063 Iustin Pop

1201 a8083063 Iustin Pop
    """
1202 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1203 a8083063 Iustin Pop
1204 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1205 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1206 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1207 a8083063 Iustin Pop

1208 a8083063 Iustin Pop
    """
1209 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1210 bcdf16d7 Guido Trotter
                                    self._config_data.instances.keys(),
1211 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1212 a8083063 Iustin Pop
1213 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1214 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1215 94bbfece Iustin Pop

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

1218 94bbfece Iustin Pop
    """
1219 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1220 94bbfece Iustin Pop
      return None
1221 94bbfece Iustin Pop
1222 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1223 94bbfece Iustin Pop
1224 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1225 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1226 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1227 a8083063 Iustin Pop

1228 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1229 a8083063 Iustin Pop
    an instance are taken from the live systems.
1230 a8083063 Iustin Pop

1231 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1232 c41eea6e Iustin Pop
        I{instance1.example.com}
1233 a8083063 Iustin Pop

1234 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1235 c41eea6e Iustin Pop
    @return: the instance object
1236 a8083063 Iustin Pop

1237 a8083063 Iustin Pop
    """
1238 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1239 a8083063 Iustin Pop
1240 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1241 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1242 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1243 0b2de758 Iustin Pop

1244 0b2de758 Iustin Pop
    @rtype: dict
1245 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1246 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1247 0b2de758 Iustin Pop

1248 0b2de758 Iustin Pop
    """
1249 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1250 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1251 0b2de758 Iustin Pop
    return my_dict
1252 0b2de758 Iustin Pop
1253 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1254 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1255 a8083063 Iustin Pop
    """Add a node to the configuration.
1256 a8083063 Iustin Pop

1257 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1258 c41eea6e Iustin Pop
    @param node: a Node instance
1259 a8083063 Iustin Pop

1260 a8083063 Iustin Pop
    """
1261 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1262 d8470559 Michael Hanselmann
1263 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1264 430b923c Iustin Pop
1265 b989e85d Iustin Pop
    node.serial_no = 1
1266 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1267 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1268 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1269 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1270 a8083063 Iustin Pop
    self._WriteConfig()
1271 a8083063 Iustin Pop
1272 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1273 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1274 a8083063 Iustin Pop
    """Remove a node from the configuration.
1275 a8083063 Iustin Pop

1276 a8083063 Iustin Pop
    """
1277 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1278 d8470559 Michael Hanselmann
1279 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1280 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1281 a8083063 Iustin Pop
1282 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1283 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1284 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1285 a8083063 Iustin Pop
    self._WriteConfig()
1286 a8083063 Iustin Pop
1287 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1288 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1289 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1290 a8083063 Iustin Pop

1291 a8083063 Iustin Pop
    """
1292 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1293 bcdf16d7 Guido Trotter
                                    self._config_data.nodes.keys(),
1294 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1295 a8083063 Iustin Pop
1296 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1297 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1298 a8083063 Iustin Pop

1299 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1300 c41eea6e Iustin Pop
    held.
1301 f78ede4e Guido Trotter

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

1304 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1305 c41eea6e Iustin Pop
    @return: the node object
1306 a8083063 Iustin Pop

1307 a8083063 Iustin Pop
    """
1308 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1309 a8083063 Iustin Pop
      return None
1310 a8083063 Iustin Pop
1311 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1312 a8083063 Iustin Pop
1313 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1314 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1315 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1316 f78ede4e Guido Trotter

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

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

1321 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1322 c41eea6e Iustin Pop
    @return: the node object
1323 f78ede4e Guido Trotter

1324 f78ede4e Guido Trotter
    """
1325 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1326 f78ede4e Guido Trotter
1327 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1328 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1329 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1330 8bf9e9a5 Iustin Pop

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

1333 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1334 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1335 8bf9e9a5 Iustin Pop

1336 8bf9e9a5 Iustin Pop
    """
1337 8bf9e9a5 Iustin Pop
    pri = []
1338 8bf9e9a5 Iustin Pop
    sec = []
1339 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1340 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1341 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1342 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1343 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1344 8bf9e9a5 Iustin Pop
    return (pri, sec)
1345 8bf9e9a5 Iustin Pop
1346 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1347 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1348 a8083063 Iustin Pop

1349 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1350 c41eea6e Iustin Pop
    held.
1351 c41eea6e Iustin Pop

1352 c41eea6e Iustin Pop
    @rtype: list
1353 f78ede4e Guido Trotter

1354 a8083063 Iustin Pop
    """
1355 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1356 a8083063 Iustin Pop
1357 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1358 f78ede4e Guido Trotter
  def GetNodeList(self):
1359 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1360 f78ede4e Guido Trotter

1361 f78ede4e Guido Trotter
    """
1362 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1363 f78ede4e Guido Trotter
1364 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1365 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1366 94a02bb5 Iustin Pop

1367 94a02bb5 Iustin Pop
    """
1368 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1369 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1370 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1371 94a02bb5 Iustin Pop
1372 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1373 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1374 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1375 6819dc49 Iustin Pop

1376 6819dc49 Iustin Pop
    """
1377 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1378 6819dc49 Iustin Pop
1379 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1380 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1381 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1382 075b62ca Iustin Pop

1383 075b62ca Iustin Pop
    """
1384 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1385 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1386 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1387 075b62ca Iustin Pop
1388 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1389 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1390 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1391 8bf9e9a5 Iustin Pop

1392 8bf9e9a5 Iustin Pop
    """
1393 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1394 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1395 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1396 8bf9e9a5 Iustin Pop
1397 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1398 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1399 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1400 d65e5776 Iustin Pop

1401 d65e5776 Iustin Pop
    @rtype: dict
1402 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1403 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1404 d65e5776 Iustin Pop

1405 d65e5776 Iustin Pop
    """
1406 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1407 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1408 d65e5776 Iustin Pop
    return my_dict
1409 d65e5776 Iustin Pop
1410 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1411 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1412 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1413 9d5b1371 Michael Hanselmann

1414 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1415 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1416 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1417 9d5b1371 Michael Hanselmann

1418 9d5b1371 Michael Hanselmann
    """
1419 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1420 9d5b1371 Michael Hanselmann
1421 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1422 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1423 ec0292f1 Iustin Pop

1424 23f06b2b Iustin Pop
    @type exceptions: list
1425 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1426 ec0292f1 Iustin Pop
    @rtype: tuple
1427 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1428 ec0292f1 Iustin Pop

1429 ec0292f1 Iustin Pop
    """
1430 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1431 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1432 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1433 23f06b2b Iustin Pop
        continue
1434 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1435 ec0292f1 Iustin Pop
        mc_max += 1
1436 ec0292f1 Iustin Pop
      if node.master_candidate:
1437 ec0292f1 Iustin Pop
        mc_now += 1
1438 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1439 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1440 ec0292f1 Iustin Pop
1441 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1442 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1443 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1444 ec0292f1 Iustin Pop

1445 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1446 ec0292f1 Iustin Pop

1447 23f06b2b Iustin Pop
    @type exceptions: list
1448 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1449 ec0292f1 Iustin Pop
    @rtype: tuple
1450 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1451 ec0292f1 Iustin Pop

1452 ec0292f1 Iustin Pop
    """
1453 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1454 ec0292f1 Iustin Pop
1455 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1456 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1457 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1458 ec0292f1 Iustin Pop

1459 44485f49 Guido Trotter
    @type exceptions: list
1460 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1461 ec0292f1 Iustin Pop
    @rtype: list
1462 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1463 ec0292f1 Iustin Pop

1464 ec0292f1 Iustin Pop
    """
1465 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1466 ec0292f1 Iustin Pop
    mod_list = []
1467 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1468 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1469 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1470 ec0292f1 Iustin Pop
      for name in node_list:
1471 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1472 ec0292f1 Iustin Pop
          break
1473 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1474 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1475 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1476 ec0292f1 Iustin Pop
          continue
1477 ee513a66 Iustin Pop
        mod_list.append(node)
1478 ec0292f1 Iustin Pop
        node.master_candidate = True
1479 ec0292f1 Iustin Pop
        node.serial_no += 1
1480 ec0292f1 Iustin Pop
        mc_now += 1
1481 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1482 ec0292f1 Iustin Pop
        # this should not happen
1483 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1484 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1485 ec0292f1 Iustin Pop
      if mod_list:
1486 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1487 ec0292f1 Iustin Pop
        self._WriteConfig()
1488 ec0292f1 Iustin Pop
1489 ec0292f1 Iustin Pop
    return mod_list
1490 ec0292f1 Iustin Pop
1491 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1492 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1493 190e3cb6 Guido Trotter

1494 190e3cb6 Guido Trotter
    """
1495 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1496 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1497 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1498 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1499 190e3cb6 Guido Trotter
      # is not found anymore.
1500 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1501 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1502 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1503 190e3cb6 Guido Trotter
1504 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1505 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1506 190e3cb6 Guido Trotter

1507 190e3cb6 Guido Trotter
    """
1508 f936c153 Iustin Pop
    nodegroup = node.group
1509 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1510 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1511 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1512 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1513 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1514 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1515 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1516 190e3cb6 Guido Trotter
    else:
1517 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1518 190e3cb6 Guido Trotter
1519 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1520 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1521 a8083063 Iustin Pop

1522 a8083063 Iustin Pop
    """
1523 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1524 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1525 a8083063 Iustin Pop
1526 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1527 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1528 76d5d3a3 Iustin Pop

1529 76d5d3a3 Iustin Pop
    """
1530 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1531 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1532 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1533 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1534 76d5d3a3 Iustin Pop
1535 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1536 a8083063 Iustin Pop
    """Read the config data from disk.
1537 a8083063 Iustin Pop

1538 a8083063 Iustin Pop
    """
1539 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1540 13998ef2 Michael Hanselmann
1541 a8083063 Iustin Pop
    try:
1542 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1543 13998ef2 Michael Hanselmann
    except Exception, err:
1544 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1545 5b263ed7 Michael Hanselmann
1546 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1547 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1548 5b263ed7 Michael Hanselmann
1549 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1550 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1551 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1552 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1553 90d726a8 Iustin Pop
1554 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
1555 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
1556 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
1557 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
1558 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
1559 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
1560 eb180fe2 Iustin Pop
1561 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1562 90d726a8 Iustin Pop
    data.UpgradeConfig()
1563 90d726a8 Iustin Pop
1564 a8083063 Iustin Pop
    self._config_data = data
1565 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1566 0779e3aa Iustin Pop
    # ssconf update
1567 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1568 a8083063 Iustin Pop
1569 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1570 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1571 76d5d3a3 Iustin Pop
1572 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
1573 bd407597 Iustin Pop
1574 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1575 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1576 76d5d3a3 Iustin Pop

1577 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1578 76d5d3a3 Iustin Pop
    whole configuration, etc.
1579 76d5d3a3 Iustin Pop

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

1586 76d5d3a3 Iustin Pop
    """
1587 76d5d3a3 Iustin Pop
    modified = False
1588 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1589 76d5d3a3 Iustin Pop
      if item.uuid is None:
1590 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1591 76d5d3a3 Iustin Pop
        modified = True
1592 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
1593 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
1594 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
1595 75cf411a Adeodato Simo
                                            members=[])
1596 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
1597 f9e81396 Guido Trotter
      modified = True
1598 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
1599 f936c153 Iustin Pop
      if not node.group:
1600 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
1601 190e3cb6 Guido Trotter
        modified = True
1602 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
1603 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
1604 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
1605 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
1606 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
1607 76d5d3a3 Iustin Pop
    if modified:
1608 76d5d3a3 Iustin Pop
      self._WriteConfig()
1609 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1610 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1611 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1612 4fae38c5 Guido Trotter
1613 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1614 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1615 a8083063 Iustin Pop

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

1619 a8083063 Iustin Pop
    """
1620 a8083063 Iustin Pop
    if self._offline:
1621 a8083063 Iustin Pop
      return True
1622 a4eae71f Michael Hanselmann
1623 a8083063 Iustin Pop
    bad = False
1624 a8083063 Iustin Pop
1625 6a5b8b4b Iustin Pop
    node_list = []
1626 6a5b8b4b Iustin Pop
    addr_list = []
1627 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1628 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1629 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1630 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1631 6b294c53 Iustin Pop
    # in between
1632 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1633 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1634 6a5b8b4b Iustin Pop
        continue
1635 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1636 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1637 6a5b8b4b Iustin Pop
        continue
1638 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1639 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1640 6b294c53 Iustin Pop
1641 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1642 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1643 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1644 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1645 1b54fc6c Guido Trotter
      if msg:
1646 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1647 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1648 1b54fc6c Guido Trotter
        logging.error(msg)
1649 a4eae71f Michael Hanselmann
1650 a4eae71f Michael Hanselmann
        if feedback_fn:
1651 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1652 a4eae71f Michael Hanselmann
1653 a8083063 Iustin Pop
        bad = True
1654 a4eae71f Michael Hanselmann
1655 a8083063 Iustin Pop
    return not bad
1656 a8083063 Iustin Pop
1657 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1658 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1659 a8083063 Iustin Pop

1660 a8083063 Iustin Pop
    """
1661 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
1662 a4eae71f Michael Hanselmann
1663 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
1664 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
1665 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
1666 d2231b8c Iustin Pop
    # recovery to the user
1667 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1668 4a89c54a Iustin Pop
    if config_errors:
1669 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
1670 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
1671 d2231b8c Iustin Pop
      logging.critical(errmsg)
1672 d2231b8c Iustin Pop
      if feedback_fn:
1673 d2231b8c Iustin Pop
        feedback_fn(errmsg)
1674 d2231b8c Iustin Pop
1675 a8083063 Iustin Pop
    if destination is None:
1676 a8083063 Iustin Pop
      destination = self._cfg_file
1677 a8083063 Iustin Pop
    self._BumpSerialNo()
1678 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1679 13998ef2 Michael Hanselmann
1680 e60c73a1 René Nussbaumer
    getents = self._getents()
1681 bd407597 Iustin Pop
    try:
1682 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
1683 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
1684 bd407597 Iustin Pop
    except errors.LockError:
1685 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
1686 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
1687 bd407597 Iustin Pop
                                      " update")
1688 bd407597 Iustin Pop
    try:
1689 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
1690 bd407597 Iustin Pop
    finally:
1691 bd407597 Iustin Pop
      os.close(fd)
1692 13998ef2 Michael Hanselmann
1693 14e15659 Iustin Pop
    self.write_count += 1
1694 3d3a04bc Iustin Pop
1695 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
1696 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
1697 a8083063 Iustin Pop
1698 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
1699 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
1700 d9a855f1 Michael Hanselmann
      if not self._offline:
1701 cd34faf2 Michael Hanselmann
        result = rpc.RpcRunner.call_write_ssconf_files(
1702 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
1703 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
1704 a4eae71f Michael Hanselmann
1705 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
1706 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
1707 e1e75d00 Iustin Pop
          if msg:
1708 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
1709 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
1710 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
1711 a4eae71f Michael Hanselmann
1712 a4eae71f Michael Hanselmann
            if feedback_fn:
1713 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
1714 a4eae71f Michael Hanselmann
1715 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
1716 54d1a06e Michael Hanselmann
1717 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
1718 054596f0 Iustin Pop
    """Return the values needed by ssconf.
1719 054596f0 Iustin Pop

1720 054596f0 Iustin Pop
    @rtype: dict
1721 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1722 054596f0 Iustin Pop
        associated value
1723 054596f0 Iustin Pop

1724 054596f0 Iustin Pop
    """
1725 a3316e4a Iustin Pop
    fn = "\n".join
1726 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1727 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1728 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1729 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1730 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1731 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1732 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1733 a3316e4a Iustin Pop
1734 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1735 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1736 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1737 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1738 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1739 8113a52e Luca Bigliardi
                     if node.master_candidate)
1740 a3316e4a Iustin Pop
    node_data = fn(node_names)
1741 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1742 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1743 f56618e0 Iustin Pop
1744 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1745 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1746 4f7a6a10 Iustin Pop
1747 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
1748 4f7a6a10 Iustin Pop
1749 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
1750 0fbae49a Balazs Lecz
1751 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
1752 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
1753 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
1754 6f076453 Guido Trotter
1755 15df79ad Iustin Pop
    ssconf_values = {
1756 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1757 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1758 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1759 53197381 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
1760 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1761 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1762 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1763 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1764 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1765 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1766 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1767 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1768 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1769 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1770 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
1771 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1772 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1773 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
1774 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
1775 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
1776 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
1777 03d1dba2 Michael Hanselmann
      }
1778 15df79ad Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
1779 15df79ad Iustin Pop
                  if not isinstance(v, (str, basestring))]
1780 15df79ad Iustin Pop
    if bad_values:
1781 15df79ad Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
1782 15df79ad Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
1783 15df79ad Iustin Pop
                                      " values: %s" % err)
1784 15df79ad Iustin Pop
    return ssconf_values
1785 03d1dba2 Michael Hanselmann
1786 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1787 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
1788 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
1789 d367b66c Manuel Franceschini

1790 d367b66c Manuel Franceschini
    """
1791 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
1792 d367b66c Manuel Franceschini
1793 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1794 a8083063 Iustin Pop
  def GetVGName(self):
1795 a8083063 Iustin Pop
    """Return the volume group name.
1796 a8083063 Iustin Pop

1797 a8083063 Iustin Pop
    """
1798 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1799 a8083063 Iustin Pop
1800 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1801 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1802 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1803 89ff8e15 Manuel Franceschini

1804 89ff8e15 Manuel Franceschini
    """
1805 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1806 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1807 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1808 89ff8e15 Manuel Franceschini
1809 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1810 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
1811 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
1812 9e33896b Luca Bigliardi

1813 9e33896b Luca Bigliardi
    """
1814 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
1815 9e33896b Luca Bigliardi
1816 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
1817 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
1818 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
1819 9e33896b Luca Bigliardi

1820 9e33896b Luca Bigliardi
    """
1821 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
1822 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
1823 9e33896b Luca Bigliardi
    self._WriteConfig()
1824 9e33896b Luca Bigliardi
1825 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1826 a8083063 Iustin Pop
  def GetMACPrefix(self):
1827 a8083063 Iustin Pop
    """Return the mac prefix.
1828 a8083063 Iustin Pop

1829 a8083063 Iustin Pop
    """
1830 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1831 62779dd0 Iustin Pop
1832 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1833 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1834 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1835 62779dd0 Iustin Pop

1836 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1837 c41eea6e Iustin Pop
    @return: the cluster object
1838 62779dd0 Iustin Pop

1839 62779dd0 Iustin Pop
    """
1840 62779dd0 Iustin Pop
    return self._config_data.cluster
1841 e00fb268 Iustin Pop
1842 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1843 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
1844 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
1845 51cb1581 Luca Bigliardi

1846 51cb1581 Luca Bigliardi
    """
1847 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
1848 51cb1581 Luca Bigliardi
1849 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1850 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1851 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1852 e00fb268 Iustin Pop

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

1859 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1860 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1861 c41eea6e Iustin Pop
        the cluster
1862 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1863 c41eea6e Iustin Pop

1864 e00fb268 Iustin Pop
    """
1865 e00fb268 Iustin Pop
    if self._config_data is None:
1866 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1867 3ecf6786 Iustin Pop
                                   " cannot save.")
1868 f34901f8 Iustin Pop
    update_serial = False
1869 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1870 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1871 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1872 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1873 f34901f8 Iustin Pop
      update_serial = True
1874 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1875 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1876 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
1877 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
1878 e00fb268 Iustin Pop
    else:
1879 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1880 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1881 e00fb268 Iustin Pop
    if not test:
1882 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1883 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1884 f34901f8 Iustin Pop
    target.serial_no += 1
1885 d693c864 Iustin Pop
    target.mtime = now = time.time()
1886 f34901f8 Iustin Pop
1887 cff4c037 Iustin Pop
    if update_serial:
1888 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1889 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1890 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1891 b989e85d Iustin Pop
1892 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1893 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1894 61cf6b5e Iustin Pop
1895 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
1896 73064714 Guido Trotter
1897 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
1898 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
1899 73064714 Guido Trotter
    """Drop per-execution-context reservations
1900 73064714 Guido Trotter

1901 73064714 Guido Trotter
    """
1902 d8aee57e Iustin Pop
    for rm in self._all_rms:
1903 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)