Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 225e2544

History | View | Annotate | Download (62.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 36b66e6e Guido Trotter
      self._temporary_macs.Reserve(mac, ec_id)
216 1862d460 Alexander Schreiber
217 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
218 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
219 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
220 d8aee57e Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

369 a8083063 Iustin Pop
    """
370 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 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
882 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
883 4a8b186a Michael Hanselmann

884 4a8b186a Michael Hanselmann
    """
885 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
886 4a8b186a Michael Hanselmann
887 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
888 a8083063 Iustin Pop
  def GetHostKey(self):
889 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
890 a8083063 Iustin Pop

891 c41eea6e Iustin Pop
    @rtype: string
892 c41eea6e Iustin Pop
    @return: the rsa hostkey
893 a8083063 Iustin Pop

894 a8083063 Iustin Pop
    """
895 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
896 a8083063 Iustin Pop
897 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
898 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
899 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
900 bf4af505 Apollon Oikonomopoulos

901 bf4af505 Apollon Oikonomopoulos
    """
902 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
903 bf4af505 Apollon Oikonomopoulos
904 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
905 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
906 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
907 868a98ca Manuel Franceschini

908 868a98ca Manuel Franceschini
    @return: primary ip family
909 868a98ca Manuel Franceschini

910 868a98ca Manuel Franceschini
    """
911 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
912 868a98ca Manuel Franceschini
913 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
914 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
915 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
916 e11a1b77 Adeodato Simo

917 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
918 90e99856 Adeodato Simo
    according to their default values.
919 90e99856 Adeodato Simo

920 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
921 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
922 e11a1b77 Adeodato Simo
    @type ec_id: string
923 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
924 e11a1b77 Adeodato Simo
    @type check_uuid: bool
925 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
926 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
927 e11a1b77 Adeodato Simo
                       configuration already
928 e11a1b77 Adeodato Simo

929 e11a1b77 Adeodato Simo
    """
930 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
931 e11a1b77 Adeodato Simo
    self._WriteConfig()
932 e11a1b77 Adeodato Simo
933 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
934 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
935 e11a1b77 Adeodato Simo

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

966 e11a1b77 Adeodato Simo
    @type group_uuid: string
967 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
968 e11a1b77 Adeodato Simo

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

985 eaa98a04 Guido Trotter
    @type target: string or None
986 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
987 eaa98a04 Guido Trotter
    @rtype: string
988 412b3531 Guido Trotter
    @return: nodegroup UUID
989 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
990 eaa98a04 Guido Trotter

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

1010 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1011 e85d8982 Stephen Shirley

1012 e85d8982 Stephen Shirley
    @type target: string or None
1013 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1014 e85d8982 Stephen Shirley
    @rtype: string
1015 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1016 e85d8982 Stephen Shirley

1017 e85d8982 Stephen Shirley
    """
1018 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1019 e85d8982 Stephen Shirley
1020 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1021 648e4196 Guido Trotter
    """Lookup a node group.
1022 648e4196 Guido Trotter

1023 648e4196 Guido Trotter
    @type uuid: string
1024 648e4196 Guido Trotter
    @param uuid: group UUID
1025 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1026 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1027 648e4196 Guido Trotter

1028 648e4196 Guido Trotter
    """
1029 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1030 648e4196 Guido Trotter
      return None
1031 648e4196 Guido Trotter
1032 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1033 648e4196 Guido Trotter
1034 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1035 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1036 5768e6a6 René Nussbaumer
    """Lookup a node group.
1037 5768e6a6 René Nussbaumer

1038 5768e6a6 René Nussbaumer
    @type uuid: string
1039 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1040 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1041 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1042 5768e6a6 René Nussbaumer

1043 5768e6a6 René Nussbaumer
    """
1044 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1045 5768e6a6 René Nussbaumer
1046 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1047 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1048 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1049 622444e5 Iustin Pop

1050 622444e5 Iustin Pop
    """
1051 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1052 622444e5 Iustin Pop
1053 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1054 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1055 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1056 1ac6f2ad Guido Trotter

1057 1ac6f2ad Guido Trotter
    """
1058 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1059 1ac6f2ad Guido Trotter
1060 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1061 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1062 a8083063 Iustin Pop
    """Add an instance to the config.
1063 a8083063 Iustin Pop

1064 a8083063 Iustin Pop
    This should be used after creating a new instance.
1065 a8083063 Iustin Pop

1066 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1067 c41eea6e Iustin Pop
    @param instance: the instance object
1068 c41eea6e Iustin Pop

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

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

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

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

1127 6a408fb2 Iustin Pop
    """
1128 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
1129 6a408fb2 Iustin Pop
1130 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1131 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1132 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1133 a8083063 Iustin Pop

1134 a8083063 Iustin Pop
    """
1135 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1136 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1137 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1138 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1139 a8083063 Iustin Pop
    self._WriteConfig()
1140 a8083063 Iustin Pop
1141 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1142 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1143 fc95f88f Iustin Pop
    """Rename an instance.
1144 fc95f88f Iustin Pop

1145 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1146 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1147 fc95f88f Iustin Pop
    rename.
1148 fc95f88f Iustin Pop

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

1176 a8083063 Iustin Pop
    """
1177 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
1178 a8083063 Iustin Pop
1179 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1180 94bbfece Iustin Pop
    """Get the list of instances.
1181 94bbfece Iustin Pop

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

1184 94bbfece Iustin Pop
    """
1185 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1186 94bbfece Iustin Pop
1187 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1188 a8083063 Iustin Pop
  def GetInstanceList(self):
1189 a8083063 Iustin Pop
    """Get the list of instances.
1190 a8083063 Iustin Pop

1191 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1192 c41eea6e Iustin Pop
        'instance1.example.com']
1193 a8083063 Iustin Pop

1194 a8083063 Iustin Pop
    """
1195 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1196 a8083063 Iustin Pop
1197 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1198 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1199 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1200 a8083063 Iustin Pop

1201 a8083063 Iustin Pop
    """
1202 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1203 bcdf16d7 Guido Trotter
                                    self._config_data.instances.keys(),
1204 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1205 a8083063 Iustin Pop
1206 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1207 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1208 94bbfece Iustin Pop

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

1211 94bbfece Iustin Pop
    """
1212 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1213 94bbfece Iustin Pop
      return None
1214 94bbfece Iustin Pop
1215 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1216 94bbfece Iustin Pop
1217 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1218 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1219 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1220 a8083063 Iustin Pop

1221 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1222 a8083063 Iustin Pop
    an instance are taken from the live systems.
1223 a8083063 Iustin Pop

1224 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1225 c41eea6e Iustin Pop
        I{instance1.example.com}
1226 a8083063 Iustin Pop

1227 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1228 c41eea6e Iustin Pop
    @return: the instance object
1229 a8083063 Iustin Pop

1230 a8083063 Iustin Pop
    """
1231 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1232 a8083063 Iustin Pop
1233 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1234 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1235 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1236 0b2de758 Iustin Pop

1237 0b2de758 Iustin Pop
    @rtype: dict
1238 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1239 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1240 0b2de758 Iustin Pop

1241 0b2de758 Iustin Pop
    """
1242 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1243 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1244 0b2de758 Iustin Pop
    return my_dict
1245 0b2de758 Iustin Pop
1246 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1247 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1248 a8083063 Iustin Pop
    """Add a node to the configuration.
1249 a8083063 Iustin Pop

1250 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1251 c41eea6e Iustin Pop
    @param node: a Node instance
1252 a8083063 Iustin Pop

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

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

1284 a8083063 Iustin Pop
    """
1285 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1286 bcdf16d7 Guido Trotter
                                    self._config_data.nodes.keys(),
1287 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1288 a8083063 Iustin Pop
1289 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1290 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1291 a8083063 Iustin Pop

1292 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1293 c41eea6e Iustin Pop
    held.
1294 f78ede4e Guido Trotter

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

1297 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1298 c41eea6e Iustin Pop
    @return: the node object
1299 a8083063 Iustin Pop

1300 a8083063 Iustin Pop
    """
1301 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1302 a8083063 Iustin Pop
      return None
1303 a8083063 Iustin Pop
1304 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1305 a8083063 Iustin Pop
1306 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1307 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1308 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1309 f78ede4e Guido Trotter

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

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

1314 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1315 c41eea6e Iustin Pop
    @return: the node object
1316 f78ede4e Guido Trotter

1317 f78ede4e Guido Trotter
    """
1318 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1319 f78ede4e Guido Trotter
1320 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1321 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1322 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1323 8bf9e9a5 Iustin Pop

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

1326 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1327 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1328 8bf9e9a5 Iustin Pop

1329 8bf9e9a5 Iustin Pop
    """
1330 8bf9e9a5 Iustin Pop
    pri = []
1331 8bf9e9a5 Iustin Pop
    sec = []
1332 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1333 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1334 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1335 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1336 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1337 8bf9e9a5 Iustin Pop
    return (pri, sec)
1338 8bf9e9a5 Iustin Pop
1339 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1340 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1341 a8083063 Iustin Pop

1342 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1343 c41eea6e Iustin Pop
    held.
1344 c41eea6e Iustin Pop

1345 c41eea6e Iustin Pop
    @rtype: list
1346 f78ede4e Guido Trotter

1347 a8083063 Iustin Pop
    """
1348 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1349 a8083063 Iustin Pop
1350 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1351 f78ede4e Guido Trotter
  def GetNodeList(self):
1352 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1353 f78ede4e Guido Trotter

1354 f78ede4e Guido Trotter
    """
1355 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1356 f78ede4e Guido Trotter
1357 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1358 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1359 94a02bb5 Iustin Pop

1360 94a02bb5 Iustin Pop
    """
1361 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1362 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1363 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1364 94a02bb5 Iustin Pop
1365 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1366 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1367 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1368 6819dc49 Iustin Pop

1369 6819dc49 Iustin Pop
    """
1370 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1371 6819dc49 Iustin Pop
1372 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1373 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1374 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1375 075b62ca Iustin Pop

1376 075b62ca Iustin Pop
    """
1377 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1378 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1379 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1380 075b62ca Iustin Pop
1381 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1382 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1383 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1384 8bf9e9a5 Iustin Pop

1385 8bf9e9a5 Iustin Pop
    """
1386 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1387 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1388 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1389 8bf9e9a5 Iustin Pop
1390 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1391 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1392 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1393 d65e5776 Iustin Pop

1394 d65e5776 Iustin Pop
    @rtype: dict
1395 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1396 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1397 d65e5776 Iustin Pop

1398 d65e5776 Iustin Pop
    """
1399 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1400 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1401 d65e5776 Iustin Pop
    return my_dict
1402 d65e5776 Iustin Pop
1403 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1404 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1405 ec0292f1 Iustin Pop

1406 23f06b2b Iustin Pop
    @type exceptions: list
1407 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1408 ec0292f1 Iustin Pop
    @rtype: tuple
1409 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1410 ec0292f1 Iustin Pop

1411 ec0292f1 Iustin Pop
    """
1412 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1413 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1414 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1415 23f06b2b Iustin Pop
        continue
1416 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1417 ec0292f1 Iustin Pop
        mc_max += 1
1418 ec0292f1 Iustin Pop
      if node.master_candidate:
1419 ec0292f1 Iustin Pop
        mc_now += 1
1420 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1421 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1422 ec0292f1 Iustin Pop
1423 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1424 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1425 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1426 ec0292f1 Iustin Pop

1427 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1428 ec0292f1 Iustin Pop

1429 23f06b2b Iustin Pop
    @type exceptions: list
1430 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1431 ec0292f1 Iustin Pop
    @rtype: tuple
1432 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1433 ec0292f1 Iustin Pop

1434 ec0292f1 Iustin Pop
    """
1435 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1436 ec0292f1 Iustin Pop
1437 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1438 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1439 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1440 ec0292f1 Iustin Pop

1441 44485f49 Guido Trotter
    @type exceptions: list
1442 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1443 ec0292f1 Iustin Pop
    @rtype: list
1444 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1445 ec0292f1 Iustin Pop

1446 ec0292f1 Iustin Pop
    """
1447 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1448 ec0292f1 Iustin Pop
    mod_list = []
1449 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1450 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1451 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1452 ec0292f1 Iustin Pop
      for name in node_list:
1453 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1454 ec0292f1 Iustin Pop
          break
1455 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1456 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1457 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1458 ec0292f1 Iustin Pop
          continue
1459 ee513a66 Iustin Pop
        mod_list.append(node)
1460 ec0292f1 Iustin Pop
        node.master_candidate = True
1461 ec0292f1 Iustin Pop
        node.serial_no += 1
1462 ec0292f1 Iustin Pop
        mc_now += 1
1463 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1464 ec0292f1 Iustin Pop
        # this should not happen
1465 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1466 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1467 ec0292f1 Iustin Pop
      if mod_list:
1468 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1469 ec0292f1 Iustin Pop
        self._WriteConfig()
1470 ec0292f1 Iustin Pop
1471 ec0292f1 Iustin Pop
    return mod_list
1472 ec0292f1 Iustin Pop
1473 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1474 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1475 190e3cb6 Guido Trotter

1476 190e3cb6 Guido Trotter
    """
1477 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1478 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1479 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1480 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1481 190e3cb6 Guido Trotter
      # is not found anymore.
1482 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1483 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1484 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1485 190e3cb6 Guido Trotter
1486 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1487 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1488 190e3cb6 Guido Trotter

1489 190e3cb6 Guido Trotter
    """
1490 f936c153 Iustin Pop
    nodegroup = node.group
1491 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1492 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1493 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1494 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1495 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1496 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1497 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1498 190e3cb6 Guido Trotter
    else:
1499 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1500 190e3cb6 Guido Trotter
1501 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1502 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1503 a8083063 Iustin Pop

1504 a8083063 Iustin Pop
    """
1505 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1506 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1507 a8083063 Iustin Pop
1508 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1509 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1510 76d5d3a3 Iustin Pop

1511 76d5d3a3 Iustin Pop
    """
1512 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1513 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1514 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1515 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1516 76d5d3a3 Iustin Pop
1517 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1518 a8083063 Iustin Pop
    """Read the config data from disk.
1519 a8083063 Iustin Pop

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

1559 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1560 76d5d3a3 Iustin Pop
    whole configuration, etc.
1561 76d5d3a3 Iustin Pop

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

1568 76d5d3a3 Iustin Pop
    """
1569 76d5d3a3 Iustin Pop
    modified = False
1570 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1571 76d5d3a3 Iustin Pop
      if item.uuid is None:
1572 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1573 76d5d3a3 Iustin Pop
        modified = True
1574 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
1575 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
1576 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
1577 75cf411a Adeodato Simo
                                            members=[])
1578 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
1579 f9e81396 Guido Trotter
      modified = True
1580 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
1581 f936c153 Iustin Pop
      if not node.group:
1582 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
1583 190e3cb6 Guido Trotter
        modified = True
1584 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
1585 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
1586 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
1587 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
1588 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
1589 76d5d3a3 Iustin Pop
    if modified:
1590 76d5d3a3 Iustin Pop
      self._WriteConfig()
1591 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1592 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1593 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1594 4fae38c5 Guido Trotter
1595 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1596 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1597 a8083063 Iustin Pop

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

1601 a8083063 Iustin Pop
    """
1602 a8083063 Iustin Pop
    if self._offline:
1603 a8083063 Iustin Pop
      return True
1604 a4eae71f Michael Hanselmann
1605 a8083063 Iustin Pop
    bad = False
1606 a8083063 Iustin Pop
1607 6a5b8b4b Iustin Pop
    node_list = []
1608 6a5b8b4b Iustin Pop
    addr_list = []
1609 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1610 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1611 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1612 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1613 6b294c53 Iustin Pop
    # in between
1614 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1615 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1616 6a5b8b4b Iustin Pop
        continue
1617 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1618 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1619 6a5b8b4b Iustin Pop
        continue
1620 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1621 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1622 6b294c53 Iustin Pop
1623 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1624 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1625 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1626 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1627 1b54fc6c Guido Trotter
      if msg:
1628 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1629 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1630 1b54fc6c Guido Trotter
        logging.error(msg)
1631 a4eae71f Michael Hanselmann
1632 a4eae71f Michael Hanselmann
        if feedback_fn:
1633 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1634 a4eae71f Michael Hanselmann
1635 a8083063 Iustin Pop
        bad = True
1636 a4eae71f Michael Hanselmann
1637 a8083063 Iustin Pop
    return not bad
1638 a8083063 Iustin Pop
1639 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1640 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1641 a8083063 Iustin Pop

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

1702 054596f0 Iustin Pop
    @rtype: dict
1703 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1704 054596f0 Iustin Pop
        associated value
1705 054596f0 Iustin Pop

1706 054596f0 Iustin Pop
    """
1707 a3316e4a Iustin Pop
    fn = "\n".join
1708 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1709 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1710 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1711 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1712 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1713 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1714 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1715 a3316e4a Iustin Pop
1716 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1717 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1718 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1719 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1720 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1721 8113a52e Luca Bigliardi
                     if node.master_candidate)
1722 a3316e4a Iustin Pop
    node_data = fn(node_names)
1723 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1724 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1725 f56618e0 Iustin Pop
1726 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1727 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1728 4f7a6a10 Iustin Pop
1729 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
1730 4f7a6a10 Iustin Pop
1731 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
1732 0fbae49a Balazs Lecz
1733 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
1734 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
1735 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
1736 6f076453 Guido Trotter
1737 03d1dba2 Michael Hanselmann
    return {
1738 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1739 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1740 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1741 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1742 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1743 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1744 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1745 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1746 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1747 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1748 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1749 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1750 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1751 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
1752 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1753 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1754 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
1755 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
1756 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
1757 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
1758 03d1dba2 Michael Hanselmann
      }
1759 03d1dba2 Michael Hanselmann
1760 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1761 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
1762 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
1763 d367b66c Manuel Franceschini

1764 d367b66c Manuel Franceschini
    """
1765 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
1766 d367b66c Manuel Franceschini
1767 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1768 a8083063 Iustin Pop
  def GetVGName(self):
1769 a8083063 Iustin Pop
    """Return the volume group name.
1770 a8083063 Iustin Pop

1771 a8083063 Iustin Pop
    """
1772 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1773 a8083063 Iustin Pop
1774 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1775 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1776 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1777 89ff8e15 Manuel Franceschini

1778 89ff8e15 Manuel Franceschini
    """
1779 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1780 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1781 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1782 89ff8e15 Manuel Franceschini
1783 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1784 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
1785 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
1786 9e33896b Luca Bigliardi

1787 9e33896b Luca Bigliardi
    """
1788 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
1789 9e33896b Luca Bigliardi
1790 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
1791 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
1792 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
1793 9e33896b Luca Bigliardi

1794 9e33896b Luca Bigliardi
    """
1795 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
1796 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
1797 9e33896b Luca Bigliardi
    self._WriteConfig()
1798 9e33896b Luca Bigliardi
1799 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1800 a8083063 Iustin Pop
  def GetMACPrefix(self):
1801 a8083063 Iustin Pop
    """Return the mac prefix.
1802 a8083063 Iustin Pop

1803 a8083063 Iustin Pop
    """
1804 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1805 62779dd0 Iustin Pop
1806 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1807 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1808 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1809 62779dd0 Iustin Pop

1810 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1811 c41eea6e Iustin Pop
    @return: the cluster object
1812 62779dd0 Iustin Pop

1813 62779dd0 Iustin Pop
    """
1814 62779dd0 Iustin Pop
    return self._config_data.cluster
1815 e00fb268 Iustin Pop
1816 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1817 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
1818 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
1819 51cb1581 Luca Bigliardi

1820 51cb1581 Luca Bigliardi
    """
1821 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
1822 51cb1581 Luca Bigliardi
1823 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1824 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1825 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1826 e00fb268 Iustin Pop

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

1833 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1834 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1835 c41eea6e Iustin Pop
        the cluster
1836 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1837 c41eea6e Iustin Pop

1838 e00fb268 Iustin Pop
    """
1839 e00fb268 Iustin Pop
    if self._config_data is None:
1840 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1841 3ecf6786 Iustin Pop
                                   " cannot save.")
1842 f34901f8 Iustin Pop
    update_serial = False
1843 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1844 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1845 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1846 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1847 f34901f8 Iustin Pop
      update_serial = True
1848 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1849 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1850 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
1851 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
1852 e00fb268 Iustin Pop
    else:
1853 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1854 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1855 e00fb268 Iustin Pop
    if not test:
1856 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1857 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1858 f34901f8 Iustin Pop
    target.serial_no += 1
1859 d693c864 Iustin Pop
    target.mtime = now = time.time()
1860 f34901f8 Iustin Pop
1861 cff4c037 Iustin Pop
    if update_serial:
1862 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1863 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1864 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1865 b989e85d Iustin Pop
1866 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1867 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1868 61cf6b5e Iustin Pop
1869 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
1870 73064714 Guido Trotter
1871 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
1872 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
1873 73064714 Guido Trotter
    """Drop per-execution-context reservations
1874 73064714 Guido Trotter

1875 73064714 Guido Trotter
    """
1876 d8aee57e Iustin Pop
    for rm in self._all_rms:
1877 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)