Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ c9f4b8e6

History | View | Annotate | Download (66.5 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 b459a848 Andrea Spadaccini
# pylint: disable=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 fe698b38 Michael Hanselmann
def _MatchNameComponentIgnoreCase(short_name, names):
129 3a93eebb Michael Hanselmann
  """Wrapper around L{utils.text.MatchNameComponent}.
130 fe698b38 Michael Hanselmann

131 fe698b38 Michael Hanselmann
  """
132 fe698b38 Michael Hanselmann
  return utils.MatchNameComponent(short_name, names, case_sensitive=False)
133 fe698b38 Michael Hanselmann
134 fe698b38 Michael Hanselmann
135 a8083063 Iustin Pop
class ConfigWriter:
136 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
137 a8083063 Iustin Pop

138 d8aee57e Iustin Pop
  @ivar _temporary_lvs: reservation manager for temporary LVs
139 d8aee57e Iustin Pop
  @ivar _all_rms: a list of all temporary reservation managers
140 d8aee57e Iustin Pop

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

174 a8083063 Iustin Pop
    """
175 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
176 a8083063 Iustin Pop
177 36b66e6e Guido Trotter
  def _GenerateOneMAC(self):
178 36b66e6e Guido Trotter
    """Generate one mac address
179 36b66e6e Guido Trotter

180 36b66e6e Guido Trotter
    """
181 36b66e6e Guido Trotter
    prefix = self._config_data.cluster.mac_prefix
182 36b66e6e Guido Trotter
    byte1 = random.randrange(0, 256)
183 36b66e6e Guido Trotter
    byte2 = random.randrange(0, 256)
184 36b66e6e Guido Trotter
    byte3 = random.randrange(0, 256)
185 36b66e6e Guido Trotter
    mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
186 36b66e6e Guido Trotter
    return mac
187 36b66e6e Guido Trotter
188 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
189 5768e6a6 René Nussbaumer
  def GetNdParams(self, node):
190 5768e6a6 René Nussbaumer
    """Get the node params populated with cluster defaults.
191 5768e6a6 René Nussbaumer

192 ce523de1 Michael Hanselmann
    @type node: L{objects.Node}
193 5768e6a6 René Nussbaumer
    @param node: The node we want to know the params for
194 5768e6a6 René Nussbaumer
    @return: A dict with the filled in node params
195 5768e6a6 René Nussbaumer

196 5768e6a6 René Nussbaumer
    """
197 5768e6a6 René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
198 5768e6a6 René Nussbaumer
    return self._config_data.cluster.FillND(node, nodegroup)
199 5768e6a6 René Nussbaumer
200 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
201 36b66e6e Guido Trotter
  def GenerateMAC(self, ec_id):
202 a8083063 Iustin Pop
    """Generate a MAC for an instance.
203 a8083063 Iustin Pop

204 a8083063 Iustin Pop
    This should check the current instances for duplicates.
205 a8083063 Iustin Pop

206 a8083063 Iustin Pop
    """
207 36b66e6e Guido Trotter
    existing = self._AllMACs()
208 36b66e6e Guido Trotter
    return self._temporary_ids.Generate(existing, self._GenerateOneMAC, ec_id)
209 a8083063 Iustin Pop
210 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
211 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
212 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
213 1862d460 Alexander Schreiber

214 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
215 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
216 1862d460 Alexander Schreiber

217 1862d460 Alexander Schreiber
    """
218 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
219 36b66e6e Guido Trotter
    if mac in all_macs:
220 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
221 36b66e6e Guido Trotter
    else:
222 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
223 1862d460 Alexander Schreiber
224 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
225 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
226 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
227 d8aee57e Iustin Pop

228 d8aee57e Iustin Pop
    @type lv_name: string
229 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
230 d8aee57e Iustin Pop

231 d8aee57e Iustin Pop
    """
232 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
233 d8aee57e Iustin Pop
    if lv_name in all_lvs:
234 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
235 d8aee57e Iustin Pop
    else:
236 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
237 d8aee57e Iustin Pop
238 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
239 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
240 f9518d38 Iustin Pop
    """Generate a DRBD secret.
241 f9518d38 Iustin Pop

242 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
243 f9518d38 Iustin Pop

244 f9518d38 Iustin Pop
    """
245 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
246 afa1386e Guido Trotter
                                            utils.GenerateSecret,
247 afa1386e Guido Trotter
                                            ec_id)
248 8d9c3bef Michael Hanselmann
249 34e54ebc Iustin Pop
  def _AllLVs(self):
250 923b1523 Iustin Pop
    """Compute the list of all LVs.
251 923b1523 Iustin Pop

252 923b1523 Iustin Pop
    """
253 923b1523 Iustin Pop
    lvnames = set()
254 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
255 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
256 923b1523 Iustin Pop
      for lv_list in node_data.values():
257 923b1523 Iustin Pop
        lvnames.update(lv_list)
258 923b1523 Iustin Pop
    return lvnames
259 923b1523 Iustin Pop
260 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
261 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
262 34e54ebc Iustin Pop

263 34e54ebc Iustin Pop
    @type include_temporary: boolean
264 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
265 34e54ebc Iustin Pop
    @rtype: set
266 34e54ebc Iustin Pop
    @return: a set of IDs
267 34e54ebc Iustin Pop

268 34e54ebc Iustin Pop
    """
269 34e54ebc Iustin Pop
    existing = set()
270 34e54ebc Iustin Pop
    if include_temporary:
271 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
272 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
273 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
274 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
275 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
276 34e54ebc Iustin Pop
    return existing
277 34e54ebc Iustin Pop
278 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
279 430b923c Iustin Pop
    """Generate an unique UUID.
280 923b1523 Iustin Pop

281 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
282 923b1523 Iustin Pop
    duplicates.
283 923b1523 Iustin Pop

284 c41eea6e Iustin Pop
    @rtype: string
285 c41eea6e Iustin Pop
    @return: the unique id
286 923b1523 Iustin Pop

287 923b1523 Iustin Pop
    """
288 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
289 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
290 923b1523 Iustin Pop
291 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
292 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
293 430b923c Iustin Pop
    """Generate an unique ID.
294 430b923c Iustin Pop

295 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
296 430b923c Iustin Pop

297 4fae38c5 Guido Trotter
    @type ec_id: string
298 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
299 34d657ba Iustin Pop

300 34d657ba Iustin Pop
    """
301 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
302 34d657ba Iustin Pop
303 a8083063 Iustin Pop
  def _AllMACs(self):
304 a8083063 Iustin Pop
    """Return all MACs present in the config.
305 a8083063 Iustin Pop

306 c41eea6e Iustin Pop
    @rtype: list
307 c41eea6e Iustin Pop
    @return: the list of all MACs
308 c41eea6e Iustin Pop

309 a8083063 Iustin Pop
    """
310 a8083063 Iustin Pop
    result = []
311 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
312 a8083063 Iustin Pop
      for nic in instance.nics:
313 a8083063 Iustin Pop
        result.append(nic.mac)
314 a8083063 Iustin Pop
315 a8083063 Iustin Pop
    return result
316 a8083063 Iustin Pop
317 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
318 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
319 f9518d38 Iustin Pop

320 c41eea6e Iustin Pop
    @rtype: list
321 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
322 c41eea6e Iustin Pop

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

342 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
343 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
344 4b98ac29 Iustin Pop
    @type l_ids: list
345 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
346 4b98ac29 Iustin Pop
    @type p_ids: list
347 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
348 4b98ac29 Iustin Pop
    @rtype: list
349 4b98ac29 Iustin Pop
    @return: a list of error messages
350 4b98ac29 Iustin Pop

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

372 4a89c54a Iustin Pop
    @rtype: list
373 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
374 4a89c54a Iustin Pop
        configuration errors
375 4a89c54a Iustin Pop

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

592 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
593 4a89c54a Iustin Pop

594 4a89c54a Iustin Pop
    @rtype: list
595 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
596 4a89c54a Iustin Pop
        configuration errors
597 4a89c54a Iustin Pop

598 4a89c54a Iustin Pop
    """
599 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
600 4a89c54a Iustin Pop
601 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
602 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
603 a8083063 Iustin Pop

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

606 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
607 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
608 a8083063 Iustin Pop
    node.
609 a8083063 Iustin Pop

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

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

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

645 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
646 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
647 f78ede4e Guido Trotter
    node.
648 f78ede4e Guido Trotter

649 f78ede4e Guido Trotter
    """
650 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
651 f78ede4e Guido Trotter
652 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
653 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
654 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
655 b2fddf63 Iustin Pop

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

667 264bb3c5 Michael Hanselmann
    """
668 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
669 264bb3c5 Michael Hanselmann
670 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
671 a8083063 Iustin Pop
  def AllocatePort(self):
672 a8083063 Iustin Pop
    """Allocate a port.
673 a8083063 Iustin Pop

674 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
675 b2fddf63 Iustin Pop
    default port range (and in this case we increase
676 b2fddf63 Iustin Pop
    highest_used_port).
677 a8083063 Iustin Pop

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

696 4a89c54a Iustin Pop
    @rtype: (dict, list)
697 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
698 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
699 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
700 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
701 4a89c54a Iustin Pop
        should raise an exception
702 a81c53c9 Iustin Pop

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

736 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
737 6d2e83d5 Iustin Pop

738 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
739 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
740 6d2e83d5 Iustin Pop
        an empty list).
741 6d2e83d5 Iustin Pop

742 6d2e83d5 Iustin Pop
    """
743 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
744 4a89c54a Iustin Pop
    if duplicates:
745 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
746 4a89c54a Iustin Pop
                                      str(duplicates))
747 4a89c54a Iustin Pop
    return d_map
748 6d2e83d5 Iustin Pop
749 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
750 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
751 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
752 a81c53c9 Iustin Pop

753 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
754 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
755 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
756 a81c53c9 Iustin Pop
    order as the passed nodes.
757 a81c53c9 Iustin Pop

758 32388e6d Iustin Pop
    @type instance: string
759 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
760 32388e6d Iustin Pop

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

808 a81c53c9 Iustin Pop
    @type instance: string
809 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
810 a81c53c9 Iustin Pop
                     released
811 a81c53c9 Iustin Pop

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

823 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
824 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
825 61cf6b5e Iustin Pop
    functions.
826 61cf6b5e Iustin Pop

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

829 61cf6b5e Iustin Pop
    @type instance: string
830 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
831 61cf6b5e Iustin Pop
                     released
832 61cf6b5e Iustin Pop

833 61cf6b5e Iustin Pop
    """
834 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
835 61cf6b5e Iustin Pop
836 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
837 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
838 4a8b186a Michael Hanselmann
    """Get the configuration version.
839 4a8b186a Michael Hanselmann

840 4a8b186a Michael Hanselmann
    @return: Config version
841 4a8b186a Michael Hanselmann

842 4a8b186a Michael Hanselmann
    """
843 4a8b186a Michael Hanselmann
    return self._config_data.version
844 4a8b186a Michael Hanselmann
845 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
846 4a8b186a Michael Hanselmann
  def GetClusterName(self):
847 4a8b186a Michael Hanselmann
    """Get cluster name.
848 4a8b186a Michael Hanselmann

849 4a8b186a Michael Hanselmann
    @return: Cluster name
850 4a8b186a Michael Hanselmann

851 4a8b186a Michael Hanselmann
    """
852 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
853 4a8b186a Michael Hanselmann
854 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
855 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
856 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
857 4a8b186a Michael Hanselmann

858 4a8b186a Michael Hanselmann
    @return: Master hostname
859 4a8b186a Michael Hanselmann

860 4a8b186a Michael Hanselmann
    """
861 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
862 4a8b186a Michael Hanselmann
863 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
864 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
865 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
866 4a8b186a Michael Hanselmann

867 4a8b186a Michael Hanselmann
    @return: Master IP
868 4a8b186a Michael Hanselmann

869 4a8b186a Michael Hanselmann
    """
870 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
871 4a8b186a Michael Hanselmann
872 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
873 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
874 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
875 4a8b186a Michael Hanselmann

876 4a8b186a Michael Hanselmann
    """
877 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
878 4a8b186a Michael Hanselmann
879 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
880 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
881 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
882 5a8648eb Andrea Spadaccini

883 5a8648eb Andrea Spadaccini
    """
884 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
885 5a8648eb Andrea Spadaccini
886 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
887 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
888 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
889 4a8b186a Michael Hanselmann

890 4a8b186a Michael Hanselmann
    """
891 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
892 4a8b186a Michael Hanselmann
893 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
894 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
895 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
896 4b97f902 Apollon Oikonomopoulos

897 4b97f902 Apollon Oikonomopoulos
    """
898 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
899 4b97f902 Apollon Oikonomopoulos
900 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
901 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
902 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
903 4a8b186a Michael Hanselmann

904 4a8b186a Michael Hanselmann
    """
905 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
906 4a8b186a Michael Hanselmann
907 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
908 a8083063 Iustin Pop
  def GetHostKey(self):
909 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
910 a8083063 Iustin Pop

911 c41eea6e Iustin Pop
    @rtype: string
912 c41eea6e Iustin Pop
    @return: the rsa hostkey
913 a8083063 Iustin Pop

914 a8083063 Iustin Pop
    """
915 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
916 a8083063 Iustin Pop
917 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
918 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
919 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
920 bf4af505 Apollon Oikonomopoulos

921 bf4af505 Apollon Oikonomopoulos
    """
922 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
923 bf4af505 Apollon Oikonomopoulos
924 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
925 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
926 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
927 868a98ca Manuel Franceschini

928 868a98ca Manuel Franceschini
    @return: primary ip family
929 868a98ca Manuel Franceschini

930 868a98ca Manuel Franceschini
    """
931 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
932 868a98ca Manuel Franceschini
933 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
934 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
935 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
936 c9f4b8e6 Andrea Spadaccini

937 c9f4b8e6 Andrea Spadaccini
    @return: tuple consisting of (master_node, master_netdev, master_ip,
938 c9f4b8e6 Andrea Spadaccini
      master_netmask, ip_family)
939 c9f4b8e6 Andrea Spadaccini

940 c9f4b8e6 Andrea Spadaccini
    """
941 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
942 c9f4b8e6 Andrea Spadaccini
943 c9f4b8e6 Andrea Spadaccini
    return (cluster.master_node,
944 c9f4b8e6 Andrea Spadaccini
      cluster.master_ip,
945 c9f4b8e6 Andrea Spadaccini
      cluster.master_netdev,
946 c9f4b8e6 Andrea Spadaccini
      cluster.master_netmask,
947 c9f4b8e6 Andrea Spadaccini
      cluster.primary_ip_family)
948 c9f4b8e6 Andrea Spadaccini
949 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
950 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
951 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
952 e11a1b77 Adeodato Simo

953 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
954 90e99856 Adeodato Simo
    according to their default values.
955 90e99856 Adeodato Simo

956 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
957 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
958 e11a1b77 Adeodato Simo
    @type ec_id: string
959 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
960 e11a1b77 Adeodato Simo
    @type check_uuid: bool
961 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
962 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
963 e11a1b77 Adeodato Simo
                       configuration already
964 e11a1b77 Adeodato Simo

965 e11a1b77 Adeodato Simo
    """
966 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
967 e11a1b77 Adeodato Simo
    self._WriteConfig()
968 e11a1b77 Adeodato Simo
969 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
970 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
971 e11a1b77 Adeodato Simo

972 e11a1b77 Adeodato Simo
    """
973 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
974 e11a1b77 Adeodato Simo
975 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
976 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
977 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
978 e11a1b77 Adeodato Simo
    if check_uuid:
979 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
980 e11a1b77 Adeodato Simo
981 18ffc0fe Stephen Shirley
    try:
982 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
983 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
984 18ffc0fe Stephen Shirley
      pass
985 18ffc0fe Stephen Shirley
    else:
986 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
987 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
988 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
989 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
990 18ffc0fe Stephen Shirley
991 e11a1b77 Adeodato Simo
    group.serial_no = 1
992 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
993 90e99856 Adeodato Simo
    group.UpgradeConfig()
994 e11a1b77 Adeodato Simo
995 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
996 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
997 e11a1b77 Adeodato Simo
998 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
999 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
1000 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
1001 e11a1b77 Adeodato Simo

1002 e11a1b77 Adeodato Simo
    @type group_uuid: string
1003 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1004 e11a1b77 Adeodato Simo

1005 e11a1b77 Adeodato Simo
    """
1006 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1007 e11a1b77 Adeodato Simo
1008 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1009 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1010 e11a1b77 Adeodato Simo
1011 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1012 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1013 0389c42a Stephen Shirley
1014 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1015 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1016 e11a1b77 Adeodato Simo
    self._WriteConfig()
1017 e11a1b77 Adeodato Simo
1018 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1019 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1020 eaa98a04 Guido Trotter

1021 eaa98a04 Guido Trotter
    @type target: string or None
1022 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1023 eaa98a04 Guido Trotter
    @rtype: string
1024 412b3531 Guido Trotter
    @return: nodegroup UUID
1025 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1026 eaa98a04 Guido Trotter

1027 eaa98a04 Guido Trotter
    """
1028 eaa98a04 Guido Trotter
    if target is None:
1029 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1030 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1031 eaa98a04 Guido Trotter
                                   " group must be specified explicitely.")
1032 eaa98a04 Guido Trotter
      else:
1033 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1034 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1035 eaa98a04 Guido Trotter
      return target
1036 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1037 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1038 eaa98a04 Guido Trotter
        return nodegroup.uuid
1039 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1040 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1041 eaa98a04 Guido Trotter
1042 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1043 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1044 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1045 e85d8982 Stephen Shirley

1046 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1047 e85d8982 Stephen Shirley

1048 e85d8982 Stephen Shirley
    @type target: string or None
1049 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1050 e85d8982 Stephen Shirley
    @rtype: string
1051 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1052 e85d8982 Stephen Shirley

1053 e85d8982 Stephen Shirley
    """
1054 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1055 e85d8982 Stephen Shirley
1056 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1057 648e4196 Guido Trotter
    """Lookup a node group.
1058 648e4196 Guido Trotter

1059 648e4196 Guido Trotter
    @type uuid: string
1060 648e4196 Guido Trotter
    @param uuid: group UUID
1061 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1062 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1063 648e4196 Guido Trotter

1064 648e4196 Guido Trotter
    """
1065 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1066 648e4196 Guido Trotter
      return None
1067 648e4196 Guido Trotter
1068 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1069 648e4196 Guido Trotter
1070 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1071 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1072 5768e6a6 René Nussbaumer
    """Lookup a node group.
1073 5768e6a6 René Nussbaumer

1074 5768e6a6 René Nussbaumer
    @type uuid: string
1075 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1076 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1077 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1078 5768e6a6 René Nussbaumer

1079 5768e6a6 René Nussbaumer
    """
1080 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1081 5768e6a6 René Nussbaumer
1082 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1083 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1084 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1085 622444e5 Iustin Pop

1086 622444e5 Iustin Pop
    """
1087 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1088 622444e5 Iustin Pop
1089 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1090 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1091 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1092 1ac6f2ad Guido Trotter

1093 1ac6f2ad Guido Trotter
    """
1094 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1095 1ac6f2ad Guido Trotter
1096 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1097 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1098 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1099 dac81741 Michael Hanselmann

1100 dac81741 Michael Hanselmann
    """
1101 dac81741 Michael Hanselmann
    ngfn = lambda node_name: self._UnlockedGetNodeInfo(node_name).group
1102 dac81741 Michael Hanselmann
    return frozenset(member_name
1103 dac81741 Michael Hanselmann
                     for node_name in nodes
1104 dac81741 Michael Hanselmann
                     for member_name in
1105 dac81741 Michael Hanselmann
                       self._UnlockedGetNodeGroup(ngfn(node_name)).members)
1106 dac81741 Michael Hanselmann
1107 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1108 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1109 a8083063 Iustin Pop
    """Add an instance to the config.
1110 a8083063 Iustin Pop

1111 a8083063 Iustin Pop
    This should be used after creating a new instance.
1112 a8083063 Iustin Pop

1113 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1114 c41eea6e Iustin Pop
    @param instance: the instance object
1115 c41eea6e Iustin Pop

1116 a8083063 Iustin Pop
    """
1117 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1118 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1119 a8083063 Iustin Pop
1120 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1121 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1122 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1123 923b1523 Iustin Pop
1124 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1125 e4640214 Guido Trotter
    for nic in instance.nics:
1126 e4640214 Guido Trotter
      if nic.mac in all_macs:
1127 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1128 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1129 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1130 430b923c Iustin Pop
1131 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1132 e4640214 Guido Trotter
1133 b989e85d Iustin Pop
    instance.serial_no = 1
1134 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1135 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1136 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1137 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1138 a8083063 Iustin Pop
    self._WriteConfig()
1139 a8083063 Iustin Pop
1140 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1141 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1142 430b923c Iustin Pop

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

1146 430b923c Iustin Pop
    """
1147 430b923c Iustin Pop
    if not item.uuid:
1148 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1149 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1150 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1151 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1152 430b923c Iustin Pop
1153 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1154 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1155 a8083063 Iustin Pop

1156 a8083063 Iustin Pop
    """
1157 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
1158 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1159 a8083063 Iustin Pop
1160 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1161 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1162 3ecf6786 Iustin Pop
                                      instance_name)
1163 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1164 0d68c45d Iustin Pop
    if instance.admin_up != status:
1165 0d68c45d Iustin Pop
      instance.admin_up = status
1166 b989e85d Iustin Pop
      instance.serial_no += 1
1167 d693c864 Iustin Pop
      instance.mtime = time.time()
1168 455a3445 Iustin Pop
      self._WriteConfig()
1169 a8083063 Iustin Pop
1170 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1171 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1172 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1173 6a408fb2 Iustin Pop

1174 6a408fb2 Iustin Pop
    """
1175 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
1176 6a408fb2 Iustin Pop
1177 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1178 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1179 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1180 a8083063 Iustin Pop

1181 a8083063 Iustin Pop
    """
1182 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1183 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1184 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1185 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1186 a8083063 Iustin Pop
    self._WriteConfig()
1187 a8083063 Iustin Pop
1188 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1189 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1190 fc95f88f Iustin Pop
    """Rename an instance.
1191 fc95f88f Iustin Pop

1192 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1193 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1194 fc95f88f Iustin Pop
    rename.
1195 fc95f88f Iustin Pop

1196 fc95f88f Iustin Pop
    """
1197 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
1198 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
1199 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
1200 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
1201 fc95f88f Iustin Pop
    inst.name = new_name
1202 b23c4333 Manuel Franceschini
1203 b23c4333 Manuel Franceschini
    for disk in inst.disks:
1204 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1205 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1206 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1207 3721d2fe Guido Trotter
        disk_fname = "disk%s" % disk.iv_name.split("/")[1]
1208 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
1209 c4feafe8 Iustin Pop
                                              utils.PathJoin(file_storage_dir,
1210 c4feafe8 Iustin Pop
                                                             inst.name,
1211 3721d2fe Guido Trotter
                                                             disk_fname))
1212 b23c4333 Manuel Franceschini
1213 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1214 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1215 1fc34c48 Michael Hanselmann
1216 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
1217 fc95f88f Iustin Pop
    self._WriteConfig()
1218 fc95f88f Iustin Pop
1219 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1220 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
1221 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1222 a8083063 Iustin Pop

1223 a8083063 Iustin Pop
    """
1224 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
1225 a8083063 Iustin Pop
1226 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1227 94bbfece Iustin Pop
    """Get the list of instances.
1228 94bbfece Iustin Pop

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

1231 94bbfece Iustin Pop
    """
1232 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1233 94bbfece Iustin Pop
1234 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1235 a8083063 Iustin Pop
  def GetInstanceList(self):
1236 a8083063 Iustin Pop
    """Get the list of instances.
1237 a8083063 Iustin Pop

1238 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1239 c41eea6e Iustin Pop
        'instance1.example.com']
1240 a8083063 Iustin Pop

1241 a8083063 Iustin Pop
    """
1242 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1243 a8083063 Iustin Pop
1244 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1245 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1246 a8083063 Iustin Pop

1247 a8083063 Iustin Pop
    """
1248 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1249 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1250 a8083063 Iustin Pop
1251 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1252 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1253 94bbfece Iustin Pop

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

1256 94bbfece Iustin Pop
    """
1257 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1258 94bbfece Iustin Pop
      return None
1259 94bbfece Iustin Pop
1260 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1261 94bbfece Iustin Pop
1262 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1263 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1264 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1265 a8083063 Iustin Pop

1266 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1267 a8083063 Iustin Pop
    an instance are taken from the live systems.
1268 a8083063 Iustin Pop

1269 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1270 c41eea6e Iustin Pop
        I{instance1.example.com}
1271 a8083063 Iustin Pop

1272 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1273 c41eea6e Iustin Pop
    @return: the instance object
1274 a8083063 Iustin Pop

1275 a8083063 Iustin Pop
    """
1276 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1277 a8083063 Iustin Pop
1278 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1279 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1280 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1281 2674690b Michael Hanselmann

1282 2674690b Michael Hanselmann
    @rtype: frozenset
1283 2674690b Michael Hanselmann

1284 2674690b Michael Hanselmann
    """
1285 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1286 2674690b Michael Hanselmann
    if not instance:
1287 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1288 2674690b Michael Hanselmann
1289 2674690b Michael Hanselmann
    if primary_only:
1290 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1291 2674690b Michael Hanselmann
    else:
1292 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1293 2674690b Michael Hanselmann
1294 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1295 2674690b Michael Hanselmann
                     for node_name in nodes)
1296 2674690b Michael Hanselmann
1297 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1298 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1299 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1300 71333cb9 Iustin Pop

1301 71333cb9 Iustin Pop
    @param instances: list of instance names
1302 71333cb9 Iustin Pop
    @rtype: list
1303 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1304 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1305 71333cb9 Iustin Pop
        node, while keeping the original order
1306 71333cb9 Iustin Pop

1307 71333cb9 Iustin Pop
    """
1308 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1309 71333cb9 Iustin Pop
1310 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1311 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1312 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1313 0b2de758 Iustin Pop

1314 0b2de758 Iustin Pop
    @rtype: dict
1315 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1316 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1317 0b2de758 Iustin Pop

1318 0b2de758 Iustin Pop
    """
1319 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1320 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1321 0b2de758 Iustin Pop
    return my_dict
1322 0b2de758 Iustin Pop
1323 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1324 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1325 a8083063 Iustin Pop
    """Add a node to the configuration.
1326 a8083063 Iustin Pop

1327 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1328 c41eea6e Iustin Pop
    @param node: a Node instance
1329 a8083063 Iustin Pop

1330 a8083063 Iustin Pop
    """
1331 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1332 d8470559 Michael Hanselmann
1333 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1334 430b923c Iustin Pop
1335 b989e85d Iustin Pop
    node.serial_no = 1
1336 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1337 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1338 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1339 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1340 a8083063 Iustin Pop
    self._WriteConfig()
1341 a8083063 Iustin Pop
1342 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1343 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1344 a8083063 Iustin Pop
    """Remove a node from the configuration.
1345 a8083063 Iustin Pop

1346 a8083063 Iustin Pop
    """
1347 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1348 d8470559 Michael Hanselmann
1349 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1350 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1351 a8083063 Iustin Pop
1352 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1353 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1354 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1355 a8083063 Iustin Pop
    self._WriteConfig()
1356 a8083063 Iustin Pop
1357 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1358 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1359 a8083063 Iustin Pop

1360 a8083063 Iustin Pop
    """
1361 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1362 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1363 a8083063 Iustin Pop
1364 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1365 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1366 a8083063 Iustin Pop

1367 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1368 c41eea6e Iustin Pop
    held.
1369 f78ede4e Guido Trotter

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

1372 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1373 c41eea6e Iustin Pop
    @return: the node object
1374 a8083063 Iustin Pop

1375 a8083063 Iustin Pop
    """
1376 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1377 a8083063 Iustin Pop
      return None
1378 a8083063 Iustin Pop
1379 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1380 a8083063 Iustin Pop
1381 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1382 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1383 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1384 f78ede4e Guido Trotter

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

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

1389 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1390 c41eea6e Iustin Pop
    @return: the node object
1391 f78ede4e Guido Trotter

1392 f78ede4e Guido Trotter
    """
1393 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1394 f78ede4e Guido Trotter
1395 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1396 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1397 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1398 8bf9e9a5 Iustin Pop

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

1401 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1402 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1403 8bf9e9a5 Iustin Pop

1404 8bf9e9a5 Iustin Pop
    """
1405 8bf9e9a5 Iustin Pop
    pri = []
1406 8bf9e9a5 Iustin Pop
    sec = []
1407 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1408 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1409 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1410 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1411 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1412 8bf9e9a5 Iustin Pop
    return (pri, sec)
1413 8bf9e9a5 Iustin Pop
1414 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1415 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1416 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1417 c71b049c Michael Hanselmann

1418 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1419 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1420 c71b049c Michael Hanselmann
    @rtype: frozenset
1421 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1422 c71b049c Michael Hanselmann

1423 c71b049c Michael Hanselmann
    """
1424 c71b049c Michael Hanselmann
    if primary_only:
1425 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1426 c71b049c Michael Hanselmann
    else:
1427 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1428 c71b049c Michael Hanselmann
1429 c71b049c Michael Hanselmann
    return frozenset(inst.name
1430 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1431 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1432 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1433 c71b049c Michael Hanselmann
1434 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1435 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1436 a8083063 Iustin Pop

1437 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1438 c41eea6e Iustin Pop
    held.
1439 c41eea6e Iustin Pop

1440 c41eea6e Iustin Pop
    @rtype: list
1441 f78ede4e Guido Trotter

1442 a8083063 Iustin Pop
    """
1443 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1444 a8083063 Iustin Pop
1445 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1446 f78ede4e Guido Trotter
  def GetNodeList(self):
1447 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1448 f78ede4e Guido Trotter

1449 f78ede4e Guido Trotter
    """
1450 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1451 f78ede4e Guido Trotter
1452 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1453 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1454 94a02bb5 Iustin Pop

1455 94a02bb5 Iustin Pop
    """
1456 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1457 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1458 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1459 94a02bb5 Iustin Pop
1460 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1461 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1462 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1463 6819dc49 Iustin Pop

1464 6819dc49 Iustin Pop
    """
1465 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1466 6819dc49 Iustin Pop
1467 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1468 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1469 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1470 075b62ca Iustin Pop

1471 075b62ca Iustin Pop
    """
1472 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1473 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1474 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1475 075b62ca Iustin Pop
1476 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1477 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1478 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1479 8bf9e9a5 Iustin Pop

1480 8bf9e9a5 Iustin Pop
    """
1481 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1482 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1483 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1484 8bf9e9a5 Iustin Pop
1485 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1486 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1487 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1488 f5eaa3c1 Iustin Pop

1489 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1490 f5eaa3c1 Iustin Pop
    @rtype: list
1491 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1492 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1493 f5eaa3c1 Iustin Pop
        order
1494 f5eaa3c1 Iustin Pop

1495 f5eaa3c1 Iustin Pop
    """
1496 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1497 f5eaa3c1 Iustin Pop
1498 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1499 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1500 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1501 d65e5776 Iustin Pop

1502 d65e5776 Iustin Pop
    @rtype: dict
1503 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1504 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1505 d65e5776 Iustin Pop

1506 d65e5776 Iustin Pop
    """
1507 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1508 ee14d800 Michael Hanselmann
1509 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1510 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1511 ee14d800 Michael Hanselmann

1512 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1513 ee14d800 Michael Hanselmann

1514 ee14d800 Michael Hanselmann
    """
1515 ee14d800 Michael Hanselmann
    return dict([(node, self._UnlockedGetNodeInfo(node))
1516 ee14d800 Michael Hanselmann
                 for node in self._UnlockedGetNodeList()])
1517 d65e5776 Iustin Pop
1518 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1519 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1520 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1521 9d5b1371 Michael Hanselmann

1522 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1523 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1524 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1525 9d5b1371 Michael Hanselmann

1526 9d5b1371 Michael Hanselmann
    """
1527 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1528 9d5b1371 Michael Hanselmann
1529 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1530 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1531 ec0292f1 Iustin Pop

1532 23f06b2b Iustin Pop
    @type exceptions: list
1533 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1534 ec0292f1 Iustin Pop
    @rtype: tuple
1535 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1536 ec0292f1 Iustin Pop

1537 ec0292f1 Iustin Pop
    """
1538 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1539 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1540 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1541 23f06b2b Iustin Pop
        continue
1542 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1543 ec0292f1 Iustin Pop
        mc_max += 1
1544 ec0292f1 Iustin Pop
      if node.master_candidate:
1545 ec0292f1 Iustin Pop
        mc_now += 1
1546 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1547 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1548 ec0292f1 Iustin Pop
1549 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1550 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1551 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1552 ec0292f1 Iustin Pop

1553 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1554 ec0292f1 Iustin Pop

1555 23f06b2b Iustin Pop
    @type exceptions: list
1556 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1557 ec0292f1 Iustin Pop
    @rtype: tuple
1558 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1559 ec0292f1 Iustin Pop

1560 ec0292f1 Iustin Pop
    """
1561 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1562 ec0292f1 Iustin Pop
1563 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1564 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1565 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1566 ec0292f1 Iustin Pop

1567 44485f49 Guido Trotter
    @type exceptions: list
1568 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1569 ec0292f1 Iustin Pop
    @rtype: list
1570 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1571 ec0292f1 Iustin Pop

1572 ec0292f1 Iustin Pop
    """
1573 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1574 ec0292f1 Iustin Pop
    mod_list = []
1575 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1576 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1577 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1578 ec0292f1 Iustin Pop
      for name in node_list:
1579 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1580 ec0292f1 Iustin Pop
          break
1581 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1582 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1583 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1584 ec0292f1 Iustin Pop
          continue
1585 ee513a66 Iustin Pop
        mod_list.append(node)
1586 ec0292f1 Iustin Pop
        node.master_candidate = True
1587 ec0292f1 Iustin Pop
        node.serial_no += 1
1588 ec0292f1 Iustin Pop
        mc_now += 1
1589 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1590 ec0292f1 Iustin Pop
        # this should not happen
1591 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1592 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1593 ec0292f1 Iustin Pop
      if mod_list:
1594 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1595 ec0292f1 Iustin Pop
        self._WriteConfig()
1596 ec0292f1 Iustin Pop
1597 ec0292f1 Iustin Pop
    return mod_list
1598 ec0292f1 Iustin Pop
1599 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1600 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1601 190e3cb6 Guido Trotter

1602 190e3cb6 Guido Trotter
    """
1603 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1604 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1605 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1606 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1607 190e3cb6 Guido Trotter
      # is not found anymore.
1608 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1609 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1610 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1611 190e3cb6 Guido Trotter
1612 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1613 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1614 190e3cb6 Guido Trotter

1615 190e3cb6 Guido Trotter
    """
1616 f936c153 Iustin Pop
    nodegroup = node.group
1617 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1618 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1619 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1620 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1621 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1622 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1623 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1624 190e3cb6 Guido Trotter
    else:
1625 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1626 190e3cb6 Guido Trotter
1627 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1628 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1629 a8083063 Iustin Pop

1630 a8083063 Iustin Pop
    """
1631 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1632 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1633 a8083063 Iustin Pop
1634 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1635 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1636 76d5d3a3 Iustin Pop

1637 76d5d3a3 Iustin Pop
    """
1638 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1639 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1640 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1641 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1642 76d5d3a3 Iustin Pop
1643 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1644 a8083063 Iustin Pop
    """Read the config data from disk.
1645 a8083063 Iustin Pop

1646 a8083063 Iustin Pop
    """
1647 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1648 13998ef2 Michael Hanselmann
1649 a8083063 Iustin Pop
    try:
1650 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1651 13998ef2 Michael Hanselmann
    except Exception, err:
1652 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1653 5b263ed7 Michael Hanselmann
1654 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1655 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1656 5b263ed7 Michael Hanselmann
1657 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1658 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1659 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1660 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1661 90d726a8 Iustin Pop
1662 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
1663 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
1664 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
1665 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
1666 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
1667 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
1668 eb180fe2 Iustin Pop
1669 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1670 90d726a8 Iustin Pop
    data.UpgradeConfig()
1671 90d726a8 Iustin Pop
1672 a8083063 Iustin Pop
    self._config_data = data
1673 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1674 0779e3aa Iustin Pop
    # ssconf update
1675 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1676 a8083063 Iustin Pop
1677 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1678 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1679 76d5d3a3 Iustin Pop
1680 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
1681 bd407597 Iustin Pop
1682 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1683 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1684 76d5d3a3 Iustin Pop

1685 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1686 76d5d3a3 Iustin Pop
    whole configuration, etc.
1687 76d5d3a3 Iustin Pop

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

1694 76d5d3a3 Iustin Pop
    """
1695 76d5d3a3 Iustin Pop
    modified = False
1696 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1697 76d5d3a3 Iustin Pop
      if item.uuid is None:
1698 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1699 76d5d3a3 Iustin Pop
        modified = True
1700 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
1701 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
1702 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
1703 75cf411a Adeodato Simo
                                            members=[])
1704 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
1705 f9e81396 Guido Trotter
      modified = True
1706 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
1707 f936c153 Iustin Pop
      if not node.group:
1708 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
1709 190e3cb6 Guido Trotter
        modified = True
1710 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
1711 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
1712 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
1713 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
1714 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
1715 76d5d3a3 Iustin Pop
    if modified:
1716 76d5d3a3 Iustin Pop
      self._WriteConfig()
1717 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1718 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1719 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1720 4fae38c5 Guido Trotter
1721 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1722 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1723 a8083063 Iustin Pop

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

1727 a8083063 Iustin Pop
    """
1728 a8083063 Iustin Pop
    if self._offline:
1729 a8083063 Iustin Pop
      return True
1730 a4eae71f Michael Hanselmann
1731 a8083063 Iustin Pop
    bad = False
1732 a8083063 Iustin Pop
1733 6a5b8b4b Iustin Pop
    node_list = []
1734 6a5b8b4b Iustin Pop
    addr_list = []
1735 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1736 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1737 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1738 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1739 6b294c53 Iustin Pop
    # in between
1740 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1741 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1742 6a5b8b4b Iustin Pop
        continue
1743 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1744 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1745 6a5b8b4b Iustin Pop
        continue
1746 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1747 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1748 6b294c53 Iustin Pop
1749 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
1750 415a7304 Michael Hanselmann
    result = \
1751 415a7304 Michael Hanselmann
      rpc.ConfigRunner(addr_list).call_upload_file(node_list, self._cfg_file)
1752 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1753 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1754 1b54fc6c Guido Trotter
      if msg:
1755 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1756 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1757 1b54fc6c Guido Trotter
        logging.error(msg)
1758 a4eae71f Michael Hanselmann
1759 a4eae71f Michael Hanselmann
        if feedback_fn:
1760 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1761 a4eae71f Michael Hanselmann
1762 a8083063 Iustin Pop
        bad = True
1763 a4eae71f Michael Hanselmann
1764 a8083063 Iustin Pop
    return not bad
1765 a8083063 Iustin Pop
1766 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1767 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1768 a8083063 Iustin Pop

1769 a8083063 Iustin Pop
    """
1770 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
1771 a4eae71f Michael Hanselmann
1772 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
1773 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
1774 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
1775 d2231b8c Iustin Pop
    # recovery to the user
1776 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1777 4a89c54a Iustin Pop
    if config_errors:
1778 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
1779 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
1780 d2231b8c Iustin Pop
      logging.critical(errmsg)
1781 d2231b8c Iustin Pop
      if feedback_fn:
1782 d2231b8c Iustin Pop
        feedback_fn(errmsg)
1783 d2231b8c Iustin Pop
1784 a8083063 Iustin Pop
    if destination is None:
1785 a8083063 Iustin Pop
      destination = self._cfg_file
1786 a8083063 Iustin Pop
    self._BumpSerialNo()
1787 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1788 13998ef2 Michael Hanselmann
1789 e60c73a1 René Nussbaumer
    getents = self._getents()
1790 bd407597 Iustin Pop
    try:
1791 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
1792 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
1793 bd407597 Iustin Pop
    except errors.LockError:
1794 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
1795 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
1796 bd407597 Iustin Pop
                                      " update")
1797 bd407597 Iustin Pop
    try:
1798 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
1799 bd407597 Iustin Pop
    finally:
1800 bd407597 Iustin Pop
      os.close(fd)
1801 13998ef2 Michael Hanselmann
1802 14e15659 Iustin Pop
    self.write_count += 1
1803 3d3a04bc Iustin Pop
1804 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
1805 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
1806 a8083063 Iustin Pop
1807 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
1808 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
1809 d9a855f1 Michael Hanselmann
      if not self._offline:
1810 415a7304 Michael Hanselmann
        result = rpc.ConfigRunner(None).call_write_ssconf_files(
1811 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
1812 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
1813 a4eae71f Michael Hanselmann
1814 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
1815 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
1816 e1e75d00 Iustin Pop
          if msg:
1817 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
1818 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
1819 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
1820 a4eae71f Michael Hanselmann
1821 a4eae71f Michael Hanselmann
            if feedback_fn:
1822 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
1823 a4eae71f Michael Hanselmann
1824 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
1825 54d1a06e Michael Hanselmann
1826 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
1827 054596f0 Iustin Pop
    """Return the values needed by ssconf.
1828 054596f0 Iustin Pop

1829 054596f0 Iustin Pop
    @rtype: dict
1830 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1831 054596f0 Iustin Pop
        associated value
1832 054596f0 Iustin Pop

1833 054596f0 Iustin Pop
    """
1834 a3316e4a Iustin Pop
    fn = "\n".join
1835 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1836 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1837 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1838 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1839 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1840 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1841 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1842 a3316e4a Iustin Pop
1843 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1844 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1845 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1846 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1847 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1848 8113a52e Luca Bigliardi
                     if node.master_candidate)
1849 a3316e4a Iustin Pop
    node_data = fn(node_names)
1850 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1851 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1852 f56618e0 Iustin Pop
1853 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1854 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1855 4f7a6a10 Iustin Pop
1856 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
1857 4f7a6a10 Iustin Pop
1858 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
1859 0fbae49a Balazs Lecz
1860 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
1861 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
1862 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
1863 6f076453 Guido Trotter
1864 2afc9238 Iustin Pop
    ssconf_values = {
1865 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1866 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1867 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1868 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
1869 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1870 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1871 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1872 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1873 5a8648eb Andrea Spadaccini
      constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
1874 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1875 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1876 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1877 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1878 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1879 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1880 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
1881 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1882 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1883 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
1884 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
1885 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
1886 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
1887 03d1dba2 Michael Hanselmann
      }
1888 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
1889 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
1890 2afc9238 Iustin Pop
    if bad_values:
1891 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
1892 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
1893 2afc9238 Iustin Pop
                                      " values: %s" % err)
1894 2afc9238 Iustin Pop
    return ssconf_values
1895 03d1dba2 Michael Hanselmann
1896 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1897 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
1898 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
1899 d367b66c Manuel Franceschini

1900 d367b66c Manuel Franceschini
    """
1901 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
1902 d367b66c Manuel Franceschini
1903 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1904 a8083063 Iustin Pop
  def GetVGName(self):
1905 a8083063 Iustin Pop
    """Return the volume group name.
1906 a8083063 Iustin Pop

1907 a8083063 Iustin Pop
    """
1908 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1909 a8083063 Iustin Pop
1910 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1911 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1912 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1913 89ff8e15 Manuel Franceschini

1914 89ff8e15 Manuel Franceschini
    """
1915 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1916 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1917 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1918 89ff8e15 Manuel Franceschini
1919 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1920 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
1921 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
1922 9e33896b Luca Bigliardi

1923 9e33896b Luca Bigliardi
    """
1924 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
1925 9e33896b Luca Bigliardi
1926 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
1927 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
1928 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
1929 9e33896b Luca Bigliardi

1930 9e33896b Luca Bigliardi
    """
1931 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
1932 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
1933 9e33896b Luca Bigliardi
    self._WriteConfig()
1934 9e33896b Luca Bigliardi
1935 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1936 a8083063 Iustin Pop
  def GetMACPrefix(self):
1937 a8083063 Iustin Pop
    """Return the mac prefix.
1938 a8083063 Iustin Pop

1939 a8083063 Iustin Pop
    """
1940 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1941 62779dd0 Iustin Pop
1942 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1943 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1944 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1945 62779dd0 Iustin Pop

1946 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1947 c41eea6e Iustin Pop
    @return: the cluster object
1948 62779dd0 Iustin Pop

1949 62779dd0 Iustin Pop
    """
1950 62779dd0 Iustin Pop
    return self._config_data.cluster
1951 e00fb268 Iustin Pop
1952 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
1953 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
1954 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
1955 51cb1581 Luca Bigliardi

1956 51cb1581 Luca Bigliardi
    """
1957 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
1958 51cb1581 Luca Bigliardi
1959 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1960 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1961 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1962 e00fb268 Iustin Pop

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

1969 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1970 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1971 c41eea6e Iustin Pop
        the cluster
1972 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1973 c41eea6e Iustin Pop

1974 e00fb268 Iustin Pop
    """
1975 e00fb268 Iustin Pop
    if self._config_data is None:
1976 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1977 3ecf6786 Iustin Pop
                                   " cannot save.")
1978 f34901f8 Iustin Pop
    update_serial = False
1979 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1980 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1981 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1982 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1983 f34901f8 Iustin Pop
      update_serial = True
1984 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1985 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1986 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
1987 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
1988 e00fb268 Iustin Pop
    else:
1989 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1990 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1991 e00fb268 Iustin Pop
    if not test:
1992 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1993 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1994 f34901f8 Iustin Pop
    target.serial_no += 1
1995 d693c864 Iustin Pop
    target.mtime = now = time.time()
1996 f34901f8 Iustin Pop
1997 cff4c037 Iustin Pop
    if update_serial:
1998 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1999 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2000 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2001 b989e85d Iustin Pop
2002 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2003 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
2004 61cf6b5e Iustin Pop
2005 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2006 73064714 Guido Trotter
2007 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2008 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2009 73064714 Guido Trotter
    """Drop per-execution-context reservations
2010 73064714 Guido Trotter

2011 73064714 Guido Trotter
    """
2012 d8aee57e Iustin Pop
    for rm in self._all_rms:
2013 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)