Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ cfdf561d

History | View | Annotate | Download (73.3 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 12378fe3 Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 54c31fd3 Michael Hanselmann
import itertools
42 a8083063 Iustin Pop
43 a8083063 Iustin Pop
from ganeti import errors
44 f78ede4e Guido Trotter
from ganeti import locking
45 a8083063 Iustin Pop
from ganeti import utils
46 a8083063 Iustin Pop
from ganeti import constants
47 a8083063 Iustin Pop
from ganeti import rpc
48 a8083063 Iustin Pop
from ganeti import objects
49 8d14b30d Iustin Pop
from ganeti import serializer
50 0fbae49a Balazs Lecz
from ganeti import uidpool
51 a744b676 Manuel Franceschini
from ganeti import netutils
52 e60c73a1 René Nussbaumer
from ganeti import runtime
53 243cdbcc Michael Hanselmann
54 243cdbcc Michael Hanselmann
55 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
56 f78ede4e Guido Trotter
57 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
58 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
59 4fae38c5 Guido Trotter
60 f78ede4e Guido Trotter
61 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
62 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
63 c41eea6e Iustin Pop

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

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

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

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

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

112 013da361 Guido Trotter
    """
113 013da361 Guido Trotter
    assert callable(generate_one_fn)
114 013da361 Guido Trotter
115 013da361 Guido Trotter
    all_elems = self.GetReserved()
116 013da361 Guido Trotter
    all_elems.update(existing)
117 013da361 Guido Trotter
    retries = 64
118 013da361 Guido Trotter
    while retries > 0:
119 013da361 Guido Trotter
      new_resource = generate_one_fn()
120 013da361 Guido Trotter
      if new_resource is not None and new_resource not in all_elems:
121 013da361 Guido Trotter
        break
122 013da361 Guido Trotter
    else:
123 013da361 Guido Trotter
      raise errors.ConfigurationError("Not able generate new resource"
124 013da361 Guido Trotter
                                      " (last tried: %s)" % new_resource)
125 013da361 Guido Trotter
    self.Reserve(ec_id, new_resource)
126 013da361 Guido Trotter
    return new_resource
127 013da361 Guido Trotter
128 013da361 Guido Trotter
129 fe698b38 Michael Hanselmann
def _MatchNameComponentIgnoreCase(short_name, names):
130 3a93eebb Michael Hanselmann
  """Wrapper around L{utils.text.MatchNameComponent}.
131 fe698b38 Michael Hanselmann

132 fe698b38 Michael Hanselmann
  """
133 fe698b38 Michael Hanselmann
  return utils.MatchNameComponent(short_name, names, case_sensitive=False)
134 fe698b38 Michael Hanselmann
135 fe698b38 Michael Hanselmann
136 82c54b5b Michael Hanselmann
def _CheckInstanceDiskIvNames(disks):
137 82c54b5b Michael Hanselmann
  """Checks if instance's disks' C{iv_name} attributes are in order.
138 82c54b5b Michael Hanselmann

139 82c54b5b Michael Hanselmann
  @type disks: list of L{objects.Disk}
140 82c54b5b Michael Hanselmann
  @param disks: List of disks
141 82c54b5b Michael Hanselmann
  @rtype: list of tuples; (int, string, string)
142 82c54b5b Michael Hanselmann
  @return: List of wrongly named disks, each tuple contains disk index,
143 82c54b5b Michael Hanselmann
    expected and actual name
144 82c54b5b Michael Hanselmann

145 82c54b5b Michael Hanselmann
  """
146 82c54b5b Michael Hanselmann
  result = []
147 82c54b5b Michael Hanselmann
148 82c54b5b Michael Hanselmann
  for (idx, disk) in enumerate(disks):
149 82c54b5b Michael Hanselmann
    exp_iv_name = "disk/%s" % idx
150 82c54b5b Michael Hanselmann
    if disk.iv_name != exp_iv_name:
151 82c54b5b Michael Hanselmann
      result.append((idx, exp_iv_name, disk.iv_name))
152 82c54b5b Michael Hanselmann
153 82c54b5b Michael Hanselmann
  return result
154 82c54b5b Michael Hanselmann
155 82c54b5b Michael Hanselmann
156 a8083063 Iustin Pop
class ConfigWriter:
157 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
158 a8083063 Iustin Pop

159 d8aee57e Iustin Pop
  @ivar _temporary_lvs: reservation manager for temporary LVs
160 d8aee57e Iustin Pop
  @ivar _all_rms: a list of all temporary reservation managers
161 d8aee57e Iustin Pop

162 098c0958 Michael Hanselmann
  """
163 eb180fe2 Iustin Pop
  def __init__(self, cfg_file=None, offline=False, _getents=runtime.GetEnts,
164 eb180fe2 Iustin Pop
               accept_foreign=False):
165 14e15659 Iustin Pop
    self.write_count = 0
166 f78ede4e Guido Trotter
    self._lock = _config_lock
167 a8083063 Iustin Pop
    self._config_data = None
168 a8083063 Iustin Pop
    self._offline = offline
169 a8083063 Iustin Pop
    if cfg_file is None:
170 a8083063 Iustin Pop
      self._cfg_file = constants.CLUSTER_CONF_FILE
171 a8083063 Iustin Pop
    else:
172 a8083063 Iustin Pop
      self._cfg_file = cfg_file
173 e60c73a1 René Nussbaumer
    self._getents = _getents
174 4fae38c5 Guido Trotter
    self._temporary_ids = TemporaryReservationManager()
175 a81c53c9 Iustin Pop
    self._temporary_drbds = {}
176 36b66e6e Guido Trotter
    self._temporary_macs = TemporaryReservationManager()
177 afa1386e Guido Trotter
    self._temporary_secrets = TemporaryReservationManager()
178 d8aee57e Iustin Pop
    self._temporary_lvs = TemporaryReservationManager()
179 d8aee57e Iustin Pop
    self._all_rms = [self._temporary_ids, self._temporary_macs,
180 d8aee57e Iustin Pop
                     self._temporary_secrets, self._temporary_lvs]
181 89e1fc26 Iustin Pop
    # Note: in order to prevent errors when resolving our name in
182 89e1fc26 Iustin Pop
    # _DistributeConfig, we compute it here once and reuse it; it's
183 89e1fc26 Iustin Pop
    # better to raise an error before starting to modify the config
184 89e1fc26 Iustin Pop
    # file than after it was modified
185 b705c7a6 Manuel Franceschini
    self._my_hostname = netutils.Hostname.GetSysName()
186 3c7f6c44 Iustin Pop
    self._last_cluster_serial = -1
187 bd407597 Iustin Pop
    self._cfg_id = None
188 b2acdbdc Michael Hanselmann
    self._context = None
189 eb180fe2 Iustin Pop
    self._OpenConfig(accept_foreign)
190 a8083063 Iustin Pop
191 b2acdbdc Michael Hanselmann
  def _GetRpc(self, address_list):
192 b2acdbdc Michael Hanselmann
    """Returns RPC runner for configuration.
193 b2acdbdc Michael Hanselmann

194 b2acdbdc Michael Hanselmann
    """
195 b2acdbdc Michael Hanselmann
    return rpc.ConfigRunner(self._context, address_list)
196 b2acdbdc Michael Hanselmann
197 b2acdbdc Michael Hanselmann
  def SetContext(self, context):
198 b2acdbdc Michael Hanselmann
    """Sets Ganeti context.
199 b2acdbdc Michael Hanselmann

200 b2acdbdc Michael Hanselmann
    """
201 b2acdbdc Michael Hanselmann
    self._context = context
202 b2acdbdc Michael Hanselmann
203 a8083063 Iustin Pop
  # this method needs to be static, so that we can call it on the class
204 a8083063 Iustin Pop
  @staticmethod
205 a8083063 Iustin Pop
  def IsCluster():
206 a8083063 Iustin Pop
    """Check if the cluster is configured.
207 a8083063 Iustin Pop

208 a8083063 Iustin Pop
    """
209 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
210 a8083063 Iustin Pop
211 36b66e6e Guido Trotter
  def _GenerateOneMAC(self):
212 36b66e6e Guido Trotter
    """Generate one mac address
213 36b66e6e Guido Trotter

214 36b66e6e Guido Trotter
    """
215 36b66e6e Guido Trotter
    prefix = self._config_data.cluster.mac_prefix
216 36b66e6e Guido Trotter
    byte1 = random.randrange(0, 256)
217 36b66e6e Guido Trotter
    byte2 = random.randrange(0, 256)
218 36b66e6e Guido Trotter
    byte3 = random.randrange(0, 256)
219 36b66e6e Guido Trotter
    mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
220 36b66e6e Guido Trotter
    return mac
221 36b66e6e Guido Trotter
222 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
223 5768e6a6 René Nussbaumer
  def GetNdParams(self, node):
224 5768e6a6 René Nussbaumer
    """Get the node params populated with cluster defaults.
225 5768e6a6 René Nussbaumer

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

230 5768e6a6 René Nussbaumer
    """
231 5768e6a6 René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
232 5768e6a6 René Nussbaumer
    return self._config_data.cluster.FillND(node, nodegroup)
233 5768e6a6 René Nussbaumer
234 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
235 36b66e6e Guido Trotter
  def GenerateMAC(self, ec_id):
236 a8083063 Iustin Pop
    """Generate a MAC for an instance.
237 a8083063 Iustin Pop

238 a8083063 Iustin Pop
    This should check the current instances for duplicates.
239 a8083063 Iustin Pop

240 a8083063 Iustin Pop
    """
241 36b66e6e Guido Trotter
    existing = self._AllMACs()
242 36b66e6e Guido Trotter
    return self._temporary_ids.Generate(existing, self._GenerateOneMAC, ec_id)
243 a8083063 Iustin Pop
244 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
245 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
246 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
247 1862d460 Alexander Schreiber

248 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
249 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
250 1862d460 Alexander Schreiber

251 1862d460 Alexander Schreiber
    """
252 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
253 36b66e6e Guido Trotter
    if mac in all_macs:
254 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
255 36b66e6e Guido Trotter
    else:
256 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
257 1862d460 Alexander Schreiber
258 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
259 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
260 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
261 d8aee57e Iustin Pop

262 d8aee57e Iustin Pop
    @type lv_name: string
263 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
264 d8aee57e Iustin Pop

265 d8aee57e Iustin Pop
    """
266 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
267 d8aee57e Iustin Pop
    if lv_name in all_lvs:
268 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
269 d8aee57e Iustin Pop
    else:
270 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
271 d8aee57e Iustin Pop
272 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
273 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
274 f9518d38 Iustin Pop
    """Generate a DRBD secret.
275 f9518d38 Iustin Pop

276 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
277 f9518d38 Iustin Pop

278 f9518d38 Iustin Pop
    """
279 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
280 afa1386e Guido Trotter
                                            utils.GenerateSecret,
281 afa1386e Guido Trotter
                                            ec_id)
282 8d9c3bef Michael Hanselmann
283 34e54ebc Iustin Pop
  def _AllLVs(self):
284 923b1523 Iustin Pop
    """Compute the list of all LVs.
285 923b1523 Iustin Pop

286 923b1523 Iustin Pop
    """
287 923b1523 Iustin Pop
    lvnames = set()
288 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
289 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
290 923b1523 Iustin Pop
      for lv_list in node_data.values():
291 923b1523 Iustin Pop
        lvnames.update(lv_list)
292 923b1523 Iustin Pop
    return lvnames
293 923b1523 Iustin Pop
294 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
295 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
296 34e54ebc Iustin Pop

297 34e54ebc Iustin Pop
    @type include_temporary: boolean
298 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
299 34e54ebc Iustin Pop
    @rtype: set
300 34e54ebc Iustin Pop
    @return: a set of IDs
301 34e54ebc Iustin Pop

302 34e54ebc Iustin Pop
    """
303 34e54ebc Iustin Pop
    existing = set()
304 34e54ebc Iustin Pop
    if include_temporary:
305 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
306 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
307 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
308 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
309 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
310 34e54ebc Iustin Pop
    return existing
311 34e54ebc Iustin Pop
312 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
313 430b923c Iustin Pop
    """Generate an unique UUID.
314 923b1523 Iustin Pop

315 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
316 923b1523 Iustin Pop
    duplicates.
317 923b1523 Iustin Pop

318 c41eea6e Iustin Pop
    @rtype: string
319 c41eea6e Iustin Pop
    @return: the unique id
320 923b1523 Iustin Pop

321 923b1523 Iustin Pop
    """
322 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
323 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
324 923b1523 Iustin Pop
325 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
326 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
327 430b923c Iustin Pop
    """Generate an unique ID.
328 430b923c Iustin Pop

329 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
330 430b923c Iustin Pop

331 4fae38c5 Guido Trotter
    @type ec_id: string
332 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
333 34d657ba Iustin Pop

334 34d657ba Iustin Pop
    """
335 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
336 34d657ba Iustin Pop
337 a8083063 Iustin Pop
  def _AllMACs(self):
338 a8083063 Iustin Pop
    """Return all MACs present in the config.
339 a8083063 Iustin Pop

340 c41eea6e Iustin Pop
    @rtype: list
341 c41eea6e Iustin Pop
    @return: the list of all MACs
342 c41eea6e Iustin Pop

343 a8083063 Iustin Pop
    """
344 a8083063 Iustin Pop
    result = []
345 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
346 a8083063 Iustin Pop
      for nic in instance.nics:
347 a8083063 Iustin Pop
        result.append(nic.mac)
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
    return result
350 a8083063 Iustin Pop
351 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
352 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
353 f9518d38 Iustin Pop

354 c41eea6e Iustin Pop
    @rtype: list
355 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
356 c41eea6e Iustin Pop

357 f9518d38 Iustin Pop
    """
358 f9518d38 Iustin Pop
    def helper(disk, result):
359 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
360 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
361 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
362 f9518d38 Iustin Pop
      if disk.children:
363 f9518d38 Iustin Pop
        for child in disk.children:
364 f9518d38 Iustin Pop
          helper(child, result)
365 f9518d38 Iustin Pop
366 f9518d38 Iustin Pop
    result = []
367 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
368 f9518d38 Iustin Pop
      for disk in instance.disks:
369 f9518d38 Iustin Pop
        helper(disk, result)
370 f9518d38 Iustin Pop
371 f9518d38 Iustin Pop
    return result
372 f9518d38 Iustin Pop
373 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
374 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
375 4b98ac29 Iustin Pop

376 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
377 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
378 4b98ac29 Iustin Pop
    @type l_ids: list
379 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
380 4b98ac29 Iustin Pop
    @type p_ids: list
381 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
382 4b98ac29 Iustin Pop
    @rtype: list
383 4b98ac29 Iustin Pop
    @return: a list of error messages
384 4b98ac29 Iustin Pop

385 4b98ac29 Iustin Pop
    """
386 4b98ac29 Iustin Pop
    result = []
387 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
388 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
389 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
390 25ae22e4 Iustin Pop
      else:
391 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
392 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
393 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
394 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
395 25ae22e4 Iustin Pop
      else:
396 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
397 4b98ac29 Iustin Pop
398 4b98ac29 Iustin Pop
    if disk.children:
399 4b98ac29 Iustin Pop
      for child in disk.children:
400 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
401 4b98ac29 Iustin Pop
    return result
402 4b98ac29 Iustin Pop
403 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
404 a8efbb40 Iustin Pop
    """Verify function.
405 a8efbb40 Iustin Pop

406 4a89c54a Iustin Pop
    @rtype: list
407 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
408 4a89c54a Iustin Pop
        configuration errors
409 4a89c54a Iustin Pop

410 a8083063 Iustin Pop
    """
411 b459a848 Andrea Spadaccini
    # pylint: disable=R0914
412 a8083063 Iustin Pop
    result = []
413 a8083063 Iustin Pop
    seen_macs = []
414 48ce9fd9 Iustin Pop
    ports = {}
415 a8083063 Iustin Pop
    data = self._config_data
416 7e01d204 Iustin Pop
    cluster = data.cluster
417 4b98ac29 Iustin Pop
    seen_lids = []
418 4b98ac29 Iustin Pop
    seen_pids = []
419 9a5fba23 Guido Trotter
420 9a5fba23 Guido Trotter
    # global cluster checks
421 7e01d204 Iustin Pop
    if not cluster.enabled_hypervisors:
422 9a5fba23 Guido Trotter
      result.append("enabled hypervisors list doesn't have any entries")
423 7e01d204 Iustin Pop
    invalid_hvs = set(cluster.enabled_hypervisors) - constants.HYPER_TYPES
424 9a5fba23 Guido Trotter
    if invalid_hvs:
425 9a5fba23 Guido Trotter
      result.append("enabled hypervisors contains invalid entries: %s" %
426 9a5fba23 Guido Trotter
                    invalid_hvs)
427 7e01d204 Iustin Pop
    missing_hvp = (set(cluster.enabled_hypervisors) -
428 7e01d204 Iustin Pop
                   set(cluster.hvparams.keys()))
429 9f3ac970 Iustin Pop
    if missing_hvp:
430 9f3ac970 Iustin Pop
      result.append("hypervisor parameters missing for the enabled"
431 9f3ac970 Iustin Pop
                    " hypervisor(s) %s" % utils.CommaJoin(missing_hvp))
432 9a5fba23 Guido Trotter
433 7e01d204 Iustin Pop
    if cluster.master_node not in data.nodes:
434 9a5fba23 Guido Trotter
      result.append("cluster has invalid primary node '%s'" %
435 7e01d204 Iustin Pop
                    cluster.master_node)
436 9a5fba23 Guido Trotter
437 26f2fd8d Iustin Pop
    def _helper(owner, attr, value, template):
438 26f2fd8d Iustin Pop
      try:
439 26f2fd8d Iustin Pop
        utils.ForceDictType(value, template)
440 26f2fd8d Iustin Pop
      except errors.GenericError, err:
441 26f2fd8d Iustin Pop
        result.append("%s has invalid %s: %s" % (owner, attr, err))
442 26f2fd8d Iustin Pop
443 26f2fd8d Iustin Pop
    def _helper_nic(owner, params):
444 26f2fd8d Iustin Pop
      try:
445 26f2fd8d Iustin Pop
        objects.NIC.CheckParameterSyntax(params)
446 26f2fd8d Iustin Pop
      except errors.ConfigurationError, err:
447 26f2fd8d Iustin Pop
        result.append("%s has invalid nicparams: %s" % (owner, err))
448 26f2fd8d Iustin Pop
449 918eb80b Agata Murawska
    def _helper_ipolicy(owner, params):
450 918eb80b Agata Murawska
      try:
451 918eb80b Agata Murawska
        objects.InstancePolicy.CheckParameterSyntax(params)
452 918eb80b Agata Murawska
      except errors.ConfigurationError, err:
453 918eb80b Agata Murawska
        result.append("%s has invalid instance policy: %s" % (owner, err))
454 918eb80b Agata Murawska
455 918eb80b Agata Murawska
    def _helper_ispecs(owner, params):
456 2cc673a3 Iustin Pop
      for key, value in params.items():
457 12378fe3 Iustin Pop
        if key in constants.IPOLICY_ISPECS:
458 2cc673a3 Iustin Pop
          fullkey = "ipolicy/" + key
459 2cc673a3 Iustin Pop
          _helper(owner, fullkey, value, constants.ISPECS_PARAMETER_TYPES)
460 2cc673a3 Iustin Pop
        else:
461 2cc673a3 Iustin Pop
          # FIXME: assuming list type
462 ff6c5e55 Iustin Pop
          if key in constants.IPOLICY_PARAMETERS:
463 ff6c5e55 Iustin Pop
            exp_type = float
464 ff6c5e55 Iustin Pop
          else:
465 ff6c5e55 Iustin Pop
            exp_type = list
466 ff6c5e55 Iustin Pop
          if not isinstance(value, exp_type):
467 2cc673a3 Iustin Pop
            result.append("%s has invalid instance policy: for %s,"
468 ff6c5e55 Iustin Pop
                          " expecting %s, got %s" %
469 ff6c5e55 Iustin Pop
                          (owner, key, exp_type.__name__, type(value)))
470 918eb80b Agata Murawska
471 26f2fd8d Iustin Pop
    # check cluster parameters
472 26f2fd8d Iustin Pop
    _helper("cluster", "beparams", cluster.SimpleFillBE({}),
473 26f2fd8d Iustin Pop
            constants.BES_PARAMETER_TYPES)
474 26f2fd8d Iustin Pop
    _helper("cluster", "nicparams", cluster.SimpleFillNIC({}),
475 26f2fd8d Iustin Pop
            constants.NICS_PARAMETER_TYPES)
476 26f2fd8d Iustin Pop
    _helper_nic("cluster", cluster.SimpleFillNIC({}))
477 26f2fd8d Iustin Pop
    _helper("cluster", "ndparams", cluster.SimpleFillND({}),
478 26f2fd8d Iustin Pop
            constants.NDS_PARAMETER_TYPES)
479 918eb80b Agata Murawska
    _helper_ipolicy("cluster", cluster.SimpleFillIPolicy({}))
480 918eb80b Agata Murawska
    _helper_ispecs("cluster", cluster.SimpleFillIPolicy({}))
481 26f2fd8d Iustin Pop
482 9a5fba23 Guido Trotter
    # per-instance checks
483 a8083063 Iustin Pop
    for instance_name in data.instances:
484 a8083063 Iustin Pop
      instance = data.instances[instance_name]
485 81196341 Iustin Pop
      if instance.name != instance_name:
486 81196341 Iustin Pop
        result.append("instance '%s' is indexed by wrong name '%s'" %
487 81196341 Iustin Pop
                      (instance.name, instance_name))
488 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
489 8522ceeb Iustin Pop
        result.append("instance '%s' has invalid primary node '%s'" %
490 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
491 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
492 a8083063 Iustin Pop
        if snode not in data.nodes:
493 8522ceeb Iustin Pop
          result.append("instance '%s' has invalid secondary node '%s'" %
494 a8083063 Iustin Pop
                        (instance_name, snode))
495 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
496 a8083063 Iustin Pop
        if nic.mac in seen_macs:
497 8522ceeb Iustin Pop
          result.append("instance '%s' has NIC %d mac %s duplicate" %
498 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
499 a8083063 Iustin Pop
        else:
500 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
501 26f2fd8d Iustin Pop
        if nic.nicparams:
502 26f2fd8d Iustin Pop
          filled = cluster.SimpleFillNIC(nic.nicparams)
503 26f2fd8d Iustin Pop
          owner = "instance %s nic %d" % (instance.name, idx)
504 26f2fd8d Iustin Pop
          _helper(owner, "nicparams",
505 26f2fd8d Iustin Pop
                  filled, constants.NICS_PARAMETER_TYPES)
506 26f2fd8d Iustin Pop
          _helper_nic(owner, filled)
507 26f2fd8d Iustin Pop
508 26f2fd8d Iustin Pop
      # parameter checks
509 26f2fd8d Iustin Pop
      if instance.beparams:
510 26f2fd8d Iustin Pop
        _helper("instance %s" % instance.name, "beparams",
511 26f2fd8d Iustin Pop
                cluster.FillBE(instance), constants.BES_PARAMETER_TYPES)
512 48ce9fd9 Iustin Pop
513 48ce9fd9 Iustin Pop
      # gather the drbd ports for duplicate checks
514 e2569c1d Michael Hanselmann
      for (idx, dsk) in enumerate(instance.disks):
515 48ce9fd9 Iustin Pop
        if dsk.dev_type in constants.LDS_DRBD:
516 48ce9fd9 Iustin Pop
          tcp_port = dsk.logical_id[2]
517 48ce9fd9 Iustin Pop
          if tcp_port not in ports:
518 48ce9fd9 Iustin Pop
            ports[tcp_port] = []
519 e2569c1d Michael Hanselmann
          ports[tcp_port].append((instance.name, "drbd disk %s" % idx))
520 48ce9fd9 Iustin Pop
      # gather network port reservation
521 48ce9fd9 Iustin Pop
      net_port = getattr(instance, "network_port", None)
522 48ce9fd9 Iustin Pop
      if net_port is not None:
523 48ce9fd9 Iustin Pop
        if net_port not in ports:
524 48ce9fd9 Iustin Pop
          ports[net_port] = []
525 48ce9fd9 Iustin Pop
        ports[net_port].append((instance.name, "network port"))
526 48ce9fd9 Iustin Pop
527 332d0e37 Iustin Pop
      # instance disk verify
528 332d0e37 Iustin Pop
      for idx, disk in enumerate(instance.disks):
529 332d0e37 Iustin Pop
        result.extend(["instance '%s' disk %d error: %s" %
530 332d0e37 Iustin Pop
                       (instance.name, idx, msg) for msg in disk.Verify()])
531 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(disk, seen_lids, seen_pids))
532 332d0e37 Iustin Pop
533 82c54b5b Michael Hanselmann
      wrong_names = _CheckInstanceDiskIvNames(instance.disks)
534 82c54b5b Michael Hanselmann
      if wrong_names:
535 82c54b5b Michael Hanselmann
        tmp = "; ".join(("name of disk %s should be '%s', but is '%s'" %
536 82c54b5b Michael Hanselmann
                         (idx, exp_name, actual_name))
537 82c54b5b Michael Hanselmann
                        for (idx, exp_name, actual_name) in wrong_names)
538 82c54b5b Michael Hanselmann
539 82c54b5b Michael Hanselmann
        result.append("Instance '%s' has wrongly named disks: %s" %
540 82c54b5b Michael Hanselmann
                      (instance.name, tmp))
541 82c54b5b Michael Hanselmann
542 48ce9fd9 Iustin Pop
    # cluster-wide pool of free ports
543 7e01d204 Iustin Pop
    for free_port in cluster.tcpudp_port_pool:
544 48ce9fd9 Iustin Pop
      if free_port not in ports:
545 48ce9fd9 Iustin Pop
        ports[free_port] = []
546 48ce9fd9 Iustin Pop
      ports[free_port].append(("cluster", "port marked as free"))
547 48ce9fd9 Iustin Pop
548 48ce9fd9 Iustin Pop
    # compute tcp/udp duplicate ports
549 48ce9fd9 Iustin Pop
    keys = ports.keys()
550 48ce9fd9 Iustin Pop
    keys.sort()
551 48ce9fd9 Iustin Pop
    for pnum in keys:
552 48ce9fd9 Iustin Pop
      pdata = ports[pnum]
553 48ce9fd9 Iustin Pop
      if len(pdata) > 1:
554 1f864b60 Iustin Pop
        txt = utils.CommaJoin(["%s/%s" % val for val in pdata])
555 48ce9fd9 Iustin Pop
        result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
556 48ce9fd9 Iustin Pop
557 48ce9fd9 Iustin Pop
    # highest used tcp port check
558 48ce9fd9 Iustin Pop
    if keys:
559 7e01d204 Iustin Pop
      if keys[-1] > cluster.highest_used_port:
560 48ce9fd9 Iustin Pop
        result.append("Highest used port mismatch, saved %s, computed %s" %
561 7e01d204 Iustin Pop
                      (cluster.highest_used_port, keys[-1]))
562 a8efbb40 Iustin Pop
563 7e01d204 Iustin Pop
    if not data.nodes[cluster.master_node].master_candidate:
564 3a26773f Iustin Pop
      result.append("Master node is not a master candidate")
565 3a26773f Iustin Pop
566 4a89c54a Iustin Pop
    # master candidate checks
567 e623dbe3 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats()
568 ec0292f1 Iustin Pop
    if mc_now < mc_max:
569 ec0292f1 Iustin Pop
      result.append("Not enough master candidates: actual %d, target %d" %
570 ec0292f1 Iustin Pop
                    (mc_now, mc_max))
571 48ce9fd9 Iustin Pop
572 5bf07049 Iustin Pop
    # node checks
573 81196341 Iustin Pop
    for node_name, node in data.nodes.items():
574 81196341 Iustin Pop
      if node.name != node_name:
575 81196341 Iustin Pop
        result.append("Node '%s' is indexed by wrong name '%s'" %
576 81196341 Iustin Pop
                      (node.name, node_name))
577 5bf07049 Iustin Pop
      if [node.master_candidate, node.drained, node.offline].count(True) > 1:
578 5bf07049 Iustin Pop
        result.append("Node %s state is invalid: master_candidate=%s,"
579 5bf07049 Iustin Pop
                      " drain=%s, offline=%s" %
580 3d889a7d Michael Hanselmann
                      (node.name, node.master_candidate, node.drained,
581 5bf07049 Iustin Pop
                       node.offline))
582 26f2fd8d Iustin Pop
      if node.group not in data.nodegroups:
583 26f2fd8d Iustin Pop
        result.append("Node '%s' has invalid group '%s'" %
584 26f2fd8d Iustin Pop
                      (node.name, node.group))
585 26f2fd8d Iustin Pop
      else:
586 26f2fd8d Iustin Pop
        _helper("node %s" % node.name, "ndparams",
587 26f2fd8d Iustin Pop
                cluster.FillND(node, data.nodegroups[node.group]),
588 26f2fd8d Iustin Pop
                constants.NDS_PARAMETER_TYPES)
589 5bf07049 Iustin Pop
590 6520ba14 Guido Trotter
    # nodegroups checks
591 ace16501 Guido Trotter
    nodegroups_names = set()
592 6520ba14 Guido Trotter
    for nodegroup_uuid in data.nodegroups:
593 6520ba14 Guido Trotter
      nodegroup = data.nodegroups[nodegroup_uuid]
594 6520ba14 Guido Trotter
      if nodegroup.uuid != nodegroup_uuid:
595 913cc25e Adeodato Simo
        result.append("node group '%s' (uuid: '%s') indexed by wrong uuid '%s'"
596 6520ba14 Guido Trotter
                      % (nodegroup.name, nodegroup.uuid, nodegroup_uuid))
597 485ba212 Guido Trotter
      if utils.UUID_RE.match(nodegroup.name.lower()):
598 913cc25e Adeodato Simo
        result.append("node group '%s' (uuid: '%s') has uuid-like name" %
599 485ba212 Guido Trotter
                      (nodegroup.name, nodegroup.uuid))
600 ace16501 Guido Trotter
      if nodegroup.name in nodegroups_names:
601 913cc25e Adeodato Simo
        result.append("duplicate node group name '%s'" % nodegroup.name)
602 ace16501 Guido Trotter
      else:
603 ace16501 Guido Trotter
        nodegroups_names.add(nodegroup.name)
604 81e3ab4f Agata Murawska
      group_name = "group %s" % nodegroup.name
605 81e3ab4f Agata Murawska
      _helper_ipolicy(group_name, cluster.SimpleFillIPolicy(nodegroup.ipolicy))
606 81e3ab4f Agata Murawska
      _helper_ispecs(group_name, cluster.SimpleFillIPolicy(nodegroup.ipolicy))
607 26f2fd8d Iustin Pop
      if nodegroup.ndparams:
608 81e3ab4f Agata Murawska
        _helper(group_name, "ndparams",
609 26f2fd8d Iustin Pop
                cluster.SimpleFillND(nodegroup.ndparams),
610 26f2fd8d Iustin Pop
                constants.NDS_PARAMETER_TYPES)
611 26f2fd8d Iustin Pop
612 4a89c54a Iustin Pop
    # drbd minors check
613 1122eb25 Iustin Pop
    _, duplicates = self._UnlockedComputeDRBDMap()
614 4a89c54a Iustin Pop
    for node, minor, instance_a, instance_b in duplicates:
615 4a89c54a Iustin Pop
      result.append("DRBD minor %d on node %s is assigned twice to instances"
616 4a89c54a Iustin Pop
                    " %s and %s" % (minor, node, instance_a, instance_b))
617 4a89c54a Iustin Pop
618 0ce8f948 Iustin Pop
    # IP checks
619 7e01d204 Iustin Pop
    default_nicparams = cluster.nicparams[constants.PP_DEFAULT]
620 b8716596 Michael Hanselmann
    ips = {}
621 b8716596 Michael Hanselmann
622 b8716596 Michael Hanselmann
    def _AddIpAddress(ip, name):
623 b8716596 Michael Hanselmann
      ips.setdefault(ip, []).append(name)
624 b8716596 Michael Hanselmann
625 7e01d204 Iustin Pop
    _AddIpAddress(cluster.master_ip, "cluster_ip")
626 0ce8f948 Iustin Pop
627 0ce8f948 Iustin Pop
    for node in data.nodes.values():
628 b8716596 Michael Hanselmann
      _AddIpAddress(node.primary_ip, "node:%s/primary" % node.name)
629 0ce8f948 Iustin Pop
      if node.secondary_ip != node.primary_ip:
630 b8716596 Michael Hanselmann
        _AddIpAddress(node.secondary_ip, "node:%s/secondary" % node.name)
631 b8716596 Michael Hanselmann
632 b8716596 Michael Hanselmann
    for instance in data.instances.values():
633 b8716596 Michael Hanselmann
      for idx, nic in enumerate(instance.nics):
634 b8716596 Michael Hanselmann
        if nic.ip is None:
635 b8716596 Michael Hanselmann
          continue
636 b8716596 Michael Hanselmann
637 b8716596 Michael Hanselmann
        nicparams = objects.FillDict(default_nicparams, nic.nicparams)
638 b8716596 Michael Hanselmann
        nic_mode = nicparams[constants.NIC_MODE]
639 b8716596 Michael Hanselmann
        nic_link = nicparams[constants.NIC_LINK]
640 b8716596 Michael Hanselmann
641 b8716596 Michael Hanselmann
        if nic_mode == constants.NIC_MODE_BRIDGED:
642 b8716596 Michael Hanselmann
          link = "bridge:%s" % nic_link
643 b8716596 Michael Hanselmann
        elif nic_mode == constants.NIC_MODE_ROUTED:
644 b8716596 Michael Hanselmann
          link = "route:%s" % nic_link
645 b8716596 Michael Hanselmann
        else:
646 b8716596 Michael Hanselmann
          raise errors.ProgrammerError("NIC mode '%s' not handled" % nic_mode)
647 b8716596 Michael Hanselmann
648 b8716596 Michael Hanselmann
        _AddIpAddress("%s/%s" % (link, nic.ip),
649 b8716596 Michael Hanselmann
                      "instance:%s/nic:%d" % (instance.name, idx))
650 0ce8f948 Iustin Pop
651 0ce8f948 Iustin Pop
    for ip, owners in ips.items():
652 0ce8f948 Iustin Pop
      if len(owners) > 1:
653 0ce8f948 Iustin Pop
        result.append("IP address %s is used by multiple owners: %s" %
654 1f864b60 Iustin Pop
                      (ip, utils.CommaJoin(owners)))
655 b8716596 Michael Hanselmann
656 a8083063 Iustin Pop
    return result
657 a8083063 Iustin Pop
658 4a89c54a Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
659 4a89c54a Iustin Pop
  def VerifyConfig(self):
660 4a89c54a Iustin Pop
    """Verify function.
661 4a89c54a Iustin Pop

662 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
663 4a89c54a Iustin Pop

664 4a89c54a Iustin Pop
    @rtype: list
665 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
666 4a89c54a Iustin Pop
        configuration errors
667 4a89c54a Iustin Pop

668 4a89c54a Iustin Pop
    """
669 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
670 4a89c54a Iustin Pop
671 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
672 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
673 a8083063 Iustin Pop

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

676 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
677 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
678 a8083063 Iustin Pop
    node.
679 a8083063 Iustin Pop

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

682 a8083063 Iustin Pop
    """
683 a8083063 Iustin Pop
    if disk.children:
684 a8083063 Iustin Pop
      for child in disk.children:
685 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
686 a8083063 Iustin Pop
687 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
688 a8083063 Iustin Pop
      return
689 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
690 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
691 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
692 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
693 3ecf6786 Iustin Pop
                                        node_name)
694 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
695 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
696 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
697 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
698 a8083063 Iustin Pop
                                        " for %s" % str(disk))
699 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
700 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
701 a8083063 Iustin Pop
      if pnode == node_name:
702 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
703 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
704 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
705 a8083063 Iustin Pop
    else:
706 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
707 a8083063 Iustin Pop
    return
708 a8083063 Iustin Pop
709 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
710 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
711 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
712 f78ede4e Guido Trotter

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

715 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
716 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
717 f78ede4e Guido Trotter
    node.
718 f78ede4e Guido Trotter

719 f78ede4e Guido Trotter
    """
720 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
721 f78ede4e Guido Trotter
722 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
723 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
724 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
725 b2fddf63 Iustin Pop

726 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
727 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
728 3b3b1bca Dimitris Aragiorgis
        configuration is stable
729 3b3b1bca Dimitris Aragiorgis

730 b2fddf63 Iustin Pop
    """
731 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
732 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
733 264bb3c5 Michael Hanselmann
734 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
735 264bb3c5 Michael Hanselmann
736 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
737 b2fddf63 Iustin Pop
  def GetPortList(self):
738 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
739 264bb3c5 Michael Hanselmann

740 264bb3c5 Michael Hanselmann
    """
741 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
742 264bb3c5 Michael Hanselmann
743 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
744 a8083063 Iustin Pop
  def AllocatePort(self):
745 a8083063 Iustin Pop
    """Allocate a port.
746 a8083063 Iustin Pop

747 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
748 b2fddf63 Iustin Pop
    default port range (and in this case we increase
749 b2fddf63 Iustin Pop
    highest_used_port).
750 a8083063 Iustin Pop

751 a8083063 Iustin Pop
    """
752 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
753 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
754 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
755 264bb3c5 Michael Hanselmann
    else:
756 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
757 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
758 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
759 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
760 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
761 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
762 a8083063 Iustin Pop
763 a8083063 Iustin Pop
    self._WriteConfig()
764 a8083063 Iustin Pop
    return port
765 a8083063 Iustin Pop
766 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
767 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
768 a81c53c9 Iustin Pop

769 4a89c54a Iustin Pop
    @rtype: (dict, list)
770 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
771 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
772 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
773 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
774 4a89c54a Iustin Pop
        should raise an exception
775 a81c53c9 Iustin Pop

776 a81c53c9 Iustin Pop
    """
777 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
778 4a89c54a Iustin Pop
      duplicates = []
779 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
780 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
781 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
782 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
783 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
784 a81c53c9 Iustin Pop
          if port in used[node]:
785 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
786 4a89c54a Iustin Pop
          else:
787 4a89c54a Iustin Pop
            used[node][port] = instance_name
788 a81c53c9 Iustin Pop
      if disk.children:
789 a81c53c9 Iustin Pop
        for child in disk.children:
790 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
791 4a89c54a Iustin Pop
      return duplicates
792 a81c53c9 Iustin Pop
793 4a89c54a Iustin Pop
    duplicates = []
794 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
795 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
796 79b26a7a Iustin Pop
      for disk in instance.disks:
797 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
798 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
799 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
800 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
801 4a89c54a Iustin Pop
      else:
802 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
803 4a89c54a Iustin Pop
    return my_dict, duplicates
804 a81c53c9 Iustin Pop
805 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
806 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
807 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
808 6d2e83d5 Iustin Pop

809 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
810 6d2e83d5 Iustin Pop

811 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
812 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
813 6d2e83d5 Iustin Pop
        an empty list).
814 6d2e83d5 Iustin Pop

815 6d2e83d5 Iustin Pop
    """
816 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
817 4a89c54a Iustin Pop
    if duplicates:
818 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
819 4a89c54a Iustin Pop
                                      str(duplicates))
820 4a89c54a Iustin Pop
    return d_map
821 6d2e83d5 Iustin Pop
822 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
823 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
824 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
825 a81c53c9 Iustin Pop

826 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
827 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
828 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
829 a81c53c9 Iustin Pop
    order as the passed nodes.
830 a81c53c9 Iustin Pop

831 32388e6d Iustin Pop
    @type instance: string
832 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
833 32388e6d Iustin Pop

834 a81c53c9 Iustin Pop
    """
835 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
836 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
837 32388e6d Iustin Pop
838 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
839 4a89c54a Iustin Pop
    if duplicates:
840 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
841 4a89c54a Iustin Pop
                                      str(duplicates))
842 a81c53c9 Iustin Pop
    result = []
843 a81c53c9 Iustin Pop
    for nname in nodes:
844 a81c53c9 Iustin Pop
      ndata = d_map[nname]
845 a81c53c9 Iustin Pop
      if not ndata:
846 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
847 a81c53c9 Iustin Pop
        result.append(0)
848 a81c53c9 Iustin Pop
        ndata[0] = instance
849 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
850 a81c53c9 Iustin Pop
        continue
851 a81c53c9 Iustin Pop
      keys = ndata.keys()
852 a81c53c9 Iustin Pop
      keys.sort()
853 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
854 a81c53c9 Iustin Pop
      if ffree is None:
855 a81c53c9 Iustin Pop
        # return the next minor
856 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
857 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
858 a81c53c9 Iustin Pop
      else:
859 a81c53c9 Iustin Pop
        minor = ffree
860 4a89c54a Iustin Pop
      # double-check minor against current instances
861 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
862 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
863 4a89c54a Iustin Pop
              " already allocated to instance %s" %
864 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
865 a81c53c9 Iustin Pop
      ndata[minor] = instance
866 4a89c54a Iustin Pop
      # double-check minor against reservation
867 4a89c54a Iustin Pop
      r_key = (nname, minor)
868 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
869 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
870 4a89c54a Iustin Pop
              " reserved for instance %s" %
871 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
872 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
873 4a89c54a Iustin Pop
      result.append(minor)
874 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
875 a81c53c9 Iustin Pop
                  nodes, result)
876 a81c53c9 Iustin Pop
    return result
877 a81c53c9 Iustin Pop
878 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
879 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
880 a81c53c9 Iustin Pop

881 a81c53c9 Iustin Pop
    @type instance: string
882 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
883 a81c53c9 Iustin Pop
                     released
884 a81c53c9 Iustin Pop

885 a81c53c9 Iustin Pop
    """
886 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
887 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
888 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
889 a81c53c9 Iustin Pop
      if name == instance:
890 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
891 a81c53c9 Iustin Pop
892 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
893 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
894 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
895 61cf6b5e Iustin Pop

896 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
897 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
898 61cf6b5e Iustin Pop
    functions.
899 61cf6b5e Iustin Pop

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

902 61cf6b5e Iustin Pop
    @type instance: string
903 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
904 61cf6b5e Iustin Pop
                     released
905 61cf6b5e Iustin Pop

906 61cf6b5e Iustin Pop
    """
907 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
908 61cf6b5e Iustin Pop
909 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
910 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
911 4a8b186a Michael Hanselmann
    """Get the configuration version.
912 4a8b186a Michael Hanselmann

913 4a8b186a Michael Hanselmann
    @return: Config version
914 4a8b186a Michael Hanselmann

915 4a8b186a Michael Hanselmann
    """
916 4a8b186a Michael Hanselmann
    return self._config_data.version
917 4a8b186a Michael Hanselmann
918 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
919 4a8b186a Michael Hanselmann
  def GetClusterName(self):
920 4a8b186a Michael Hanselmann
    """Get cluster name.
921 4a8b186a Michael Hanselmann

922 4a8b186a Michael Hanselmann
    @return: Cluster name
923 4a8b186a Michael Hanselmann

924 4a8b186a Michael Hanselmann
    """
925 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
926 4a8b186a Michael Hanselmann
927 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
928 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
929 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
930 4a8b186a Michael Hanselmann

931 4a8b186a Michael Hanselmann
    @return: Master hostname
932 4a8b186a Michael Hanselmann

933 4a8b186a Michael Hanselmann
    """
934 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
935 4a8b186a Michael Hanselmann
936 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
937 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
938 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
939 4a8b186a Michael Hanselmann

940 4a8b186a Michael Hanselmann
    @return: Master IP
941 4a8b186a Michael Hanselmann

942 4a8b186a Michael Hanselmann
    """
943 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
944 4a8b186a Michael Hanselmann
945 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
946 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
947 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
948 4a8b186a Michael Hanselmann

949 4a8b186a Michael Hanselmann
    """
950 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
951 4a8b186a Michael Hanselmann
952 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
953 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
954 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
955 5a8648eb Andrea Spadaccini

956 5a8648eb Andrea Spadaccini
    """
957 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
958 5a8648eb Andrea Spadaccini
959 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
960 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
961 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
962 33be7576 Andrea Spadaccini

963 33be7576 Andrea Spadaccini
    """
964 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
965 33be7576 Andrea Spadaccini
966 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
967 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
968 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
969 4a8b186a Michael Hanselmann

970 4a8b186a Michael Hanselmann
    """
971 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
972 4a8b186a Michael Hanselmann
973 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
974 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
975 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
976 4b97f902 Apollon Oikonomopoulos

977 4b97f902 Apollon Oikonomopoulos
    """
978 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
979 4b97f902 Apollon Oikonomopoulos
980 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
981 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
982 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
983 4a8b186a Michael Hanselmann

984 4a8b186a Michael Hanselmann
    """
985 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
986 4a8b186a Michael Hanselmann
987 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
988 a8083063 Iustin Pop
  def GetHostKey(self):
989 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
990 a8083063 Iustin Pop

991 c41eea6e Iustin Pop
    @rtype: string
992 c41eea6e Iustin Pop
    @return: the rsa hostkey
993 a8083063 Iustin Pop

994 a8083063 Iustin Pop
    """
995 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
996 a8083063 Iustin Pop
997 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
998 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
999 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1000 bf4af505 Apollon Oikonomopoulos

1001 bf4af505 Apollon Oikonomopoulos
    """
1002 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1003 bf4af505 Apollon Oikonomopoulos
1004 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1005 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1006 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1007 868a98ca Manuel Franceschini

1008 868a98ca Manuel Franceschini
    @return: primary ip family
1009 868a98ca Manuel Franceschini

1010 868a98ca Manuel Franceschini
    """
1011 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1012 868a98ca Manuel Franceschini
1013 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1014 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1015 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1016 c9f4b8e6 Andrea Spadaccini

1017 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1018 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1019 c9f4b8e6 Andrea Spadaccini

1020 c9f4b8e6 Andrea Spadaccini
    """
1021 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1022 c79198a0 Andrea Spadaccini
    result = objects.MasterNetworkParameters(name=cluster.master_node,
1023 c79198a0 Andrea Spadaccini
      ip=cluster.master_ip,
1024 c79198a0 Andrea Spadaccini
      netmask=cluster.master_netmask,
1025 c79198a0 Andrea Spadaccini
      netdev=cluster.master_netdev,
1026 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1027 c9f4b8e6 Andrea Spadaccini
1028 f9d20654 Andrea Spadaccini
    return result
1029 f9d20654 Andrea Spadaccini
1030 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1031 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1032 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1033 e11a1b77 Adeodato Simo

1034 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1035 90e99856 Adeodato Simo
    according to their default values.
1036 90e99856 Adeodato Simo

1037 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1038 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1039 e11a1b77 Adeodato Simo
    @type ec_id: string
1040 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1041 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1042 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1043 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1044 e11a1b77 Adeodato Simo
                       configuration already
1045 e11a1b77 Adeodato Simo

1046 e11a1b77 Adeodato Simo
    """
1047 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1048 e11a1b77 Adeodato Simo
    self._WriteConfig()
1049 e11a1b77 Adeodato Simo
1050 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1051 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1052 e11a1b77 Adeodato Simo

1053 e11a1b77 Adeodato Simo
    """
1054 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
1055 e11a1b77 Adeodato Simo
1056 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
1057 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
1058 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
1059 e11a1b77 Adeodato Simo
    if check_uuid:
1060 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
1061 e11a1b77 Adeodato Simo
1062 18ffc0fe Stephen Shirley
    try:
1063 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
1064 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
1065 18ffc0fe Stephen Shirley
      pass
1066 18ffc0fe Stephen Shirley
    else:
1067 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
1068 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
1069 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
1070 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
1071 18ffc0fe Stephen Shirley
1072 e11a1b77 Adeodato Simo
    group.serial_no = 1
1073 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
1074 90e99856 Adeodato Simo
    group.UpgradeConfig()
1075 e11a1b77 Adeodato Simo
1076 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
1077 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1078 e11a1b77 Adeodato Simo
1079 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1080 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
1081 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
1082 e11a1b77 Adeodato Simo

1083 e11a1b77 Adeodato Simo
    @type group_uuid: string
1084 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1085 e11a1b77 Adeodato Simo

1086 e11a1b77 Adeodato Simo
    """
1087 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1088 e11a1b77 Adeodato Simo
1089 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1090 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1091 e11a1b77 Adeodato Simo
1092 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1093 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1094 0389c42a Stephen Shirley
1095 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1096 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1097 e11a1b77 Adeodato Simo
    self._WriteConfig()
1098 e11a1b77 Adeodato Simo
1099 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1100 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1101 eaa98a04 Guido Trotter

1102 eaa98a04 Guido Trotter
    @type target: string or None
1103 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1104 eaa98a04 Guido Trotter
    @rtype: string
1105 412b3531 Guido Trotter
    @return: nodegroup UUID
1106 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1107 eaa98a04 Guido Trotter

1108 eaa98a04 Guido Trotter
    """
1109 eaa98a04 Guido Trotter
    if target is None:
1110 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1111 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1112 eaa98a04 Guido Trotter
                                   " group must be specified explicitely.")
1113 eaa98a04 Guido Trotter
      else:
1114 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1115 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1116 eaa98a04 Guido Trotter
      return target
1117 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1118 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1119 eaa98a04 Guido Trotter
        return nodegroup.uuid
1120 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1121 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1122 eaa98a04 Guido Trotter
1123 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1124 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1125 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1126 e85d8982 Stephen Shirley

1127 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1128 e85d8982 Stephen Shirley

1129 e85d8982 Stephen Shirley
    @type target: string or None
1130 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1131 e85d8982 Stephen Shirley
    @rtype: string
1132 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1133 e85d8982 Stephen Shirley

1134 e85d8982 Stephen Shirley
    """
1135 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1136 e85d8982 Stephen Shirley
1137 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1138 648e4196 Guido Trotter
    """Lookup a node group.
1139 648e4196 Guido Trotter

1140 648e4196 Guido Trotter
    @type uuid: string
1141 648e4196 Guido Trotter
    @param uuid: group UUID
1142 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1143 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1144 648e4196 Guido Trotter

1145 648e4196 Guido Trotter
    """
1146 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1147 648e4196 Guido Trotter
      return None
1148 648e4196 Guido Trotter
1149 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1150 648e4196 Guido Trotter
1151 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1152 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1153 5768e6a6 René Nussbaumer
    """Lookup a node group.
1154 5768e6a6 René Nussbaumer

1155 5768e6a6 René Nussbaumer
    @type uuid: string
1156 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1157 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1158 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1159 5768e6a6 René Nussbaumer

1160 5768e6a6 René Nussbaumer
    """
1161 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1162 5768e6a6 René Nussbaumer
1163 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1164 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1165 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1166 622444e5 Iustin Pop

1167 622444e5 Iustin Pop
    """
1168 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1169 622444e5 Iustin Pop
1170 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1171 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1172 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1173 1ac6f2ad Guido Trotter

1174 1ac6f2ad Guido Trotter
    """
1175 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1176 1ac6f2ad Guido Trotter
1177 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1178 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1179 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1180 dac81741 Michael Hanselmann

1181 dac81741 Michael Hanselmann
    """
1182 dac81741 Michael Hanselmann
    ngfn = lambda node_name: self._UnlockedGetNodeInfo(node_name).group
1183 dac81741 Michael Hanselmann
    return frozenset(member_name
1184 dac81741 Michael Hanselmann
                     for node_name in nodes
1185 dac81741 Michael Hanselmann
                     for member_name in
1186 dac81741 Michael Hanselmann
                       self._UnlockedGetNodeGroup(ngfn(node_name)).members)
1187 dac81741 Michael Hanselmann
1188 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1189 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1190 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1191 080fbeea Michael Hanselmann

1192 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1193 080fbeea Michael Hanselmann
    @rtype: list
1194 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1195 080fbeea Michael Hanselmann

1196 080fbeea Michael Hanselmann
    """
1197 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1198 080fbeea Michael Hanselmann
1199 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1200 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1201 a8083063 Iustin Pop
    """Add an instance to the config.
1202 a8083063 Iustin Pop

1203 a8083063 Iustin Pop
    This should be used after creating a new instance.
1204 a8083063 Iustin Pop

1205 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1206 c41eea6e Iustin Pop
    @param instance: the instance object
1207 c41eea6e Iustin Pop

1208 a8083063 Iustin Pop
    """
1209 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1210 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1211 a8083063 Iustin Pop
1212 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1213 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1214 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1215 923b1523 Iustin Pop
1216 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1217 e4640214 Guido Trotter
    for nic in instance.nics:
1218 e4640214 Guido Trotter
      if nic.mac in all_macs:
1219 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1220 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1221 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1222 430b923c Iustin Pop
1223 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1224 e4640214 Guido Trotter
1225 b989e85d Iustin Pop
    instance.serial_no = 1
1226 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1227 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1228 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1229 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1230 a8083063 Iustin Pop
    self._WriteConfig()
1231 a8083063 Iustin Pop
1232 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1233 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1234 430b923c Iustin Pop

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

1238 430b923c Iustin Pop
    """
1239 430b923c Iustin Pop
    if not item.uuid:
1240 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1241 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1242 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1243 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1244 430b923c Iustin Pop
1245 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1246 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1247 a8083063 Iustin Pop

1248 a8083063 Iustin Pop
    """
1249 2e04d454 Agata Murawska
    assert status in constants.ADMINST_ALL, \
1250 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1251 a8083063 Iustin Pop
1252 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1253 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1254 3ecf6786 Iustin Pop
                                      instance_name)
1255 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1256 9ca8a7c5 Agata Murawska
    if instance.admin_state != status:
1257 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1258 b989e85d Iustin Pop
      instance.serial_no += 1
1259 d693c864 Iustin Pop
      instance.mtime = time.time()
1260 455a3445 Iustin Pop
      self._WriteConfig()
1261 a8083063 Iustin Pop
1262 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1263 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1264 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1265 6a408fb2 Iustin Pop

1266 6a408fb2 Iustin Pop
    """
1267 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_UP)
1268 57de31c0 Agata Murawska
1269 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1270 57de31c0 Agata Murawska
  def MarkInstanceOffline(self, instance_name):
1271 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1272 57de31c0 Agata Murawska

1273 57de31c0 Agata Murawska
    """
1274 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_OFFLINE)
1275 6a408fb2 Iustin Pop
1276 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1277 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1278 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1279 a8083063 Iustin Pop

1280 a8083063 Iustin Pop
    """
1281 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1282 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1283 f396ad8c Vangelis Koukis
1284 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1285 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1286 f396ad8c Vangelis Koukis
    inst = self._config_data.instances[instance_name]
1287 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1288 f396ad8c Vangelis Koukis
    if network_port is not None:
1289 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1290 f396ad8c Vangelis Koukis
1291 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1292 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1293 a8083063 Iustin Pop
    self._WriteConfig()
1294 a8083063 Iustin Pop
1295 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1296 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1297 fc95f88f Iustin Pop
    """Rename an instance.
1298 fc95f88f Iustin Pop

1299 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1300 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1301 fc95f88f Iustin Pop
    rename.
1302 fc95f88f Iustin Pop

1303 fc95f88f Iustin Pop
    """
1304 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
1305 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
1306 ea642319 Michael Hanselmann
1307 ea642319 Michael Hanselmann
    # Operate on a copy to not loose instance object in case of a failure
1308 ea642319 Michael Hanselmann
    inst = self._config_data.instances[old_name].Copy()
1309 fc95f88f Iustin Pop
    inst.name = new_name
1310 b23c4333 Manuel Franceschini
1311 ea642319 Michael Hanselmann
    for (idx, disk) in enumerate(inst.disks):
1312 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1313 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1314 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1315 ea642319 Michael Hanselmann
        disk.logical_id = (disk.logical_id[0],
1316 ea642319 Michael Hanselmann
                           utils.PathJoin(file_storage_dir, inst.name,
1317 ea642319 Michael Hanselmann
                                          "disk%s" % idx))
1318 ea642319 Michael Hanselmann
        disk.physical_id = disk.logical_id
1319 ea642319 Michael Hanselmann
1320 ea642319 Michael Hanselmann
    # Actually replace instance object
1321 ea642319 Michael Hanselmann
    del self._config_data.instances[old_name]
1322 ea642319 Michael Hanselmann
    self._config_data.instances[inst.name] = inst
1323 b23c4333 Manuel Franceschini
1324 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1325 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1326 1fc34c48 Michael Hanselmann
1327 fc95f88f Iustin Pop
    self._WriteConfig()
1328 fc95f88f Iustin Pop
1329 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1330 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
1331 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1332 a8083063 Iustin Pop

1333 a8083063 Iustin Pop
    """
1334 2e04d454 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_DOWN)
1335 a8083063 Iustin Pop
1336 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1337 94bbfece Iustin Pop
    """Get the list of instances.
1338 94bbfece Iustin Pop

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

1341 94bbfece Iustin Pop
    """
1342 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1343 94bbfece Iustin Pop
1344 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1345 a8083063 Iustin Pop
  def GetInstanceList(self):
1346 a8083063 Iustin Pop
    """Get the list of instances.
1347 a8083063 Iustin Pop

1348 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1349 c41eea6e Iustin Pop
        'instance1.example.com']
1350 a8083063 Iustin Pop

1351 a8083063 Iustin Pop
    """
1352 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1353 a8083063 Iustin Pop
1354 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1355 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1356 a8083063 Iustin Pop

1357 a8083063 Iustin Pop
    """
1358 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1359 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1360 a8083063 Iustin Pop
1361 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1362 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1363 94bbfece Iustin Pop

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

1366 94bbfece Iustin Pop
    """
1367 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1368 94bbfece Iustin Pop
      return None
1369 94bbfece Iustin Pop
1370 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1371 94bbfece Iustin Pop
1372 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1373 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1374 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1375 a8083063 Iustin Pop

1376 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1377 a8083063 Iustin Pop
    an instance are taken from the live systems.
1378 a8083063 Iustin Pop

1379 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1380 c41eea6e Iustin Pop
        I{instance1.example.com}
1381 a8083063 Iustin Pop

1382 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1383 c41eea6e Iustin Pop
    @return: the instance object
1384 a8083063 Iustin Pop

1385 a8083063 Iustin Pop
    """
1386 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1387 a8083063 Iustin Pop
1388 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1389 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1390 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1391 2674690b Michael Hanselmann

1392 2674690b Michael Hanselmann
    @rtype: frozenset
1393 2674690b Michael Hanselmann

1394 2674690b Michael Hanselmann
    """
1395 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1396 2674690b Michael Hanselmann
    if not instance:
1397 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1398 2674690b Michael Hanselmann
1399 2674690b Michael Hanselmann
    if primary_only:
1400 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1401 2674690b Michael Hanselmann
    else:
1402 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1403 2674690b Michael Hanselmann
1404 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1405 2674690b Michael Hanselmann
                     for node_name in nodes)
1406 2674690b Michael Hanselmann
1407 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1408 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1409 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1410 71333cb9 Iustin Pop

1411 71333cb9 Iustin Pop
    @param instances: list of instance names
1412 71333cb9 Iustin Pop
    @rtype: list
1413 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1414 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1415 71333cb9 Iustin Pop
        node, while keeping the original order
1416 71333cb9 Iustin Pop

1417 71333cb9 Iustin Pop
    """
1418 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1419 71333cb9 Iustin Pop
1420 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1421 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1422 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1423 0b2de758 Iustin Pop

1424 0b2de758 Iustin Pop
    @rtype: dict
1425 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1426 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1427 0b2de758 Iustin Pop

1428 0b2de758 Iustin Pop
    """
1429 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1430 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1431 0b2de758 Iustin Pop
    return my_dict
1432 0b2de758 Iustin Pop
1433 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1434 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1435 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1436 cc19798f Michael Hanselmann

1437 cc19798f Michael Hanselmann
    @type filter_fn: callable
1438 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1439 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1440 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1441 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1442 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1443 cc19798f Michael Hanselmann

1444 cc19798f Michael Hanselmann
    """
1445 cc19798f Michael Hanselmann
    return dict((name, inst)
1446 cc19798f Michael Hanselmann
                for (name, inst) in self._config_data.instances.items()
1447 cc19798f Michael Hanselmann
                if filter_fn(inst))
1448 cc19798f Michael Hanselmann
1449 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1450 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1451 a8083063 Iustin Pop
    """Add a node to the configuration.
1452 a8083063 Iustin Pop

1453 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1454 c41eea6e Iustin Pop
    @param node: a Node instance
1455 a8083063 Iustin Pop

1456 a8083063 Iustin Pop
    """
1457 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1458 d8470559 Michael Hanselmann
1459 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1460 430b923c Iustin Pop
1461 b989e85d Iustin Pop
    node.serial_no = 1
1462 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1463 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1464 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1465 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1466 a8083063 Iustin Pop
    self._WriteConfig()
1467 a8083063 Iustin Pop
1468 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1469 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1470 a8083063 Iustin Pop
    """Remove a node from the configuration.
1471 a8083063 Iustin Pop

1472 a8083063 Iustin Pop
    """
1473 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1474 d8470559 Michael Hanselmann
1475 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1476 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1477 a8083063 Iustin Pop
1478 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1479 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1480 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1481 a8083063 Iustin Pop
    self._WriteConfig()
1482 a8083063 Iustin Pop
1483 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1484 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1485 a8083063 Iustin Pop

1486 a8083063 Iustin Pop
    """
1487 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1488 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1489 a8083063 Iustin Pop
1490 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1491 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1492 a8083063 Iustin Pop

1493 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1494 c41eea6e Iustin Pop
    held.
1495 f78ede4e Guido Trotter

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

1498 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1499 c41eea6e Iustin Pop
    @return: the node object
1500 a8083063 Iustin Pop

1501 a8083063 Iustin Pop
    """
1502 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1503 a8083063 Iustin Pop
      return None
1504 a8083063 Iustin Pop
1505 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1506 a8083063 Iustin Pop
1507 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1508 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1509 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1510 f78ede4e Guido Trotter

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

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

1515 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1516 c41eea6e Iustin Pop
    @return: the node object
1517 f78ede4e Guido Trotter

1518 f78ede4e Guido Trotter
    """
1519 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1520 f78ede4e Guido Trotter
1521 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1522 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1523 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1524 8bf9e9a5 Iustin Pop

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

1527 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1528 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1529 8bf9e9a5 Iustin Pop

1530 8bf9e9a5 Iustin Pop
    """
1531 8bf9e9a5 Iustin Pop
    pri = []
1532 8bf9e9a5 Iustin Pop
    sec = []
1533 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1534 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1535 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1536 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1537 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1538 8bf9e9a5 Iustin Pop
    return (pri, sec)
1539 8bf9e9a5 Iustin Pop
1540 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1541 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1542 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1543 c71b049c Michael Hanselmann

1544 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1545 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1546 c71b049c Michael Hanselmann
    @rtype: frozenset
1547 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1548 c71b049c Michael Hanselmann

1549 c71b049c Michael Hanselmann
    """
1550 c71b049c Michael Hanselmann
    if primary_only:
1551 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1552 c71b049c Michael Hanselmann
    else:
1553 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1554 c71b049c Michael Hanselmann
1555 c71b049c Michael Hanselmann
    return frozenset(inst.name
1556 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1557 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1558 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1559 c71b049c Michael Hanselmann
1560 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1561 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1562 a8083063 Iustin Pop

1563 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1564 c41eea6e Iustin Pop
    held.
1565 c41eea6e Iustin Pop

1566 c41eea6e Iustin Pop
    @rtype: list
1567 f78ede4e Guido Trotter

1568 a8083063 Iustin Pop
    """
1569 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1570 a8083063 Iustin Pop
1571 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1572 f78ede4e Guido Trotter
  def GetNodeList(self):
1573 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1574 f78ede4e Guido Trotter

1575 f78ede4e Guido Trotter
    """
1576 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1577 f78ede4e Guido Trotter
1578 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1579 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1580 94a02bb5 Iustin Pop

1581 94a02bb5 Iustin Pop
    """
1582 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1583 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1584 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1585 94a02bb5 Iustin Pop
1586 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1587 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1588 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1589 6819dc49 Iustin Pop

1590 6819dc49 Iustin Pop
    """
1591 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1592 6819dc49 Iustin Pop
1593 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1594 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1595 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1596 075b62ca Iustin Pop

1597 075b62ca Iustin Pop
    """
1598 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1599 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1600 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1601 075b62ca Iustin Pop
1602 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1603 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1604 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1605 8bf9e9a5 Iustin Pop

1606 8bf9e9a5 Iustin Pop
    """
1607 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1608 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1609 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1610 8bf9e9a5 Iustin Pop
1611 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1612 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1613 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1614 f5eaa3c1 Iustin Pop

1615 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1616 f5eaa3c1 Iustin Pop
    @rtype: list
1617 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1618 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1619 f5eaa3c1 Iustin Pop
        order
1620 f5eaa3c1 Iustin Pop

1621 f5eaa3c1 Iustin Pop
    """
1622 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1623 f5eaa3c1 Iustin Pop
1624 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1625 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1626 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1627 d65e5776 Iustin Pop

1628 d65e5776 Iustin Pop
    @rtype: dict
1629 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1630 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1631 d65e5776 Iustin Pop

1632 d65e5776 Iustin Pop
    """
1633 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1634 ee14d800 Michael Hanselmann
1635 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1636 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1637 ee14d800 Michael Hanselmann

1638 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1639 ee14d800 Michael Hanselmann

1640 ee14d800 Michael Hanselmann
    """
1641 ee14d800 Michael Hanselmann
    return dict([(node, self._UnlockedGetNodeInfo(node))
1642 ee14d800 Michael Hanselmann
                 for node in self._UnlockedGetNodeList()])
1643 d65e5776 Iustin Pop
1644 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1645 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1646 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1647 9d5b1371 Michael Hanselmann

1648 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1649 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1650 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1651 9d5b1371 Michael Hanselmann

1652 9d5b1371 Michael Hanselmann
    """
1653 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1654 9d5b1371 Michael Hanselmann
1655 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1656 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1657 ec0292f1 Iustin Pop

1658 23f06b2b Iustin Pop
    @type exceptions: list
1659 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1660 ec0292f1 Iustin Pop
    @rtype: tuple
1661 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1662 ec0292f1 Iustin Pop

1663 ec0292f1 Iustin Pop
    """
1664 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1665 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1666 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1667 23f06b2b Iustin Pop
        continue
1668 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1669 ec0292f1 Iustin Pop
        mc_max += 1
1670 ec0292f1 Iustin Pop
      if node.master_candidate:
1671 ec0292f1 Iustin Pop
        mc_now += 1
1672 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1673 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1674 ec0292f1 Iustin Pop
1675 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1676 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1677 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1678 ec0292f1 Iustin Pop

1679 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1680 ec0292f1 Iustin Pop

1681 23f06b2b Iustin Pop
    @type exceptions: list
1682 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1683 ec0292f1 Iustin Pop
    @rtype: tuple
1684 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1685 ec0292f1 Iustin Pop

1686 ec0292f1 Iustin Pop
    """
1687 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1688 ec0292f1 Iustin Pop
1689 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1690 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1691 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1692 ec0292f1 Iustin Pop

1693 44485f49 Guido Trotter
    @type exceptions: list
1694 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1695 ec0292f1 Iustin Pop
    @rtype: list
1696 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1697 ec0292f1 Iustin Pop

1698 ec0292f1 Iustin Pop
    """
1699 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1700 ec0292f1 Iustin Pop
    mod_list = []
1701 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1702 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1703 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1704 ec0292f1 Iustin Pop
      for name in node_list:
1705 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1706 ec0292f1 Iustin Pop
          break
1707 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1708 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1709 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1710 ec0292f1 Iustin Pop
          continue
1711 ee513a66 Iustin Pop
        mod_list.append(node)
1712 ec0292f1 Iustin Pop
        node.master_candidate = True
1713 ec0292f1 Iustin Pop
        node.serial_no += 1
1714 ec0292f1 Iustin Pop
        mc_now += 1
1715 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1716 ec0292f1 Iustin Pop
        # this should not happen
1717 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1718 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1719 ec0292f1 Iustin Pop
      if mod_list:
1720 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1721 ec0292f1 Iustin Pop
        self._WriteConfig()
1722 ec0292f1 Iustin Pop
1723 ec0292f1 Iustin Pop
    return mod_list
1724 ec0292f1 Iustin Pop
1725 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1726 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1727 190e3cb6 Guido Trotter

1728 190e3cb6 Guido Trotter
    """
1729 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1730 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1731 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1732 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1733 190e3cb6 Guido Trotter
      # is not found anymore.
1734 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1735 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1736 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1737 190e3cb6 Guido Trotter
1738 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1739 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1740 190e3cb6 Guido Trotter

1741 190e3cb6 Guido Trotter
    """
1742 f936c153 Iustin Pop
    nodegroup = node.group
1743 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1744 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1745 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1746 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1747 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1748 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1749 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1750 190e3cb6 Guido Trotter
    else:
1751 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1752 190e3cb6 Guido Trotter
1753 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
1754 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
1755 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
1756 54c31fd3 Michael Hanselmann

1757 54c31fd3 Michael Hanselmann
    @type mods: list of tuples; (node name, new group UUID)
1758 1d4930b9 Michael Hanselmann
    @param mods: Node membership modifications
1759 54c31fd3 Michael Hanselmann

1760 54c31fd3 Michael Hanselmann
    """
1761 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
1762 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
1763 54c31fd3 Michael Hanselmann
1764 54c31fd3 Michael Hanselmann
    resmod = []
1765 54c31fd3 Michael Hanselmann
1766 54c31fd3 Michael Hanselmann
    # Try to resolve names/UUIDs first
1767 54c31fd3 Michael Hanselmann
    for (node_name, new_group_uuid) in mods:
1768 54c31fd3 Michael Hanselmann
      try:
1769 54c31fd3 Michael Hanselmann
        node = nodes[node_name]
1770 54c31fd3 Michael Hanselmann
      except KeyError:
1771 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find node '%s'" % node_name)
1772 54c31fd3 Michael Hanselmann
1773 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
1774 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
1775 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
1776 54c31fd3 Michael Hanselmann
                      node_name, node.group)
1777 54c31fd3 Michael Hanselmann
        continue
1778 54c31fd3 Michael Hanselmann
1779 54c31fd3 Michael Hanselmann
      # Try to find current group of node
1780 54c31fd3 Michael Hanselmann
      try:
1781 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
1782 54c31fd3 Michael Hanselmann
      except KeyError:
1783 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
1784 54c31fd3 Michael Hanselmann
                                        node.group)
1785 54c31fd3 Michael Hanselmann
1786 54c31fd3 Michael Hanselmann
      # Try to find new group for node
1787 54c31fd3 Michael Hanselmann
      try:
1788 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
1789 54c31fd3 Michael Hanselmann
      except KeyError:
1790 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
1791 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
1792 54c31fd3 Michael Hanselmann
1793 54c31fd3 Michael Hanselmann
      assert node.name in old_group.members, \
1794 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
1795 54c31fd3 Michael Hanselmann
         " old group '%s'" % (node.name, old_group.uuid))
1796 54c31fd3 Michael Hanselmann
      assert node.name not in new_group.members, \
1797 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
1798 54c31fd3 Michael Hanselmann
         " its new group '%s'" % (node.name, new_group.uuid))
1799 54c31fd3 Michael Hanselmann
1800 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
1801 54c31fd3 Michael Hanselmann
1802 54c31fd3 Michael Hanselmann
    # Apply changes
1803 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
1804 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
1805 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
1806 54c31fd3 Michael Hanselmann
1807 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
1808 54c31fd3 Michael Hanselmann
1809 54c31fd3 Michael Hanselmann
      # Update members of involved groups
1810 54c31fd3 Michael Hanselmann
      if node.name in old_group.members:
1811 54c31fd3 Michael Hanselmann
        old_group.members.remove(node.name)
1812 54c31fd3 Michael Hanselmann
      if node.name not in new_group.members:
1813 54c31fd3 Michael Hanselmann
        new_group.members.append(node.name)
1814 54c31fd3 Michael Hanselmann
1815 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
1816 54c31fd3 Michael Hanselmann
    now = time.time()
1817 75191077 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142
1818 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
1819 54c31fd3 Michael Hanselmann
      obj.mtime = now
1820 54c31fd3 Michael Hanselmann
1821 54c31fd3 Michael Hanselmann
    # Force ssconf update
1822 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1823 54c31fd3 Michael Hanselmann
1824 54c31fd3 Michael Hanselmann
    self._WriteConfig()
1825 54c31fd3 Michael Hanselmann
1826 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1827 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1828 a8083063 Iustin Pop

1829 a8083063 Iustin Pop
    """
1830 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1831 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1832 a8083063 Iustin Pop
1833 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1834 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1835 76d5d3a3 Iustin Pop

1836 76d5d3a3 Iustin Pop
    """
1837 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1838 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1839 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1840 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1841 76d5d3a3 Iustin Pop
1842 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1843 a8083063 Iustin Pop
    """Read the config data from disk.
1844 a8083063 Iustin Pop

1845 a8083063 Iustin Pop
    """
1846 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1847 13998ef2 Michael Hanselmann
1848 a8083063 Iustin Pop
    try:
1849 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1850 13998ef2 Michael Hanselmann
    except Exception, err:
1851 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1852 5b263ed7 Michael Hanselmann
1853 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1854 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1855 5b263ed7 Michael Hanselmann
1856 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
1857 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
1858 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1859 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1860 90d726a8 Iustin Pop
1861 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
1862 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
1863 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
1864 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
1865 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
1866 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
1867 eb180fe2 Iustin Pop
1868 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1869 90d726a8 Iustin Pop
    data.UpgradeConfig()
1870 90d726a8 Iustin Pop
1871 a8083063 Iustin Pop
    self._config_data = data
1872 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1873 0779e3aa Iustin Pop
    # ssconf update
1874 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1875 a8083063 Iustin Pop
1876 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1877 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1878 76d5d3a3 Iustin Pop
1879 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
1880 bd407597 Iustin Pop
1881 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1882 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1883 76d5d3a3 Iustin Pop

1884 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1885 76d5d3a3 Iustin Pop
    whole configuration, etc.
1886 76d5d3a3 Iustin Pop

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

1893 76d5d3a3 Iustin Pop
    """
1894 76d5d3a3 Iustin Pop
    modified = False
1895 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1896 76d5d3a3 Iustin Pop
      if item.uuid is None:
1897 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1898 76d5d3a3 Iustin Pop
        modified = True
1899 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
1900 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
1901 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
1902 75cf411a Adeodato Simo
                                            members=[])
1903 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
1904 f9e81396 Guido Trotter
      modified = True
1905 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
1906 f936c153 Iustin Pop
      if not node.group:
1907 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
1908 190e3cb6 Guido Trotter
        modified = True
1909 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
1910 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
1911 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
1912 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
1913 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
1914 76d5d3a3 Iustin Pop
    if modified:
1915 76d5d3a3 Iustin Pop
      self._WriteConfig()
1916 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1917 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1918 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1919 4fae38c5 Guido Trotter
1920 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1921 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1922 a8083063 Iustin Pop

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

1926 a8083063 Iustin Pop
    """
1927 a8083063 Iustin Pop
    if self._offline:
1928 a8083063 Iustin Pop
      return True
1929 a4eae71f Michael Hanselmann
1930 a8083063 Iustin Pop
    bad = False
1931 a8083063 Iustin Pop
1932 6a5b8b4b Iustin Pop
    node_list = []
1933 6a5b8b4b Iustin Pop
    addr_list = []
1934 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1935 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1936 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1937 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1938 6b294c53 Iustin Pop
    # in between
1939 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1940 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1941 6a5b8b4b Iustin Pop
        continue
1942 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1943 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1944 6a5b8b4b Iustin Pop
        continue
1945 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1946 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1947 6b294c53 Iustin Pop
1948 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
1949 415a7304 Michael Hanselmann
    result = \
1950 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
1951 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1952 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1953 1b54fc6c Guido Trotter
      if msg:
1954 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1955 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1956 1b54fc6c Guido Trotter
        logging.error(msg)
1957 a4eae71f Michael Hanselmann
1958 a4eae71f Michael Hanselmann
        if feedback_fn:
1959 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1960 a4eae71f Michael Hanselmann
1961 a8083063 Iustin Pop
        bad = True
1962 a4eae71f Michael Hanselmann
1963 a8083063 Iustin Pop
    return not bad
1964 a8083063 Iustin Pop
1965 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1966 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1967 a8083063 Iustin Pop

1968 a8083063 Iustin Pop
    """
1969 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
1970 a4eae71f Michael Hanselmann
1971 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
1972 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
1973 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
1974 d2231b8c Iustin Pop
    # recovery to the user
1975 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1976 4a89c54a Iustin Pop
    if config_errors:
1977 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
1978 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
1979 d2231b8c Iustin Pop
      logging.critical(errmsg)
1980 d2231b8c Iustin Pop
      if feedback_fn:
1981 d2231b8c Iustin Pop
        feedback_fn(errmsg)
1982 d2231b8c Iustin Pop
1983 a8083063 Iustin Pop
    if destination is None:
1984 a8083063 Iustin Pop
      destination = self._cfg_file
1985 a8083063 Iustin Pop
    self._BumpSerialNo()
1986 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1987 13998ef2 Michael Hanselmann
1988 e60c73a1 René Nussbaumer
    getents = self._getents()
1989 bd407597 Iustin Pop
    try:
1990 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
1991 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
1992 bd407597 Iustin Pop
    except errors.LockError:
1993 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
1994 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
1995 bd407597 Iustin Pop
                                      " update")
1996 bd407597 Iustin Pop
    try:
1997 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
1998 bd407597 Iustin Pop
    finally:
1999 bd407597 Iustin Pop
      os.close(fd)
2000 13998ef2 Michael Hanselmann
2001 14e15659 Iustin Pop
    self.write_count += 1
2002 3d3a04bc Iustin Pop
2003 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
2004 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
2005 a8083063 Iustin Pop
2006 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
2007 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
2008 d9a855f1 Michael Hanselmann
      if not self._offline:
2009 b2acdbdc Michael Hanselmann
        result = self._GetRpc(None).call_write_ssconf_files(
2010 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
2011 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
2012 a4eae71f Michael Hanselmann
2013 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
2014 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
2015 e1e75d00 Iustin Pop
          if msg:
2016 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
2017 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
2018 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
2019 a4eae71f Michael Hanselmann
2020 a4eae71f Michael Hanselmann
            if feedback_fn:
2021 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
2022 a4eae71f Michael Hanselmann
2023 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
2024 54d1a06e Michael Hanselmann
2025 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
2026 054596f0 Iustin Pop
    """Return the values needed by ssconf.
2027 054596f0 Iustin Pop

2028 054596f0 Iustin Pop
    @rtype: dict
2029 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2030 054596f0 Iustin Pop
        associated value
2031 054596f0 Iustin Pop

2032 054596f0 Iustin Pop
    """
2033 a3316e4a Iustin Pop
    fn = "\n".join
2034 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
2035 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
2036 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
2037 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
2038 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
2039 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
2040 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
2041 a3316e4a Iustin Pop
2042 81a49123 Iustin Pop
    instance_data = fn(instance_names)
2043 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
2044 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
2045 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
2046 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
2047 8113a52e Luca Bigliardi
                     if node.master_candidate)
2048 a3316e4a Iustin Pop
    node_data = fn(node_names)
2049 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
2050 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
2051 f56618e0 Iustin Pop
2052 054596f0 Iustin Pop
    cluster = self._config_data.cluster
2053 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
2054 4f7a6a10 Iustin Pop
2055 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
2056 4f7a6a10 Iustin Pop
2057 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
2058 0fbae49a Balazs Lecz
2059 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
2060 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
2061 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
2062 6f076453 Guido Trotter
2063 2afc9238 Iustin Pop
    ssconf_values = {
2064 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
2065 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
2066 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
2067 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
2068 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
2069 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
2070 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
2071 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
2072 5a8648eb Andrea Spadaccini
      constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
2073 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
2074 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
2075 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
2076 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
2077 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
2078 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
2079 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
2080 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
2081 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
2082 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
2083 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
2084 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
2085 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
2086 03d1dba2 Michael Hanselmann
      }
2087 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
2088 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
2089 2afc9238 Iustin Pop
    if bad_values:
2090 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
2091 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
2092 2afc9238 Iustin Pop
                                      " values: %s" % err)
2093 2afc9238 Iustin Pop
    return ssconf_values
2094 03d1dba2 Michael Hanselmann
2095 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2096 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
2097 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
2098 d367b66c Manuel Franceschini

2099 d367b66c Manuel Franceschini
    """
2100 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2101 d367b66c Manuel Franceschini
2102 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2103 a8083063 Iustin Pop
  def GetVGName(self):
2104 a8083063 Iustin Pop
    """Return the volume group name.
2105 a8083063 Iustin Pop

2106 a8083063 Iustin Pop
    """
2107 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2108 a8083063 Iustin Pop
2109 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2110 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2111 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2112 89ff8e15 Manuel Franceschini

2113 89ff8e15 Manuel Franceschini
    """
2114 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2115 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2116 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2117 89ff8e15 Manuel Franceschini
2118 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2119 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2120 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2121 9e33896b Luca Bigliardi

2122 9e33896b Luca Bigliardi
    """
2123 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2124 9e33896b Luca Bigliardi
2125 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2126 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2127 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2128 9e33896b Luca Bigliardi

2129 9e33896b Luca Bigliardi
    """
2130 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2131 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2132 9e33896b Luca Bigliardi
    self._WriteConfig()
2133 9e33896b Luca Bigliardi
2134 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2135 a8083063 Iustin Pop
  def GetMACPrefix(self):
2136 a8083063 Iustin Pop
    """Return the mac prefix.
2137 a8083063 Iustin Pop

2138 a8083063 Iustin Pop
    """
2139 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2140 62779dd0 Iustin Pop
2141 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2142 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2143 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2144 62779dd0 Iustin Pop

2145 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2146 c41eea6e Iustin Pop
    @return: the cluster object
2147 62779dd0 Iustin Pop

2148 62779dd0 Iustin Pop
    """
2149 62779dd0 Iustin Pop
    return self._config_data.cluster
2150 e00fb268 Iustin Pop
2151 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2152 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2153 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2154 51cb1581 Luca Bigliardi

2155 51cb1581 Luca Bigliardi
    """
2156 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2157 51cb1581 Luca Bigliardi
2158 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2159 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
2160 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2161 e00fb268 Iustin Pop

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

2168 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2169 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2170 c41eea6e Iustin Pop
        the cluster
2171 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2172 c41eea6e Iustin Pop

2173 e00fb268 Iustin Pop
    """
2174 e00fb268 Iustin Pop
    if self._config_data is None:
2175 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2176 3ecf6786 Iustin Pop
                                   " cannot save.")
2177 f34901f8 Iustin Pop
    update_serial = False
2178 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2179 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2180 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2181 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2182 f34901f8 Iustin Pop
      update_serial = True
2183 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2184 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2185 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2186 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2187 e00fb268 Iustin Pop
    else:
2188 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2189 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2190 e00fb268 Iustin Pop
    if not test:
2191 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2192 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2193 f34901f8 Iustin Pop
    target.serial_no += 1
2194 d693c864 Iustin Pop
    target.mtime = now = time.time()
2195 f34901f8 Iustin Pop
2196 cff4c037 Iustin Pop
    if update_serial:
2197 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2198 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2199 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2200 b989e85d Iustin Pop
2201 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2202 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
2203 61cf6b5e Iustin Pop
2204 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2205 73064714 Guido Trotter
2206 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2207 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2208 73064714 Guido Trotter
    """Drop per-execution-context reservations
2209 73064714 Guido Trotter

2210 73064714 Guido Trotter
    """
2211 d8aee57e Iustin Pop
    for rm in self._all_rms:
2212 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)