Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 1a2eb2dc

History | View | Annotate | Download (74.6 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 57407093 Michael Hanselmann
from ganeti import pathutils
54 243cdbcc Michael Hanselmann
55 243cdbcc Michael Hanselmann
56 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
57 f78ede4e Guido Trotter
58 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
59 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
60 4fae38c5 Guido Trotter
61 f78ede4e Guido Trotter
62 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
63 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
64 c41eea6e Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

231 5768e6a6 René Nussbaumer
    """
232 5768e6a6 René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
233 5768e6a6 René Nussbaumer
    return self._config_data.cluster.FillND(node, nodegroup)
234 5768e6a6 René Nussbaumer
235 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
236 8a147bba René Nussbaumer
  def GetInstanceDiskParams(self, instance):
237 8a147bba René Nussbaumer
    """Get the disk params populated with inherit chain.
238 8a147bba René Nussbaumer

239 8a147bba René Nussbaumer
    @type instance: L{objects.Instance}
240 8a147bba René Nussbaumer
    @param instance: The instance we want to know the params for
241 8a147bba René Nussbaumer
    @return: A dict with the filled in disk params
242 8a147bba René Nussbaumer

243 8a147bba René Nussbaumer
    """
244 8a147bba René Nussbaumer
    node = self._UnlockedGetNodeInfo(instance.primary_node)
245 8a147bba René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
246 99ccf8b9 René Nussbaumer
    return self._UnlockedGetGroupDiskParams(nodegroup)
247 99ccf8b9 René Nussbaumer
248 99ccf8b9 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
249 99ccf8b9 René Nussbaumer
  def GetGroupDiskParams(self, group):
250 99ccf8b9 René Nussbaumer
    """Get the disk params populated with inherit chain.
251 99ccf8b9 René Nussbaumer

252 af9fb4cc René Nussbaumer
    @type group: L{objects.NodeGroup}
253 99ccf8b9 René Nussbaumer
    @param group: The group we want to know the params for
254 99ccf8b9 René Nussbaumer
    @return: A dict with the filled in disk params
255 99ccf8b9 René Nussbaumer

256 99ccf8b9 René Nussbaumer
    """
257 99ccf8b9 René Nussbaumer
    return self._UnlockedGetGroupDiskParams(group)
258 99ccf8b9 René Nussbaumer
259 99ccf8b9 René Nussbaumer
  def _UnlockedGetGroupDiskParams(self, group):
260 99ccf8b9 René Nussbaumer
    """Get the disk params populated with inherit chain down to node-group.
261 99ccf8b9 René Nussbaumer

262 af9fb4cc René Nussbaumer
    @type group: L{objects.NodeGroup}
263 99ccf8b9 René Nussbaumer
    @param group: The group we want to know the params for
264 99ccf8b9 René Nussbaumer
    @return: A dict with the filled in disk params
265 99ccf8b9 René Nussbaumer

266 99ccf8b9 René Nussbaumer
    """
267 99ccf8b9 René Nussbaumer
    return self._config_data.cluster.SimpleFillDP(group.diskparams)
268 8a147bba René Nussbaumer
269 8a147bba René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
270 36b66e6e Guido Trotter
  def GenerateMAC(self, ec_id):
271 a8083063 Iustin Pop
    """Generate a MAC for an instance.
272 a8083063 Iustin Pop

273 a8083063 Iustin Pop
    This should check the current instances for duplicates.
274 a8083063 Iustin Pop

275 a8083063 Iustin Pop
    """
276 36b66e6e Guido Trotter
    existing = self._AllMACs()
277 36b66e6e Guido Trotter
    return self._temporary_ids.Generate(existing, self._GenerateOneMAC, ec_id)
278 a8083063 Iustin Pop
279 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
280 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
281 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
282 1862d460 Alexander Schreiber

283 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
284 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
285 1862d460 Alexander Schreiber

286 1862d460 Alexander Schreiber
    """
287 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
288 36b66e6e Guido Trotter
    if mac in all_macs:
289 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
290 36b66e6e Guido Trotter
    else:
291 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
292 1862d460 Alexander Schreiber
293 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
294 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
295 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
296 d8aee57e Iustin Pop

297 d8aee57e Iustin Pop
    @type lv_name: string
298 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
299 d8aee57e Iustin Pop

300 d8aee57e Iustin Pop
    """
301 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
302 d8aee57e Iustin Pop
    if lv_name in all_lvs:
303 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
304 d8aee57e Iustin Pop
    else:
305 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
306 d8aee57e Iustin Pop
307 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
308 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
309 f9518d38 Iustin Pop
    """Generate a DRBD secret.
310 f9518d38 Iustin Pop

311 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
312 f9518d38 Iustin Pop

313 f9518d38 Iustin Pop
    """
314 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
315 afa1386e Guido Trotter
                                            utils.GenerateSecret,
316 afa1386e Guido Trotter
                                            ec_id)
317 8d9c3bef Michael Hanselmann
318 34e54ebc Iustin Pop
  def _AllLVs(self):
319 923b1523 Iustin Pop
    """Compute the list of all LVs.
320 923b1523 Iustin Pop

321 923b1523 Iustin Pop
    """
322 923b1523 Iustin Pop
    lvnames = set()
323 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
324 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
325 923b1523 Iustin Pop
      for lv_list in node_data.values():
326 923b1523 Iustin Pop
        lvnames.update(lv_list)
327 923b1523 Iustin Pop
    return lvnames
328 923b1523 Iustin Pop
329 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
330 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
331 34e54ebc Iustin Pop

332 34e54ebc Iustin Pop
    @type include_temporary: boolean
333 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
334 34e54ebc Iustin Pop
    @rtype: set
335 34e54ebc Iustin Pop
    @return: a set of IDs
336 34e54ebc Iustin Pop

337 34e54ebc Iustin Pop
    """
338 34e54ebc Iustin Pop
    existing = set()
339 34e54ebc Iustin Pop
    if include_temporary:
340 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
341 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
342 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
343 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
344 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
345 34e54ebc Iustin Pop
    return existing
346 34e54ebc Iustin Pop
347 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
348 430b923c Iustin Pop
    """Generate an unique UUID.
349 923b1523 Iustin Pop

350 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
351 923b1523 Iustin Pop
    duplicates.
352 923b1523 Iustin Pop

353 c41eea6e Iustin Pop
    @rtype: string
354 c41eea6e Iustin Pop
    @return: the unique id
355 923b1523 Iustin Pop

356 923b1523 Iustin Pop
    """
357 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
358 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
359 923b1523 Iustin Pop
360 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
361 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
362 430b923c Iustin Pop
    """Generate an unique ID.
363 430b923c Iustin Pop

364 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
365 430b923c Iustin Pop

366 4fae38c5 Guido Trotter
    @type ec_id: string
367 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
368 34d657ba Iustin Pop

369 34d657ba Iustin Pop
    """
370 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
371 34d657ba Iustin Pop
372 a8083063 Iustin Pop
  def _AllMACs(self):
373 a8083063 Iustin Pop
    """Return all MACs present in the config.
374 a8083063 Iustin Pop

375 c41eea6e Iustin Pop
    @rtype: list
376 c41eea6e Iustin Pop
    @return: the list of all MACs
377 c41eea6e Iustin Pop

378 a8083063 Iustin Pop
    """
379 a8083063 Iustin Pop
    result = []
380 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
381 a8083063 Iustin Pop
      for nic in instance.nics:
382 a8083063 Iustin Pop
        result.append(nic.mac)
383 a8083063 Iustin Pop
384 a8083063 Iustin Pop
    return result
385 a8083063 Iustin Pop
386 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
387 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
388 f9518d38 Iustin Pop

389 c41eea6e Iustin Pop
    @rtype: list
390 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
391 c41eea6e Iustin Pop

392 f9518d38 Iustin Pop
    """
393 f9518d38 Iustin Pop
    def helper(disk, result):
394 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
395 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
396 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
397 f9518d38 Iustin Pop
      if disk.children:
398 f9518d38 Iustin Pop
        for child in disk.children:
399 f9518d38 Iustin Pop
          helper(child, result)
400 f9518d38 Iustin Pop
401 f9518d38 Iustin Pop
    result = []
402 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
403 f9518d38 Iustin Pop
      for disk in instance.disks:
404 f9518d38 Iustin Pop
        helper(disk, result)
405 f9518d38 Iustin Pop
406 f9518d38 Iustin Pop
    return result
407 f9518d38 Iustin Pop
408 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
409 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
410 4b98ac29 Iustin Pop

411 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
412 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
413 4b98ac29 Iustin Pop
    @type l_ids: list
414 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
415 4b98ac29 Iustin Pop
    @type p_ids: list
416 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
417 4b98ac29 Iustin Pop
    @rtype: list
418 4b98ac29 Iustin Pop
    @return: a list of error messages
419 4b98ac29 Iustin Pop

420 4b98ac29 Iustin Pop
    """
421 4b98ac29 Iustin Pop
    result = []
422 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
423 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
424 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
425 25ae22e4 Iustin Pop
      else:
426 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
427 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
428 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
429 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
430 25ae22e4 Iustin Pop
      else:
431 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
432 4b98ac29 Iustin Pop
433 4b98ac29 Iustin Pop
    if disk.children:
434 4b98ac29 Iustin Pop
      for child in disk.children:
435 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
436 4b98ac29 Iustin Pop
    return result
437 4b98ac29 Iustin Pop
438 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
439 a8efbb40 Iustin Pop
    """Verify function.
440 a8efbb40 Iustin Pop

441 4a89c54a Iustin Pop
    @rtype: list
442 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
443 4a89c54a Iustin Pop
        configuration errors
444 4a89c54a Iustin Pop

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

698 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
699 4a89c54a Iustin Pop

700 4a89c54a Iustin Pop
    @rtype: list
701 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
702 4a89c54a Iustin Pop
        configuration errors
703 4a89c54a Iustin Pop

704 4a89c54a Iustin Pop
    """
705 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
706 4a89c54a Iustin Pop
707 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
708 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
709 a8083063 Iustin Pop

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

712 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
713 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
714 a8083063 Iustin Pop
    node.
715 a8083063 Iustin Pop

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

718 a8083063 Iustin Pop
    """
719 a8083063 Iustin Pop
    if disk.children:
720 a8083063 Iustin Pop
      for child in disk.children:
721 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
722 a8083063 Iustin Pop
723 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
724 a8083063 Iustin Pop
      return
725 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
726 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
727 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
728 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
729 3ecf6786 Iustin Pop
                                        node_name)
730 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
731 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
732 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
733 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
734 a8083063 Iustin Pop
                                        " for %s" % str(disk))
735 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
736 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
737 a8083063 Iustin Pop
      if pnode == node_name:
738 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
739 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
740 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
741 a8083063 Iustin Pop
    else:
742 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
743 a8083063 Iustin Pop
    return
744 a8083063 Iustin Pop
745 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
746 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
747 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
748 f78ede4e Guido Trotter

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

751 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
752 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
753 f78ede4e Guido Trotter
    node.
754 f78ede4e Guido Trotter

755 f78ede4e Guido Trotter
    """
756 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
757 f78ede4e Guido Trotter
758 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
759 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
760 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
761 b2fddf63 Iustin Pop

762 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
763 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
764 3b3b1bca Dimitris Aragiorgis
        configuration is stable
765 3b3b1bca Dimitris Aragiorgis

766 b2fddf63 Iustin Pop
    """
767 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
768 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
769 264bb3c5 Michael Hanselmann
770 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
771 264bb3c5 Michael Hanselmann
772 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
773 b2fddf63 Iustin Pop
  def GetPortList(self):
774 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
775 264bb3c5 Michael Hanselmann

776 264bb3c5 Michael Hanselmann
    """
777 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
778 264bb3c5 Michael Hanselmann
779 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
780 a8083063 Iustin Pop
  def AllocatePort(self):
781 a8083063 Iustin Pop
    """Allocate a port.
782 a8083063 Iustin Pop

783 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
784 b2fddf63 Iustin Pop
    default port range (and in this case we increase
785 b2fddf63 Iustin Pop
    highest_used_port).
786 a8083063 Iustin Pop

787 a8083063 Iustin Pop
    """
788 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
789 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
790 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
791 264bb3c5 Michael Hanselmann
    else:
792 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
793 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
794 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
795 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
796 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
797 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
798 a8083063 Iustin Pop
799 a8083063 Iustin Pop
    self._WriteConfig()
800 a8083063 Iustin Pop
    return port
801 a8083063 Iustin Pop
802 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
803 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
804 a81c53c9 Iustin Pop

805 4a89c54a Iustin Pop
    @rtype: (dict, list)
806 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
807 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
808 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
809 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
810 4a89c54a Iustin Pop
        should raise an exception
811 a81c53c9 Iustin Pop

812 a81c53c9 Iustin Pop
    """
813 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
814 4a89c54a Iustin Pop
      duplicates = []
815 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
816 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
817 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
818 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
819 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
820 a81c53c9 Iustin Pop
          if port in used[node]:
821 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
822 4a89c54a Iustin Pop
          else:
823 4a89c54a Iustin Pop
            used[node][port] = instance_name
824 a81c53c9 Iustin Pop
      if disk.children:
825 a81c53c9 Iustin Pop
        for child in disk.children:
826 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
827 4a89c54a Iustin Pop
      return duplicates
828 a81c53c9 Iustin Pop
829 4a89c54a Iustin Pop
    duplicates = []
830 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
831 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
832 79b26a7a Iustin Pop
      for disk in instance.disks:
833 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
834 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
835 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
836 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
837 4a89c54a Iustin Pop
      else:
838 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
839 4a89c54a Iustin Pop
    return my_dict, duplicates
840 a81c53c9 Iustin Pop
841 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
842 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
843 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
844 6d2e83d5 Iustin Pop

845 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
846 6d2e83d5 Iustin Pop

847 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
848 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
849 6d2e83d5 Iustin Pop
        an empty list).
850 6d2e83d5 Iustin Pop

851 6d2e83d5 Iustin Pop
    """
852 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
853 4a89c54a Iustin Pop
    if duplicates:
854 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
855 4a89c54a Iustin Pop
                                      str(duplicates))
856 4a89c54a Iustin Pop
    return d_map
857 6d2e83d5 Iustin Pop
858 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
859 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
860 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
861 a81c53c9 Iustin Pop

862 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
863 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
864 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
865 a81c53c9 Iustin Pop
    order as the passed nodes.
866 a81c53c9 Iustin Pop

867 32388e6d Iustin Pop
    @type instance: string
868 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
869 32388e6d Iustin Pop

870 a81c53c9 Iustin Pop
    """
871 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
872 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
873 32388e6d Iustin Pop
874 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
875 4a89c54a Iustin Pop
    if duplicates:
876 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
877 4a89c54a Iustin Pop
                                      str(duplicates))
878 a81c53c9 Iustin Pop
    result = []
879 a81c53c9 Iustin Pop
    for nname in nodes:
880 a81c53c9 Iustin Pop
      ndata = d_map[nname]
881 a81c53c9 Iustin Pop
      if not ndata:
882 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
883 a81c53c9 Iustin Pop
        result.append(0)
884 a81c53c9 Iustin Pop
        ndata[0] = instance
885 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
886 a81c53c9 Iustin Pop
        continue
887 a81c53c9 Iustin Pop
      keys = ndata.keys()
888 a81c53c9 Iustin Pop
      keys.sort()
889 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
890 a81c53c9 Iustin Pop
      if ffree is None:
891 a81c53c9 Iustin Pop
        # return the next minor
892 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
893 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
894 a81c53c9 Iustin Pop
      else:
895 a81c53c9 Iustin Pop
        minor = ffree
896 4a89c54a Iustin Pop
      # double-check minor against current instances
897 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
898 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
899 4a89c54a Iustin Pop
              " already allocated to instance %s" %
900 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
901 a81c53c9 Iustin Pop
      ndata[minor] = instance
902 4a89c54a Iustin Pop
      # double-check minor against reservation
903 4a89c54a Iustin Pop
      r_key = (nname, minor)
904 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
905 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
906 4a89c54a Iustin Pop
              " reserved for instance %s" %
907 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
908 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
909 4a89c54a Iustin Pop
      result.append(minor)
910 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
911 a81c53c9 Iustin Pop
                  nodes, result)
912 a81c53c9 Iustin Pop
    return result
913 a81c53c9 Iustin Pop
914 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
915 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
916 a81c53c9 Iustin Pop

917 a81c53c9 Iustin Pop
    @type instance: string
918 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
919 a81c53c9 Iustin Pop
                     released
920 a81c53c9 Iustin Pop

921 a81c53c9 Iustin Pop
    """
922 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
923 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
924 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
925 a81c53c9 Iustin Pop
      if name == instance:
926 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
927 a81c53c9 Iustin Pop
928 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
929 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
930 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
931 61cf6b5e Iustin Pop

932 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
933 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
934 61cf6b5e Iustin Pop
    functions.
935 61cf6b5e Iustin Pop

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

938 61cf6b5e Iustin Pop
    @type instance: string
939 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
940 61cf6b5e Iustin Pop
                     released
941 61cf6b5e Iustin Pop

942 61cf6b5e Iustin Pop
    """
943 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
944 61cf6b5e Iustin Pop
945 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
946 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
947 4a8b186a Michael Hanselmann
    """Get the configuration version.
948 4a8b186a Michael Hanselmann

949 4a8b186a Michael Hanselmann
    @return: Config version
950 4a8b186a Michael Hanselmann

951 4a8b186a Michael Hanselmann
    """
952 4a8b186a Michael Hanselmann
    return self._config_data.version
953 4a8b186a Michael Hanselmann
954 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
955 4a8b186a Michael Hanselmann
  def GetClusterName(self):
956 4a8b186a Michael Hanselmann
    """Get cluster name.
957 4a8b186a Michael Hanselmann

958 4a8b186a Michael Hanselmann
    @return: Cluster name
959 4a8b186a Michael Hanselmann

960 4a8b186a Michael Hanselmann
    """
961 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
962 4a8b186a Michael Hanselmann
963 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
964 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
965 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
966 4a8b186a Michael Hanselmann

967 4a8b186a Michael Hanselmann
    @return: Master hostname
968 4a8b186a Michael Hanselmann

969 4a8b186a Michael Hanselmann
    """
970 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
971 4a8b186a Michael Hanselmann
972 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
973 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
974 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
975 4a8b186a Michael Hanselmann

976 4a8b186a Michael Hanselmann
    @return: Master IP
977 4a8b186a Michael Hanselmann

978 4a8b186a Michael Hanselmann
    """
979 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
980 4a8b186a Michael Hanselmann
981 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
982 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
983 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
984 4a8b186a Michael Hanselmann

985 4a8b186a Michael Hanselmann
    """
986 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
987 4a8b186a Michael Hanselmann
988 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
989 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
990 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
991 5a8648eb Andrea Spadaccini

992 5a8648eb Andrea Spadaccini
    """
993 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
994 5a8648eb Andrea Spadaccini
995 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
996 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
997 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
998 33be7576 Andrea Spadaccini

999 33be7576 Andrea Spadaccini
    """
1000 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
1001 33be7576 Andrea Spadaccini
1002 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1003 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
1004 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
1005 4a8b186a Michael Hanselmann

1006 4a8b186a Michael Hanselmann
    """
1007 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
1008 4a8b186a Michael Hanselmann
1009 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1010 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
1011 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
1012 4b97f902 Apollon Oikonomopoulos

1013 4b97f902 Apollon Oikonomopoulos
    """
1014 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1015 4b97f902 Apollon Oikonomopoulos
1016 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1017 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1018 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1019 4a8b186a Michael Hanselmann

1020 4a8b186a Michael Hanselmann
    """
1021 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
1022 4a8b186a Michael Hanselmann
1023 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1024 a8083063 Iustin Pop
  def GetHostKey(self):
1025 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1026 a8083063 Iustin Pop

1027 c41eea6e Iustin Pop
    @rtype: string
1028 c41eea6e Iustin Pop
    @return: the rsa hostkey
1029 a8083063 Iustin Pop

1030 a8083063 Iustin Pop
    """
1031 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1032 a8083063 Iustin Pop
1033 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1034 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1035 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1036 bf4af505 Apollon Oikonomopoulos

1037 bf4af505 Apollon Oikonomopoulos
    """
1038 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1039 bf4af505 Apollon Oikonomopoulos
1040 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1041 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1042 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1043 868a98ca Manuel Franceschini

1044 868a98ca Manuel Franceschini
    @return: primary ip family
1045 868a98ca Manuel Franceschini

1046 868a98ca Manuel Franceschini
    """
1047 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1048 868a98ca Manuel Franceschini
1049 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1050 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1051 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1052 c9f4b8e6 Andrea Spadaccini

1053 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1054 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1055 c9f4b8e6 Andrea Spadaccini

1056 c9f4b8e6 Andrea Spadaccini
    """
1057 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1058 5ae4945a Iustin Pop
    result = objects.MasterNetworkParameters(
1059 5ae4945a Iustin Pop
      name=cluster.master_node, ip=cluster.master_ip,
1060 5ae4945a Iustin Pop
      netmask=cluster.master_netmask, netdev=cluster.master_netdev,
1061 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1062 c9f4b8e6 Andrea Spadaccini
1063 f9d20654 Andrea Spadaccini
    return result
1064 f9d20654 Andrea Spadaccini
1065 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1066 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1067 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1068 e11a1b77 Adeodato Simo

1069 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1070 90e99856 Adeodato Simo
    according to their default values.
1071 90e99856 Adeodato Simo

1072 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1073 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1074 e11a1b77 Adeodato Simo
    @type ec_id: string
1075 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1076 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1077 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1078 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1079 e11a1b77 Adeodato Simo
                       configuration already
1080 e11a1b77 Adeodato Simo

1081 e11a1b77 Adeodato Simo
    """
1082 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1083 e11a1b77 Adeodato Simo
    self._WriteConfig()
1084 e11a1b77 Adeodato Simo
1085 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1086 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1087 e11a1b77 Adeodato Simo

1088 e11a1b77 Adeodato Simo
    """
1089 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
1090 e11a1b77 Adeodato Simo
1091 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
1092 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
1093 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
1094 e11a1b77 Adeodato Simo
    if check_uuid:
1095 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
1096 e11a1b77 Adeodato Simo
1097 18ffc0fe Stephen Shirley
    try:
1098 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
1099 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
1100 18ffc0fe Stephen Shirley
      pass
1101 18ffc0fe Stephen Shirley
    else:
1102 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
1103 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
1104 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
1105 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
1106 18ffc0fe Stephen Shirley
1107 e11a1b77 Adeodato Simo
    group.serial_no = 1
1108 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
1109 90e99856 Adeodato Simo
    group.UpgradeConfig()
1110 e11a1b77 Adeodato Simo
1111 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
1112 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1113 e11a1b77 Adeodato Simo
1114 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1115 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
1116 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
1117 e11a1b77 Adeodato Simo

1118 e11a1b77 Adeodato Simo
    @type group_uuid: string
1119 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1120 e11a1b77 Adeodato Simo

1121 e11a1b77 Adeodato Simo
    """
1122 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1123 e11a1b77 Adeodato Simo
1124 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1125 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1126 e11a1b77 Adeodato Simo
1127 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1128 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1129 0389c42a Stephen Shirley
1130 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1131 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1132 e11a1b77 Adeodato Simo
    self._WriteConfig()
1133 e11a1b77 Adeodato Simo
1134 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1135 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1136 eaa98a04 Guido Trotter

1137 eaa98a04 Guido Trotter
    @type target: string or None
1138 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1139 eaa98a04 Guido Trotter
    @rtype: string
1140 412b3531 Guido Trotter
    @return: nodegroup UUID
1141 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1142 eaa98a04 Guido Trotter

1143 eaa98a04 Guido Trotter
    """
1144 eaa98a04 Guido Trotter
    if target is None:
1145 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1146 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1147 2ed0e208 Iustin Pop
                                   " group must be specified explicitly.")
1148 eaa98a04 Guido Trotter
      else:
1149 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1150 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1151 eaa98a04 Guido Trotter
      return target
1152 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1153 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1154 eaa98a04 Guido Trotter
        return nodegroup.uuid
1155 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1156 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1157 eaa98a04 Guido Trotter
1158 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1159 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1160 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1161 e85d8982 Stephen Shirley

1162 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1163 e85d8982 Stephen Shirley

1164 e85d8982 Stephen Shirley
    @type target: string or None
1165 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1166 e85d8982 Stephen Shirley
    @rtype: string
1167 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1168 e85d8982 Stephen Shirley

1169 e85d8982 Stephen Shirley
    """
1170 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1171 e85d8982 Stephen Shirley
1172 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1173 648e4196 Guido Trotter
    """Lookup a node group.
1174 648e4196 Guido Trotter

1175 648e4196 Guido Trotter
    @type uuid: string
1176 648e4196 Guido Trotter
    @param uuid: group UUID
1177 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1178 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1179 648e4196 Guido Trotter

1180 648e4196 Guido Trotter
    """
1181 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1182 648e4196 Guido Trotter
      return None
1183 648e4196 Guido Trotter
1184 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1185 648e4196 Guido Trotter
1186 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1187 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1188 5768e6a6 René Nussbaumer
    """Lookup a node group.
1189 5768e6a6 René Nussbaumer

1190 5768e6a6 René Nussbaumer
    @type uuid: string
1191 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1192 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1193 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1194 5768e6a6 René Nussbaumer

1195 5768e6a6 René Nussbaumer
    """
1196 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1197 5768e6a6 René Nussbaumer
1198 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1199 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1200 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1201 622444e5 Iustin Pop

1202 622444e5 Iustin Pop
    """
1203 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1204 622444e5 Iustin Pop
1205 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1206 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1207 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1208 1ac6f2ad Guido Trotter

1209 1ac6f2ad Guido Trotter
    """
1210 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1211 1ac6f2ad Guido Trotter
1212 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1213 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1214 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1215 dac81741 Michael Hanselmann

1216 dac81741 Michael Hanselmann
    """
1217 dac81741 Michael Hanselmann
    ngfn = lambda node_name: self._UnlockedGetNodeInfo(node_name).group
1218 dac81741 Michael Hanselmann
    return frozenset(member_name
1219 dac81741 Michael Hanselmann
                     for node_name in nodes
1220 dac81741 Michael Hanselmann
                     for member_name in
1221 dac81741 Michael Hanselmann
                       self._UnlockedGetNodeGroup(ngfn(node_name)).members)
1222 dac81741 Michael Hanselmann
1223 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1224 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1225 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1226 080fbeea Michael Hanselmann

1227 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1228 080fbeea Michael Hanselmann
    @rtype: list
1229 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1230 080fbeea Michael Hanselmann

1231 080fbeea Michael Hanselmann
    """
1232 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1233 080fbeea Michael Hanselmann
1234 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1235 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1236 a8083063 Iustin Pop
    """Add an instance to the config.
1237 a8083063 Iustin Pop

1238 a8083063 Iustin Pop
    This should be used after creating a new instance.
1239 a8083063 Iustin Pop

1240 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1241 c41eea6e Iustin Pop
    @param instance: the instance object
1242 c41eea6e Iustin Pop

1243 a8083063 Iustin Pop
    """
1244 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1245 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1246 a8083063 Iustin Pop
1247 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1248 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1249 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1250 923b1523 Iustin Pop
1251 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1252 e4640214 Guido Trotter
    for nic in instance.nics:
1253 e4640214 Guido Trotter
      if nic.mac in all_macs:
1254 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1255 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1256 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1257 430b923c Iustin Pop
1258 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1259 e4640214 Guido Trotter
1260 b989e85d Iustin Pop
    instance.serial_no = 1
1261 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1262 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1263 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1264 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1265 a8083063 Iustin Pop
    self._WriteConfig()
1266 a8083063 Iustin Pop
1267 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1268 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1269 430b923c Iustin Pop

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

1273 430b923c Iustin Pop
    """
1274 430b923c Iustin Pop
    if not item.uuid:
1275 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1276 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1277 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1278 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1279 430b923c Iustin Pop
1280 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1281 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1282 a8083063 Iustin Pop

1283 a8083063 Iustin Pop
    """
1284 2e04d454 Agata Murawska
    assert status in constants.ADMINST_ALL, \
1285 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1286 a8083063 Iustin Pop
1287 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1288 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1289 3ecf6786 Iustin Pop
                                      instance_name)
1290 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1291 9ca8a7c5 Agata Murawska
    if instance.admin_state != status:
1292 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1293 b989e85d Iustin Pop
      instance.serial_no += 1
1294 d693c864 Iustin Pop
      instance.mtime = time.time()
1295 455a3445 Iustin Pop
      self._WriteConfig()
1296 a8083063 Iustin Pop
1297 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1298 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1299 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1300 6a408fb2 Iustin Pop

1301 6a408fb2 Iustin Pop
    """
1302 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_UP)
1303 57de31c0 Agata Murawska
1304 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1305 57de31c0 Agata Murawska
  def MarkInstanceOffline(self, instance_name):
1306 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1307 57de31c0 Agata Murawska

1308 57de31c0 Agata Murawska
    """
1309 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_OFFLINE)
1310 6a408fb2 Iustin Pop
1311 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1312 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1313 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1314 a8083063 Iustin Pop

1315 a8083063 Iustin Pop
    """
1316 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1317 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1318 f396ad8c Vangelis Koukis
1319 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1320 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1321 f396ad8c Vangelis Koukis
    inst = self._config_data.instances[instance_name]
1322 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1323 f396ad8c Vangelis Koukis
    if network_port is not None:
1324 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1325 f396ad8c Vangelis Koukis
1326 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1327 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1328 a8083063 Iustin Pop
    self._WriteConfig()
1329 a8083063 Iustin Pop
1330 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1331 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1332 fc95f88f Iustin Pop
    """Rename an instance.
1333 fc95f88f Iustin Pop

1334 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1335 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1336 fc95f88f Iustin Pop
    rename.
1337 fc95f88f Iustin Pop

1338 fc95f88f Iustin Pop
    """
1339 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
1340 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
1341 ea642319 Michael Hanselmann
1342 ea642319 Michael Hanselmann
    # Operate on a copy to not loose instance object in case of a failure
1343 ea642319 Michael Hanselmann
    inst = self._config_data.instances[old_name].Copy()
1344 fc95f88f Iustin Pop
    inst.name = new_name
1345 b23c4333 Manuel Franceschini
1346 ea642319 Michael Hanselmann
    for (idx, disk) in enumerate(inst.disks):
1347 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1348 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1349 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1350 ea642319 Michael Hanselmann
        disk.logical_id = (disk.logical_id[0],
1351 ea642319 Michael Hanselmann
                           utils.PathJoin(file_storage_dir, inst.name,
1352 ea642319 Michael Hanselmann
                                          "disk%s" % idx))
1353 ea642319 Michael Hanselmann
        disk.physical_id = disk.logical_id
1354 ea642319 Michael Hanselmann
1355 ea642319 Michael Hanselmann
    # Actually replace instance object
1356 ea642319 Michael Hanselmann
    del self._config_data.instances[old_name]
1357 ea642319 Michael Hanselmann
    self._config_data.instances[inst.name] = inst
1358 b23c4333 Manuel Franceschini
1359 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1360 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1361 1fc34c48 Michael Hanselmann
1362 fc95f88f Iustin Pop
    self._WriteConfig()
1363 fc95f88f Iustin Pop
1364 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1365 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
1366 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1367 a8083063 Iustin Pop

1368 a8083063 Iustin Pop
    """
1369 2e04d454 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_DOWN)
1370 a8083063 Iustin Pop
1371 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1372 94bbfece Iustin Pop
    """Get the list of instances.
1373 94bbfece Iustin Pop

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

1376 94bbfece Iustin Pop
    """
1377 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1378 94bbfece Iustin Pop
1379 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1380 a8083063 Iustin Pop
  def GetInstanceList(self):
1381 a8083063 Iustin Pop
    """Get the list of instances.
1382 a8083063 Iustin Pop

1383 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1384 c41eea6e Iustin Pop
        'instance1.example.com']
1385 a8083063 Iustin Pop

1386 a8083063 Iustin Pop
    """
1387 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1388 a8083063 Iustin Pop
1389 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1390 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1391 a8083063 Iustin Pop

1392 a8083063 Iustin Pop
    """
1393 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1394 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1395 a8083063 Iustin Pop
1396 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1397 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1398 94bbfece Iustin Pop

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

1401 94bbfece Iustin Pop
    """
1402 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1403 94bbfece Iustin Pop
      return None
1404 94bbfece Iustin Pop
1405 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1406 94bbfece Iustin Pop
1407 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1408 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1409 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1410 a8083063 Iustin Pop

1411 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1412 a8083063 Iustin Pop
    an instance are taken from the live systems.
1413 a8083063 Iustin Pop

1414 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1415 c41eea6e Iustin Pop
        I{instance1.example.com}
1416 a8083063 Iustin Pop

1417 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1418 c41eea6e Iustin Pop
    @return: the instance object
1419 a8083063 Iustin Pop

1420 a8083063 Iustin Pop
    """
1421 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1422 a8083063 Iustin Pop
1423 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1424 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1425 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1426 2674690b Michael Hanselmann

1427 2674690b Michael Hanselmann
    @rtype: frozenset
1428 2674690b Michael Hanselmann

1429 2674690b Michael Hanselmann
    """
1430 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1431 2674690b Michael Hanselmann
    if not instance:
1432 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1433 2674690b Michael Hanselmann
1434 2674690b Michael Hanselmann
    if primary_only:
1435 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1436 2674690b Michael Hanselmann
    else:
1437 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1438 2674690b Michael Hanselmann
1439 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1440 2674690b Michael Hanselmann
                     for node_name in nodes)
1441 2674690b Michael Hanselmann
1442 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1443 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1444 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1445 71333cb9 Iustin Pop

1446 71333cb9 Iustin Pop
    @param instances: list of instance names
1447 71333cb9 Iustin Pop
    @rtype: list
1448 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1449 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1450 71333cb9 Iustin Pop
        node, while keeping the original order
1451 71333cb9 Iustin Pop

1452 71333cb9 Iustin Pop
    """
1453 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1454 71333cb9 Iustin Pop
1455 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1456 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1457 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1458 0b2de758 Iustin Pop

1459 0b2de758 Iustin Pop
    @rtype: dict
1460 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1461 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1462 0b2de758 Iustin Pop

1463 0b2de758 Iustin Pop
    """
1464 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1465 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1466 0b2de758 Iustin Pop
    return my_dict
1467 0b2de758 Iustin Pop
1468 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1469 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1470 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1471 cc19798f Michael Hanselmann

1472 cc19798f Michael Hanselmann
    @type filter_fn: callable
1473 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1474 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1475 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1476 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1477 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1478 cc19798f Michael Hanselmann

1479 cc19798f Michael Hanselmann
    """
1480 cc19798f Michael Hanselmann
    return dict((name, inst)
1481 cc19798f Michael Hanselmann
                for (name, inst) in self._config_data.instances.items()
1482 cc19798f Michael Hanselmann
                if filter_fn(inst))
1483 cc19798f Michael Hanselmann
1484 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1485 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1486 a8083063 Iustin Pop
    """Add a node to the configuration.
1487 a8083063 Iustin Pop

1488 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1489 c41eea6e Iustin Pop
    @param node: a Node instance
1490 a8083063 Iustin Pop

1491 a8083063 Iustin Pop
    """
1492 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1493 d8470559 Michael Hanselmann
1494 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1495 430b923c Iustin Pop
1496 b989e85d Iustin Pop
    node.serial_no = 1
1497 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1498 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1499 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1500 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1501 a8083063 Iustin Pop
    self._WriteConfig()
1502 a8083063 Iustin Pop
1503 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1504 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1505 a8083063 Iustin Pop
    """Remove a node from the configuration.
1506 a8083063 Iustin Pop

1507 a8083063 Iustin Pop
    """
1508 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1509 d8470559 Michael Hanselmann
1510 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1511 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1512 a8083063 Iustin Pop
1513 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1514 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1515 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1516 a8083063 Iustin Pop
    self._WriteConfig()
1517 a8083063 Iustin Pop
1518 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1519 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1520 a8083063 Iustin Pop

1521 a8083063 Iustin Pop
    """
1522 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1523 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1524 a8083063 Iustin Pop
1525 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1526 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1527 a8083063 Iustin Pop

1528 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1529 c41eea6e Iustin Pop
    held.
1530 f78ede4e Guido Trotter

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

1533 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1534 c41eea6e Iustin Pop
    @return: the node object
1535 a8083063 Iustin Pop

1536 a8083063 Iustin Pop
    """
1537 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1538 a8083063 Iustin Pop
      return None
1539 a8083063 Iustin Pop
1540 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1541 a8083063 Iustin Pop
1542 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1543 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1544 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1545 f78ede4e Guido Trotter

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

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

1550 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1551 c41eea6e Iustin Pop
    @return: the node object
1552 f78ede4e Guido Trotter

1553 f78ede4e Guido Trotter
    """
1554 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1555 f78ede4e Guido Trotter
1556 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1557 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1558 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1559 8bf9e9a5 Iustin Pop

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

1562 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1563 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1564 8bf9e9a5 Iustin Pop

1565 8bf9e9a5 Iustin Pop
    """
1566 8bf9e9a5 Iustin Pop
    pri = []
1567 8bf9e9a5 Iustin Pop
    sec = []
1568 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1569 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1570 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1571 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1572 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1573 8bf9e9a5 Iustin Pop
    return (pri, sec)
1574 8bf9e9a5 Iustin Pop
1575 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1576 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1577 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1578 c71b049c Michael Hanselmann

1579 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1580 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1581 c71b049c Michael Hanselmann
    @rtype: frozenset
1582 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1583 c71b049c Michael Hanselmann

1584 c71b049c Michael Hanselmann
    """
1585 c71b049c Michael Hanselmann
    if primary_only:
1586 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1587 c71b049c Michael Hanselmann
    else:
1588 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1589 c71b049c Michael Hanselmann
1590 c71b049c Michael Hanselmann
    return frozenset(inst.name
1591 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1592 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1593 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1594 c71b049c Michael Hanselmann
1595 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1596 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1597 a8083063 Iustin Pop

1598 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1599 c41eea6e Iustin Pop
    held.
1600 c41eea6e Iustin Pop

1601 c41eea6e Iustin Pop
    @rtype: list
1602 f78ede4e Guido Trotter

1603 a8083063 Iustin Pop
    """
1604 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1605 a8083063 Iustin Pop
1606 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1607 f78ede4e Guido Trotter
  def GetNodeList(self):
1608 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1609 f78ede4e Guido Trotter

1610 f78ede4e Guido Trotter
    """
1611 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1612 f78ede4e Guido Trotter
1613 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1614 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1615 94a02bb5 Iustin Pop

1616 94a02bb5 Iustin Pop
    """
1617 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1618 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1619 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1620 94a02bb5 Iustin Pop
1621 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1622 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1623 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1624 6819dc49 Iustin Pop

1625 6819dc49 Iustin Pop
    """
1626 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1627 6819dc49 Iustin Pop
1628 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1629 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1630 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1631 075b62ca Iustin Pop

1632 075b62ca Iustin Pop
    """
1633 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1634 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1635 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1636 075b62ca Iustin Pop
1637 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1638 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1639 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1640 8bf9e9a5 Iustin Pop

1641 8bf9e9a5 Iustin Pop
    """
1642 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1643 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1644 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1645 8bf9e9a5 Iustin Pop
1646 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1647 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1648 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1649 f5eaa3c1 Iustin Pop

1650 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1651 f5eaa3c1 Iustin Pop
    @rtype: list
1652 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1653 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1654 f5eaa3c1 Iustin Pop
        order
1655 f5eaa3c1 Iustin Pop

1656 f5eaa3c1 Iustin Pop
    """
1657 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1658 f5eaa3c1 Iustin Pop
1659 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1660 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1661 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1662 d65e5776 Iustin Pop

1663 d65e5776 Iustin Pop
    @rtype: dict
1664 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1665 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1666 d65e5776 Iustin Pop

1667 d65e5776 Iustin Pop
    """
1668 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1669 ee14d800 Michael Hanselmann
1670 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1671 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1672 ee14d800 Michael Hanselmann

1673 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1674 ee14d800 Michael Hanselmann

1675 ee14d800 Michael Hanselmann
    """
1676 ee14d800 Michael Hanselmann
    return dict([(node, self._UnlockedGetNodeInfo(node))
1677 ee14d800 Michael Hanselmann
                 for node in self._UnlockedGetNodeList()])
1678 d65e5776 Iustin Pop
1679 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1680 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1681 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1682 9d5b1371 Michael Hanselmann

1683 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1684 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1685 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1686 9d5b1371 Michael Hanselmann

1687 9d5b1371 Michael Hanselmann
    """
1688 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1689 9d5b1371 Michael Hanselmann
1690 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1691 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1692 ec0292f1 Iustin Pop

1693 23f06b2b Iustin Pop
    @type exceptions: list
1694 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1695 ec0292f1 Iustin Pop
    @rtype: tuple
1696 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1697 ec0292f1 Iustin Pop

1698 ec0292f1 Iustin Pop
    """
1699 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1700 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1701 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1702 23f06b2b Iustin Pop
        continue
1703 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1704 ec0292f1 Iustin Pop
        mc_max += 1
1705 ec0292f1 Iustin Pop
      if node.master_candidate:
1706 ec0292f1 Iustin Pop
        mc_now += 1
1707 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1708 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1709 ec0292f1 Iustin Pop
1710 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1711 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1712 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1713 ec0292f1 Iustin Pop

1714 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1715 ec0292f1 Iustin Pop

1716 23f06b2b Iustin Pop
    @type exceptions: list
1717 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1718 ec0292f1 Iustin Pop
    @rtype: tuple
1719 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1720 ec0292f1 Iustin Pop

1721 ec0292f1 Iustin Pop
    """
1722 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1723 ec0292f1 Iustin Pop
1724 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1725 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1726 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1727 ec0292f1 Iustin Pop

1728 44485f49 Guido Trotter
    @type exceptions: list
1729 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1730 ec0292f1 Iustin Pop
    @rtype: list
1731 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1732 ec0292f1 Iustin Pop

1733 ec0292f1 Iustin Pop
    """
1734 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1735 ec0292f1 Iustin Pop
    mod_list = []
1736 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1737 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1738 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1739 ec0292f1 Iustin Pop
      for name in node_list:
1740 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1741 ec0292f1 Iustin Pop
          break
1742 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1743 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1744 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1745 ec0292f1 Iustin Pop
          continue
1746 ee513a66 Iustin Pop
        mod_list.append(node)
1747 ec0292f1 Iustin Pop
        node.master_candidate = True
1748 ec0292f1 Iustin Pop
        node.serial_no += 1
1749 ec0292f1 Iustin Pop
        mc_now += 1
1750 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1751 ec0292f1 Iustin Pop
        # this should not happen
1752 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1753 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1754 ec0292f1 Iustin Pop
      if mod_list:
1755 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1756 ec0292f1 Iustin Pop
        self._WriteConfig()
1757 ec0292f1 Iustin Pop
1758 ec0292f1 Iustin Pop
    return mod_list
1759 ec0292f1 Iustin Pop
1760 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1761 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1762 190e3cb6 Guido Trotter

1763 190e3cb6 Guido Trotter
    """
1764 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1765 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1766 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1767 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1768 190e3cb6 Guido Trotter
      # is not found anymore.
1769 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1770 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1771 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1772 190e3cb6 Guido Trotter
1773 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1774 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1775 190e3cb6 Guido Trotter

1776 190e3cb6 Guido Trotter
    """
1777 f936c153 Iustin Pop
    nodegroup = node.group
1778 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1779 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1780 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1781 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1782 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1783 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1784 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1785 190e3cb6 Guido Trotter
    else:
1786 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1787 190e3cb6 Guido Trotter
1788 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
1789 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
1790 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
1791 54c31fd3 Michael Hanselmann

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

1795 54c31fd3 Michael Hanselmann
    """
1796 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
1797 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
1798 54c31fd3 Michael Hanselmann
1799 54c31fd3 Michael Hanselmann
    resmod = []
1800 54c31fd3 Michael Hanselmann
1801 54c31fd3 Michael Hanselmann
    # Try to resolve names/UUIDs first
1802 54c31fd3 Michael Hanselmann
    for (node_name, new_group_uuid) in mods:
1803 54c31fd3 Michael Hanselmann
      try:
1804 54c31fd3 Michael Hanselmann
        node = nodes[node_name]
1805 54c31fd3 Michael Hanselmann
      except KeyError:
1806 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find node '%s'" % node_name)
1807 54c31fd3 Michael Hanselmann
1808 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
1809 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
1810 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
1811 54c31fd3 Michael Hanselmann
                      node_name, node.group)
1812 54c31fd3 Michael Hanselmann
        continue
1813 54c31fd3 Michael Hanselmann
1814 54c31fd3 Michael Hanselmann
      # Try to find current group of node
1815 54c31fd3 Michael Hanselmann
      try:
1816 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
1817 54c31fd3 Michael Hanselmann
      except KeyError:
1818 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
1819 54c31fd3 Michael Hanselmann
                                        node.group)
1820 54c31fd3 Michael Hanselmann
1821 54c31fd3 Michael Hanselmann
      # Try to find new group for node
1822 54c31fd3 Michael Hanselmann
      try:
1823 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
1824 54c31fd3 Michael Hanselmann
      except KeyError:
1825 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
1826 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
1827 54c31fd3 Michael Hanselmann
1828 54c31fd3 Michael Hanselmann
      assert node.name in old_group.members, \
1829 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
1830 54c31fd3 Michael Hanselmann
         " old group '%s'" % (node.name, old_group.uuid))
1831 54c31fd3 Michael Hanselmann
      assert node.name not in new_group.members, \
1832 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
1833 54c31fd3 Michael Hanselmann
         " its new group '%s'" % (node.name, new_group.uuid))
1834 54c31fd3 Michael Hanselmann
1835 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
1836 54c31fd3 Michael Hanselmann
1837 54c31fd3 Michael Hanselmann
    # Apply changes
1838 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
1839 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
1840 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
1841 54c31fd3 Michael Hanselmann
1842 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
1843 54c31fd3 Michael Hanselmann
1844 54c31fd3 Michael Hanselmann
      # Update members of involved groups
1845 54c31fd3 Michael Hanselmann
      if node.name in old_group.members:
1846 54c31fd3 Michael Hanselmann
        old_group.members.remove(node.name)
1847 54c31fd3 Michael Hanselmann
      if node.name not in new_group.members:
1848 54c31fd3 Michael Hanselmann
        new_group.members.append(node.name)
1849 54c31fd3 Michael Hanselmann
1850 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
1851 54c31fd3 Michael Hanselmann
    now = time.time()
1852 75191077 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142
1853 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
1854 54c31fd3 Michael Hanselmann
      obj.mtime = now
1855 54c31fd3 Michael Hanselmann
1856 54c31fd3 Michael Hanselmann
    # Force ssconf update
1857 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1858 54c31fd3 Michael Hanselmann
1859 54c31fd3 Michael Hanselmann
    self._WriteConfig()
1860 54c31fd3 Michael Hanselmann
1861 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1862 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1863 a8083063 Iustin Pop

1864 a8083063 Iustin Pop
    """
1865 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1866 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1867 a8083063 Iustin Pop
1868 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1869 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1870 76d5d3a3 Iustin Pop

1871 76d5d3a3 Iustin Pop
    """
1872 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1873 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1874 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1875 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1876 76d5d3a3 Iustin Pop
1877 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1878 a8083063 Iustin Pop
    """Read the config data from disk.
1879 a8083063 Iustin Pop

1880 a8083063 Iustin Pop
    """
1881 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1882 13998ef2 Michael Hanselmann
1883 a8083063 Iustin Pop
    try:
1884 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1885 13998ef2 Michael Hanselmann
    except Exception, err:
1886 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1887 5b263ed7 Michael Hanselmann
1888 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1889 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1890 5b263ed7 Michael Hanselmann
1891 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
1892 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
1893 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1894 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1895 90d726a8 Iustin Pop
1896 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
1897 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
1898 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
1899 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
1900 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
1901 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
1902 eb180fe2 Iustin Pop
1903 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1904 90d726a8 Iustin Pop
    data.UpgradeConfig()
1905 90d726a8 Iustin Pop
1906 a8083063 Iustin Pop
    self._config_data = data
1907 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1908 0779e3aa Iustin Pop
    # ssconf update
1909 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1910 a8083063 Iustin Pop
1911 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1912 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1913 76d5d3a3 Iustin Pop
1914 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
1915 bd407597 Iustin Pop
1916 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1917 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1918 76d5d3a3 Iustin Pop

1919 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1920 76d5d3a3 Iustin Pop
    whole configuration, etc.
1921 76d5d3a3 Iustin Pop

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

1928 76d5d3a3 Iustin Pop
    """
1929 76d5d3a3 Iustin Pop
    modified = False
1930 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1931 76d5d3a3 Iustin Pop
      if item.uuid is None:
1932 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1933 76d5d3a3 Iustin Pop
        modified = True
1934 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
1935 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
1936 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
1937 75cf411a Adeodato Simo
                                            members=[])
1938 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
1939 f9e81396 Guido Trotter
      modified = True
1940 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
1941 f936c153 Iustin Pop
      if not node.group:
1942 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
1943 190e3cb6 Guido Trotter
        modified = True
1944 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
1945 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
1946 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
1947 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
1948 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
1949 76d5d3a3 Iustin Pop
    if modified:
1950 76d5d3a3 Iustin Pop
      self._WriteConfig()
1951 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1952 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1953 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1954 4fae38c5 Guido Trotter
1955 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1956 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1957 a8083063 Iustin Pop

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

1961 a8083063 Iustin Pop
    """
1962 a8083063 Iustin Pop
    if self._offline:
1963 a8083063 Iustin Pop
      return True
1964 a4eae71f Michael Hanselmann
1965 a8083063 Iustin Pop
    bad = False
1966 a8083063 Iustin Pop
1967 6a5b8b4b Iustin Pop
    node_list = []
1968 6a5b8b4b Iustin Pop
    addr_list = []
1969 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1970 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1971 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1972 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1973 6b294c53 Iustin Pop
    # in between
1974 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1975 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1976 6a5b8b4b Iustin Pop
        continue
1977 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1978 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1979 6a5b8b4b Iustin Pop
        continue
1980 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1981 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1982 6b294c53 Iustin Pop
1983 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
1984 415a7304 Michael Hanselmann
    result = \
1985 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
1986 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1987 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1988 1b54fc6c Guido Trotter
      if msg:
1989 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1990 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1991 1b54fc6c Guido Trotter
        logging.error(msg)
1992 a4eae71f Michael Hanselmann
1993 a4eae71f Michael Hanselmann
        if feedback_fn:
1994 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1995 a4eae71f Michael Hanselmann
1996 a8083063 Iustin Pop
        bad = True
1997 a4eae71f Michael Hanselmann
1998 a8083063 Iustin Pop
    return not bad
1999 a8083063 Iustin Pop
2000 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
2001 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
2002 a8083063 Iustin Pop

2003 a8083063 Iustin Pop
    """
2004 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
2005 a4eae71f Michael Hanselmann
2006 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
2007 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
2008 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
2009 d2231b8c Iustin Pop
    # recovery to the user
2010 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
2011 4a89c54a Iustin Pop
    if config_errors:
2012 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
2013 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
2014 d2231b8c Iustin Pop
      logging.critical(errmsg)
2015 d2231b8c Iustin Pop
      if feedback_fn:
2016 d2231b8c Iustin Pop
        feedback_fn(errmsg)
2017 d2231b8c Iustin Pop
2018 a8083063 Iustin Pop
    if destination is None:
2019 a8083063 Iustin Pop
      destination = self._cfg_file
2020 a8083063 Iustin Pop
    self._BumpSerialNo()
2021 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
2022 13998ef2 Michael Hanselmann
2023 e60c73a1 René Nussbaumer
    getents = self._getents()
2024 bd407597 Iustin Pop
    try:
2025 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
2026 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
2027 bd407597 Iustin Pop
    except errors.LockError:
2028 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
2029 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
2030 bd407597 Iustin Pop
                                      " update")
2031 bd407597 Iustin Pop
    try:
2032 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
2033 bd407597 Iustin Pop
    finally:
2034 bd407597 Iustin Pop
      os.close(fd)
2035 13998ef2 Michael Hanselmann
2036 14e15659 Iustin Pop
    self.write_count += 1
2037 3d3a04bc Iustin Pop
2038 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
2039 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
2040 a8083063 Iustin Pop
2041 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
2042 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
2043 d9a855f1 Michael Hanselmann
      if not self._offline:
2044 b2acdbdc Michael Hanselmann
        result = self._GetRpc(None).call_write_ssconf_files(
2045 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
2046 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
2047 a4eae71f Michael Hanselmann
2048 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
2049 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
2050 e1e75d00 Iustin Pop
          if msg:
2051 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
2052 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
2053 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
2054 a4eae71f Michael Hanselmann
2055 a4eae71f Michael Hanselmann
            if feedback_fn:
2056 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
2057 a4eae71f Michael Hanselmann
2058 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
2059 54d1a06e Michael Hanselmann
2060 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
2061 054596f0 Iustin Pop
    """Return the values needed by ssconf.
2062 054596f0 Iustin Pop

2063 054596f0 Iustin Pop
    @rtype: dict
2064 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2065 054596f0 Iustin Pop
        associated value
2066 054596f0 Iustin Pop

2067 054596f0 Iustin Pop
    """
2068 a3316e4a Iustin Pop
    fn = "\n".join
2069 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
2070 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
2071 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
2072 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
2073 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
2074 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
2075 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
2076 a3316e4a Iustin Pop
2077 81a49123 Iustin Pop
    instance_data = fn(instance_names)
2078 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
2079 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
2080 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
2081 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
2082 8113a52e Luca Bigliardi
                     if node.master_candidate)
2083 a3316e4a Iustin Pop
    node_data = fn(node_names)
2084 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
2085 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
2086 f56618e0 Iustin Pop
2087 054596f0 Iustin Pop
    cluster = self._config_data.cluster
2088 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
2089 4f7a6a10 Iustin Pop
2090 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
2091 4f7a6a10 Iustin Pop
2092 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
2093 0fbae49a Balazs Lecz
2094 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
2095 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
2096 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
2097 6f076453 Guido Trotter
2098 2afc9238 Iustin Pop
    ssconf_values = {
2099 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
2100 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
2101 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
2102 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
2103 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
2104 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
2105 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
2106 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
2107 5a8648eb Andrea Spadaccini
      constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
2108 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
2109 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
2110 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
2111 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
2112 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
2113 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
2114 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
2115 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
2116 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
2117 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
2118 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
2119 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
2120 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
2121 03d1dba2 Michael Hanselmann
      }
2122 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
2123 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
2124 2afc9238 Iustin Pop
    if bad_values:
2125 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
2126 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
2127 2afc9238 Iustin Pop
                                      " values: %s" % err)
2128 2afc9238 Iustin Pop
    return ssconf_values
2129 03d1dba2 Michael Hanselmann
2130 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2131 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
2132 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
2133 d367b66c Manuel Franceschini

2134 d367b66c Manuel Franceschini
    """
2135 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2136 d367b66c Manuel Franceschini
2137 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2138 a8083063 Iustin Pop
  def GetVGName(self):
2139 a8083063 Iustin Pop
    """Return the volume group name.
2140 a8083063 Iustin Pop

2141 a8083063 Iustin Pop
    """
2142 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2143 a8083063 Iustin Pop
2144 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2145 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2146 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2147 89ff8e15 Manuel Franceschini

2148 89ff8e15 Manuel Franceschini
    """
2149 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2150 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2151 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2152 89ff8e15 Manuel Franceschini
2153 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2154 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2155 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2156 9e33896b Luca Bigliardi

2157 9e33896b Luca Bigliardi
    """
2158 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2159 9e33896b Luca Bigliardi
2160 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2161 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2162 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2163 9e33896b Luca Bigliardi

2164 9e33896b Luca Bigliardi
    """
2165 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2166 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2167 9e33896b Luca Bigliardi
    self._WriteConfig()
2168 9e33896b Luca Bigliardi
2169 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2170 a8083063 Iustin Pop
  def GetMACPrefix(self):
2171 a8083063 Iustin Pop
    """Return the mac prefix.
2172 a8083063 Iustin Pop

2173 a8083063 Iustin Pop
    """
2174 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2175 62779dd0 Iustin Pop
2176 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2177 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2178 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2179 62779dd0 Iustin Pop

2180 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2181 c41eea6e Iustin Pop
    @return: the cluster object
2182 62779dd0 Iustin Pop

2183 62779dd0 Iustin Pop
    """
2184 62779dd0 Iustin Pop
    return self._config_data.cluster
2185 e00fb268 Iustin Pop
2186 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2187 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2188 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2189 51cb1581 Luca Bigliardi

2190 51cb1581 Luca Bigliardi
    """
2191 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2192 51cb1581 Luca Bigliardi
2193 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2194 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
2195 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2196 e00fb268 Iustin Pop

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

2203 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2204 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2205 c41eea6e Iustin Pop
        the cluster
2206 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2207 c41eea6e Iustin Pop

2208 e00fb268 Iustin Pop
    """
2209 e00fb268 Iustin Pop
    if self._config_data is None:
2210 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2211 3ecf6786 Iustin Pop
                                   " cannot save.")
2212 f34901f8 Iustin Pop
    update_serial = False
2213 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2214 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2215 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2216 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2217 f34901f8 Iustin Pop
      update_serial = True
2218 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2219 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2220 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2221 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2222 e00fb268 Iustin Pop
    else:
2223 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2224 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2225 e00fb268 Iustin Pop
    if not test:
2226 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2227 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2228 f34901f8 Iustin Pop
    target.serial_no += 1
2229 d693c864 Iustin Pop
    target.mtime = now = time.time()
2230 f34901f8 Iustin Pop
2231 cff4c037 Iustin Pop
    if update_serial:
2232 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2233 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2234 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2235 b989e85d Iustin Pop
2236 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2237 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
2238 61cf6b5e Iustin Pop
2239 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2240 73064714 Guido Trotter
2241 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2242 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2243 73064714 Guido Trotter
    """Drop per-execution-context reservations
2244 73064714 Guido Trotter

2245 73064714 Guido Trotter
    """
2246 d8aee57e Iustin Pop
    for rm in self._all_rms:
2247 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)