Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 2435f63b

History | View | Annotate | Download (74.8 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 12378fe3 Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Configuration management for Ganeti
23 a8083063 Iustin Pop

24 319856a9 Michael Hanselmann
This module provides the interface to the Ganeti cluster configuration.
25 a8083063 Iustin Pop

26 319856a9 Michael Hanselmann
The configuration data is stored on every node but is updated on the master
27 319856a9 Michael Hanselmann
only. After each update, the master distributes the data to the other nodes.
28 a8083063 Iustin Pop

29 319856a9 Michael Hanselmann
Currently, the data storage format is JSON. YAML was slow and consuming too
30 319856a9 Michael Hanselmann
much memory.
31 a8083063 Iustin Pop

32 a8083063 Iustin Pop
"""
33 a8083063 Iustin Pop
34 b459a848 Andrea Spadaccini
# pylint: disable=R0904
35 d367b66c Manuel Franceschini
# R0904: Too many public methods
36 d367b66c Manuel Franceschini
37 a8083063 Iustin Pop
import os
38 a8083063 Iustin Pop
import random
39 d8470559 Michael Hanselmann
import logging
40 d693c864 Iustin Pop
import time
41 54c31fd3 Michael Hanselmann
import itertools
42 a8083063 Iustin Pop
43 a8083063 Iustin Pop
from ganeti import errors
44 f78ede4e Guido Trotter
from ganeti import locking
45 a8083063 Iustin Pop
from ganeti import utils
46 a8083063 Iustin Pop
from ganeti import constants
47 a8083063 Iustin Pop
from ganeti import rpc
48 a8083063 Iustin Pop
from ganeti import objects
49 8d14b30d Iustin Pop
from ganeti import serializer
50 0fbae49a Balazs Lecz
from ganeti import uidpool
51 a744b676 Manuel Franceschini
from ganeti import netutils
52 e60c73a1 René Nussbaumer
from ganeti import runtime
53 243cdbcc Michael Hanselmann
54 243cdbcc Michael Hanselmann
55 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
56 f78ede4e Guido Trotter
57 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
58 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
59 4fae38c5 Guido Trotter
60 f78ede4e Guido Trotter
61 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
62 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
63 c41eea6e Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

285 1862d460 Alexander Schreiber
    """
286 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
287 36b66e6e Guido Trotter
    if mac in all_macs:
288 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
289 36b66e6e Guido Trotter
    else:
290 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
291 1862d460 Alexander Schreiber
292 c1bfc2eb Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
293 2435f63b Dimitris Aragiorgis
  def GetHotplugIndex(self, instance, dev_type):
294 9ac60360 Dimitris Aragiorgis
295 9ac60360 Dimitris Aragiorgis
    idx = getattr(instance.hotplug_info, dev_type)
296 2435f63b Dimitris Aragiorgis
    setattr(instance.hotplug_info, dev_type, idx + 1)
297 9ac60360 Dimitris Aragiorgis
    self._WriteConfig()
298 9ac60360 Dimitris Aragiorgis
299 2435f63b Dimitris Aragiorgis
    return idx
300 9ac60360 Dimitris Aragiorgis
301 9ac60360 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
302 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
303 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
304 d8aee57e Iustin Pop

305 d8aee57e Iustin Pop
    @type lv_name: string
306 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
307 d8aee57e Iustin Pop

308 d8aee57e Iustin Pop
    """
309 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
310 d8aee57e Iustin Pop
    if lv_name in all_lvs:
311 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
312 d8aee57e Iustin Pop
    else:
313 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
314 d8aee57e Iustin Pop
315 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
316 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
317 f9518d38 Iustin Pop
    """Generate a DRBD secret.
318 f9518d38 Iustin Pop

319 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
320 f9518d38 Iustin Pop

321 f9518d38 Iustin Pop
    """
322 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
323 afa1386e Guido Trotter
                                            utils.GenerateSecret,
324 afa1386e Guido Trotter
                                            ec_id)
325 8d9c3bef Michael Hanselmann
326 34e54ebc Iustin Pop
  def _AllLVs(self):
327 923b1523 Iustin Pop
    """Compute the list of all LVs.
328 923b1523 Iustin Pop

329 923b1523 Iustin Pop
    """
330 923b1523 Iustin Pop
    lvnames = set()
331 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
332 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
333 923b1523 Iustin Pop
      for lv_list in node_data.values():
334 923b1523 Iustin Pop
        lvnames.update(lv_list)
335 923b1523 Iustin Pop
    return lvnames
336 923b1523 Iustin Pop
337 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
338 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
339 34e54ebc Iustin Pop

340 34e54ebc Iustin Pop
    @type include_temporary: boolean
341 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
342 34e54ebc Iustin Pop
    @rtype: set
343 34e54ebc Iustin Pop
    @return: a set of IDs
344 34e54ebc Iustin Pop

345 34e54ebc Iustin Pop
    """
346 34e54ebc Iustin Pop
    existing = set()
347 34e54ebc Iustin Pop
    if include_temporary:
348 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
349 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
350 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
351 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
352 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
353 34e54ebc Iustin Pop
    return existing
354 34e54ebc Iustin Pop
355 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
356 430b923c Iustin Pop
    """Generate an unique UUID.
357 923b1523 Iustin Pop

358 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
359 923b1523 Iustin Pop
    duplicates.
360 923b1523 Iustin Pop

361 c41eea6e Iustin Pop
    @rtype: string
362 c41eea6e Iustin Pop
    @return: the unique id
363 923b1523 Iustin Pop

364 923b1523 Iustin Pop
    """
365 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
366 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
367 923b1523 Iustin Pop
368 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
369 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
370 430b923c Iustin Pop
    """Generate an unique ID.
371 430b923c Iustin Pop

372 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
373 430b923c Iustin Pop

374 4fae38c5 Guido Trotter
    @type ec_id: string
375 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
376 34d657ba Iustin Pop

377 34d657ba Iustin Pop
    """
378 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
379 34d657ba Iustin Pop
380 a8083063 Iustin Pop
  def _AllMACs(self):
381 a8083063 Iustin Pop
    """Return all MACs present in the config.
382 a8083063 Iustin Pop

383 c41eea6e Iustin Pop
    @rtype: list
384 c41eea6e Iustin Pop
    @return: the list of all MACs
385 c41eea6e Iustin Pop

386 a8083063 Iustin Pop
    """
387 a8083063 Iustin Pop
    result = []
388 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
389 a8083063 Iustin Pop
      for nic in instance.nics:
390 a8083063 Iustin Pop
        result.append(nic.mac)
391 a8083063 Iustin Pop
392 a8083063 Iustin Pop
    return result
393 a8083063 Iustin Pop
394 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
395 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
396 f9518d38 Iustin Pop

397 c41eea6e Iustin Pop
    @rtype: list
398 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
399 c41eea6e Iustin Pop

400 f9518d38 Iustin Pop
    """
401 f9518d38 Iustin Pop
    def helper(disk, result):
402 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
403 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
404 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
405 f9518d38 Iustin Pop
      if disk.children:
406 f9518d38 Iustin Pop
        for child in disk.children:
407 f9518d38 Iustin Pop
          helper(child, result)
408 f9518d38 Iustin Pop
409 f9518d38 Iustin Pop
    result = []
410 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
411 f9518d38 Iustin Pop
      for disk in instance.disks:
412 f9518d38 Iustin Pop
        helper(disk, result)
413 f9518d38 Iustin Pop
414 f9518d38 Iustin Pop
    return result
415 f9518d38 Iustin Pop
416 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
417 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
418 4b98ac29 Iustin Pop

419 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
420 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
421 4b98ac29 Iustin Pop
    @type l_ids: list
422 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
423 4b98ac29 Iustin Pop
    @type p_ids: list
424 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
425 4b98ac29 Iustin Pop
    @rtype: list
426 4b98ac29 Iustin Pop
    @return: a list of error messages
427 4b98ac29 Iustin Pop

428 4b98ac29 Iustin Pop
    """
429 4b98ac29 Iustin Pop
    result = []
430 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
431 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
432 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
433 25ae22e4 Iustin Pop
      else:
434 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
435 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
436 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
437 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
438 25ae22e4 Iustin Pop
      else:
439 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
440 4b98ac29 Iustin Pop
441 4b98ac29 Iustin Pop
    if disk.children:
442 4b98ac29 Iustin Pop
      for child in disk.children:
443 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
444 4b98ac29 Iustin Pop
    return result
445 4b98ac29 Iustin Pop
446 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
447 a8efbb40 Iustin Pop
    """Verify function.
448 a8efbb40 Iustin Pop

449 4a89c54a Iustin Pop
    @rtype: list
450 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
451 4a89c54a Iustin Pop
        configuration errors
452 4a89c54a Iustin Pop

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

706 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
707 4a89c54a Iustin Pop

708 4a89c54a Iustin Pop
    @rtype: list
709 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
710 4a89c54a Iustin Pop
        configuration errors
711 4a89c54a Iustin Pop

712 4a89c54a Iustin Pop
    """
713 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
714 4a89c54a Iustin Pop
715 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
716 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
717 a8083063 Iustin Pop

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

720 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
721 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
722 a8083063 Iustin Pop
    node.
723 a8083063 Iustin Pop

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

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

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

759 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
760 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
761 f78ede4e Guido Trotter
    node.
762 f78ede4e Guido Trotter

763 f78ede4e Guido Trotter
    """
764 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
765 f78ede4e Guido Trotter
766 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
767 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
768 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
769 b2fddf63 Iustin Pop

770 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
771 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
772 3b3b1bca Dimitris Aragiorgis
        configuration is stable
773 3b3b1bca Dimitris Aragiorgis

774 b2fddf63 Iustin Pop
    """
775 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
776 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
777 264bb3c5 Michael Hanselmann
778 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
779 264bb3c5 Michael Hanselmann
780 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
781 b2fddf63 Iustin Pop
  def GetPortList(self):
782 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
783 264bb3c5 Michael Hanselmann

784 264bb3c5 Michael Hanselmann
    """
785 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
786 264bb3c5 Michael Hanselmann
787 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
788 a8083063 Iustin Pop
  def AllocatePort(self):
789 a8083063 Iustin Pop
    """Allocate a port.
790 a8083063 Iustin Pop

791 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
792 b2fddf63 Iustin Pop
    default port range (and in this case we increase
793 b2fddf63 Iustin Pop
    highest_used_port).
794 a8083063 Iustin Pop

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

813 4a89c54a Iustin Pop
    @rtype: (dict, list)
814 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
815 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
816 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
817 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
818 4a89c54a Iustin Pop
        should raise an exception
819 a81c53c9 Iustin Pop

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

853 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
854 6d2e83d5 Iustin Pop

855 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
856 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
857 6d2e83d5 Iustin Pop
        an empty list).
858 6d2e83d5 Iustin Pop

859 6d2e83d5 Iustin Pop
    """
860 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
861 4a89c54a Iustin Pop
    if duplicates:
862 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
863 4a89c54a Iustin Pop
                                      str(duplicates))
864 4a89c54a Iustin Pop
    return d_map
865 6d2e83d5 Iustin Pop
866 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
867 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
868 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
869 a81c53c9 Iustin Pop

870 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
871 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
872 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
873 a81c53c9 Iustin Pop
    order as the passed nodes.
874 a81c53c9 Iustin Pop

875 32388e6d Iustin Pop
    @type instance: string
876 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
877 32388e6d Iustin Pop

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

925 a81c53c9 Iustin Pop
    @type instance: string
926 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
927 a81c53c9 Iustin Pop
                     released
928 a81c53c9 Iustin Pop

929 a81c53c9 Iustin Pop
    """
930 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
931 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
932 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
933 a81c53c9 Iustin Pop
      if name == instance:
934 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
935 a81c53c9 Iustin Pop
936 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
937 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
938 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
939 61cf6b5e Iustin Pop

940 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
941 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
942 61cf6b5e Iustin Pop
    functions.
943 61cf6b5e Iustin Pop

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

946 61cf6b5e Iustin Pop
    @type instance: string
947 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
948 61cf6b5e Iustin Pop
                     released
949 61cf6b5e Iustin Pop

950 61cf6b5e Iustin Pop
    """
951 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
952 61cf6b5e Iustin Pop
953 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
954 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
955 4a8b186a Michael Hanselmann
    """Get the configuration version.
956 4a8b186a Michael Hanselmann

957 4a8b186a Michael Hanselmann
    @return: Config version
958 4a8b186a Michael Hanselmann

959 4a8b186a Michael Hanselmann
    """
960 4a8b186a Michael Hanselmann
    return self._config_data.version
961 4a8b186a Michael Hanselmann
962 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
963 4a8b186a Michael Hanselmann
  def GetClusterName(self):
964 4a8b186a Michael Hanselmann
    """Get cluster name.
965 4a8b186a Michael Hanselmann

966 4a8b186a Michael Hanselmann
    @return: Cluster name
967 4a8b186a Michael Hanselmann

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

975 4a8b186a Michael Hanselmann
    @return: Master hostname
976 4a8b186a Michael Hanselmann

977 4a8b186a Michael Hanselmann
    """
978 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
979 4a8b186a Michael Hanselmann
980 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
981 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
982 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
983 4a8b186a Michael Hanselmann

984 4a8b186a Michael Hanselmann
    @return: Master IP
985 4a8b186a Michael Hanselmann

986 4a8b186a Michael Hanselmann
    """
987 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
988 4a8b186a Michael Hanselmann
989 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
990 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
991 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
992 4a8b186a Michael Hanselmann

993 4a8b186a Michael Hanselmann
    """
994 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
995 4a8b186a Michael Hanselmann
996 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
997 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
998 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
999 5a8648eb Andrea Spadaccini

1000 5a8648eb Andrea Spadaccini
    """
1001 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
1002 5a8648eb Andrea Spadaccini
1003 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1004 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
1005 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
1006 33be7576 Andrea Spadaccini

1007 33be7576 Andrea Spadaccini
    """
1008 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
1009 33be7576 Andrea Spadaccini
1010 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1011 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
1012 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
1013 4a8b186a Michael Hanselmann

1014 4a8b186a Michael Hanselmann
    """
1015 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
1016 4a8b186a Michael Hanselmann
1017 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1018 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
1019 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
1020 4b97f902 Apollon Oikonomopoulos

1021 4b97f902 Apollon Oikonomopoulos
    """
1022 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1023 4b97f902 Apollon Oikonomopoulos
1024 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1025 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1026 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1027 4a8b186a Michael Hanselmann

1028 4a8b186a Michael Hanselmann
    """
1029 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
1030 4a8b186a Michael Hanselmann
1031 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1032 a8083063 Iustin Pop
  def GetHostKey(self):
1033 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1034 a8083063 Iustin Pop

1035 c41eea6e Iustin Pop
    @rtype: string
1036 c41eea6e Iustin Pop
    @return: the rsa hostkey
1037 a8083063 Iustin Pop

1038 a8083063 Iustin Pop
    """
1039 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1040 a8083063 Iustin Pop
1041 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1042 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1043 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1044 bf4af505 Apollon Oikonomopoulos

1045 bf4af505 Apollon Oikonomopoulos
    """
1046 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1047 bf4af505 Apollon Oikonomopoulos
1048 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1049 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1050 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1051 868a98ca Manuel Franceschini

1052 868a98ca Manuel Franceschini
    @return: primary ip family
1053 868a98ca Manuel Franceschini

1054 868a98ca Manuel Franceschini
    """
1055 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1056 868a98ca Manuel Franceschini
1057 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1058 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1059 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1060 c9f4b8e6 Andrea Spadaccini

1061 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1062 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1063 c9f4b8e6 Andrea Spadaccini

1064 c9f4b8e6 Andrea Spadaccini
    """
1065 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1066 c79198a0 Andrea Spadaccini
    result = objects.MasterNetworkParameters(name=cluster.master_node,
1067 c79198a0 Andrea Spadaccini
      ip=cluster.master_ip,
1068 c79198a0 Andrea Spadaccini
      netmask=cluster.master_netmask,
1069 c79198a0 Andrea Spadaccini
      netdev=cluster.master_netdev,
1070 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1071 c9f4b8e6 Andrea Spadaccini
1072 f9d20654 Andrea Spadaccini
    return result
1073 f9d20654 Andrea Spadaccini
1074 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1075 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1076 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1077 e11a1b77 Adeodato Simo

1078 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1079 90e99856 Adeodato Simo
    according to their default values.
1080 90e99856 Adeodato Simo

1081 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1082 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1083 e11a1b77 Adeodato Simo
    @type ec_id: string
1084 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1085 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1086 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1087 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1088 e11a1b77 Adeodato Simo
                       configuration already
1089 e11a1b77 Adeodato Simo

1090 e11a1b77 Adeodato Simo
    """
1091 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1092 e11a1b77 Adeodato Simo
    self._WriteConfig()
1093 e11a1b77 Adeodato Simo
1094 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1095 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1096 e11a1b77 Adeodato Simo

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

1127 e11a1b77 Adeodato Simo
    @type group_uuid: string
1128 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1129 e11a1b77 Adeodato Simo

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

1146 eaa98a04 Guido Trotter
    @type target: string or None
1147 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1148 eaa98a04 Guido Trotter
    @rtype: string
1149 412b3531 Guido Trotter
    @return: nodegroup UUID
1150 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1151 eaa98a04 Guido Trotter

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

1171 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1172 e85d8982 Stephen Shirley

1173 e85d8982 Stephen Shirley
    @type target: string or None
1174 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1175 e85d8982 Stephen Shirley
    @rtype: string
1176 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1177 e85d8982 Stephen Shirley

1178 e85d8982 Stephen Shirley
    """
1179 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1180 e85d8982 Stephen Shirley
1181 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1182 648e4196 Guido Trotter
    """Lookup a node group.
1183 648e4196 Guido Trotter

1184 648e4196 Guido Trotter
    @type uuid: string
1185 648e4196 Guido Trotter
    @param uuid: group UUID
1186 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1187 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1188 648e4196 Guido Trotter

1189 648e4196 Guido Trotter
    """
1190 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1191 648e4196 Guido Trotter
      return None
1192 648e4196 Guido Trotter
1193 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1194 648e4196 Guido Trotter
1195 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1196 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1197 5768e6a6 René Nussbaumer
    """Lookup a node group.
1198 5768e6a6 René Nussbaumer

1199 5768e6a6 René Nussbaumer
    @type uuid: string
1200 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1201 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1202 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1203 5768e6a6 René Nussbaumer

1204 5768e6a6 René Nussbaumer
    """
1205 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1206 5768e6a6 René Nussbaumer
1207 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1208 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1209 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1210 622444e5 Iustin Pop

1211 622444e5 Iustin Pop
    """
1212 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1213 622444e5 Iustin Pop
1214 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1215 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1216 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1217 1ac6f2ad Guido Trotter

1218 1ac6f2ad Guido Trotter
    """
1219 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1220 1ac6f2ad Guido Trotter
1221 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1222 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1223 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1224 dac81741 Michael Hanselmann

1225 dac81741 Michael Hanselmann
    """
1226 dac81741 Michael Hanselmann
    ngfn = lambda node_name: self._UnlockedGetNodeInfo(node_name).group
1227 dac81741 Michael Hanselmann
    return frozenset(member_name
1228 dac81741 Michael Hanselmann
                     for node_name in nodes
1229 dac81741 Michael Hanselmann
                     for member_name in
1230 dac81741 Michael Hanselmann
                       self._UnlockedGetNodeGroup(ngfn(node_name)).members)
1231 dac81741 Michael Hanselmann
1232 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1233 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1234 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1235 080fbeea Michael Hanselmann

1236 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1237 080fbeea Michael Hanselmann
    @rtype: list
1238 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1239 080fbeea Michael Hanselmann

1240 080fbeea Michael Hanselmann
    """
1241 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1242 080fbeea Michael Hanselmann
1243 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1244 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1245 a8083063 Iustin Pop
    """Add an instance to the config.
1246 a8083063 Iustin Pop

1247 a8083063 Iustin Pop
    This should be used after creating a new instance.
1248 a8083063 Iustin Pop

1249 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1250 c41eea6e Iustin Pop
    @param instance: the instance object
1251 c41eea6e Iustin Pop

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

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

1282 430b923c Iustin Pop
    """
1283 430b923c Iustin Pop
    if not item.uuid:
1284 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1285 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1286 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1287 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1288 430b923c Iustin Pop
1289 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1290 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1291 a8083063 Iustin Pop

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

1310 6a408fb2 Iustin Pop
    """
1311 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_UP)
1312 57de31c0 Agata Murawska
1313 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1314 57de31c0 Agata Murawska
  def MarkInstanceOffline(self, instance_name):
1315 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1316 57de31c0 Agata Murawska

1317 57de31c0 Agata Murawska
    """
1318 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_OFFLINE)
1319 6a408fb2 Iustin Pop
1320 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1321 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1322 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1323 a8083063 Iustin Pop

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

1343 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1344 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1345 fc95f88f Iustin Pop
    rename.
1346 fc95f88f Iustin Pop

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

1377 a8083063 Iustin Pop
    """
1378 2e04d454 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_DOWN)
1379 a8083063 Iustin Pop
1380 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1381 94bbfece Iustin Pop
    """Get the list of instances.
1382 94bbfece Iustin Pop

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

1385 94bbfece Iustin Pop
    """
1386 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1387 94bbfece Iustin Pop
1388 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1389 a8083063 Iustin Pop
  def GetInstanceList(self):
1390 a8083063 Iustin Pop
    """Get the list of instances.
1391 a8083063 Iustin Pop

1392 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1393 c41eea6e Iustin Pop
        'instance1.example.com']
1394 a8083063 Iustin Pop

1395 a8083063 Iustin Pop
    """
1396 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1397 a8083063 Iustin Pop
1398 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1399 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1400 a8083063 Iustin Pop

1401 a8083063 Iustin Pop
    """
1402 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1403 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1404 a8083063 Iustin Pop
1405 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1406 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1407 94bbfece Iustin Pop

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

1410 94bbfece Iustin Pop
    """
1411 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1412 94bbfece Iustin Pop
      return None
1413 94bbfece Iustin Pop
1414 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1415 94bbfece Iustin Pop
1416 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1417 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1418 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1419 a8083063 Iustin Pop

1420 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1421 a8083063 Iustin Pop
    an instance are taken from the live systems.
1422 a8083063 Iustin Pop

1423 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1424 c41eea6e Iustin Pop
        I{instance1.example.com}
1425 a8083063 Iustin Pop

1426 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1427 c41eea6e Iustin Pop
    @return: the instance object
1428 a8083063 Iustin Pop

1429 a8083063 Iustin Pop
    """
1430 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1431 a8083063 Iustin Pop
1432 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1433 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1434 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1435 2674690b Michael Hanselmann

1436 2674690b Michael Hanselmann
    @rtype: frozenset
1437 2674690b Michael Hanselmann

1438 2674690b Michael Hanselmann
    """
1439 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1440 2674690b Michael Hanselmann
    if not instance:
1441 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1442 2674690b Michael Hanselmann
1443 2674690b Michael Hanselmann
    if primary_only:
1444 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1445 2674690b Michael Hanselmann
    else:
1446 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1447 2674690b Michael Hanselmann
1448 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1449 2674690b Michael Hanselmann
                     for node_name in nodes)
1450 2674690b Michael Hanselmann
1451 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1452 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1453 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1454 71333cb9 Iustin Pop

1455 71333cb9 Iustin Pop
    @param instances: list of instance names
1456 71333cb9 Iustin Pop
    @rtype: list
1457 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1458 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1459 71333cb9 Iustin Pop
        node, while keeping the original order
1460 71333cb9 Iustin Pop

1461 71333cb9 Iustin Pop
    """
1462 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1463 71333cb9 Iustin Pop
1464 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1465 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1466 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1467 0b2de758 Iustin Pop

1468 0b2de758 Iustin Pop
    @rtype: dict
1469 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1470 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1471 0b2de758 Iustin Pop

1472 0b2de758 Iustin Pop
    """
1473 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1474 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1475 0b2de758 Iustin Pop
    return my_dict
1476 0b2de758 Iustin Pop
1477 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1478 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1479 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1480 cc19798f Michael Hanselmann

1481 cc19798f Michael Hanselmann
    @type filter_fn: callable
1482 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1483 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1484 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1485 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1486 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1487 cc19798f Michael Hanselmann

1488 cc19798f Michael Hanselmann
    """
1489 cc19798f Michael Hanselmann
    return dict((name, inst)
1490 cc19798f Michael Hanselmann
                for (name, inst) in self._config_data.instances.items()
1491 cc19798f Michael Hanselmann
                if filter_fn(inst))
1492 cc19798f Michael Hanselmann
1493 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1494 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1495 a8083063 Iustin Pop
    """Add a node to the configuration.
1496 a8083063 Iustin Pop

1497 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1498 c41eea6e Iustin Pop
    @param node: a Node instance
1499 a8083063 Iustin Pop

1500 a8083063 Iustin Pop
    """
1501 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1502 d8470559 Michael Hanselmann
1503 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1504 430b923c Iustin Pop
1505 b989e85d Iustin Pop
    node.serial_no = 1
1506 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1507 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1508 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1509 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1510 a8083063 Iustin Pop
    self._WriteConfig()
1511 a8083063 Iustin Pop
1512 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1513 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1514 a8083063 Iustin Pop
    """Remove a node from the configuration.
1515 a8083063 Iustin Pop

1516 a8083063 Iustin Pop
    """
1517 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1518 d8470559 Michael Hanselmann
1519 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1520 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1521 a8083063 Iustin Pop
1522 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1523 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1524 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1525 a8083063 Iustin Pop
    self._WriteConfig()
1526 a8083063 Iustin Pop
1527 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1528 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1529 a8083063 Iustin Pop

1530 a8083063 Iustin Pop
    """
1531 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1532 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1533 a8083063 Iustin Pop
1534 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1535 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1536 a8083063 Iustin Pop

1537 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1538 c41eea6e Iustin Pop
    held.
1539 f78ede4e Guido Trotter

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

1542 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1543 c41eea6e Iustin Pop
    @return: the node object
1544 a8083063 Iustin Pop

1545 a8083063 Iustin Pop
    """
1546 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1547 a8083063 Iustin Pop
      return None
1548 a8083063 Iustin Pop
1549 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1550 a8083063 Iustin Pop
1551 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1552 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1553 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1554 f78ede4e Guido Trotter

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

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

1559 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1560 c41eea6e Iustin Pop
    @return: the node object
1561 f78ede4e Guido Trotter

1562 f78ede4e Guido Trotter
    """
1563 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1564 f78ede4e Guido Trotter
1565 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1566 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1567 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1568 8bf9e9a5 Iustin Pop

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

1571 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1572 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1573 8bf9e9a5 Iustin Pop

1574 8bf9e9a5 Iustin Pop
    """
1575 8bf9e9a5 Iustin Pop
    pri = []
1576 8bf9e9a5 Iustin Pop
    sec = []
1577 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1578 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1579 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1580 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1581 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1582 8bf9e9a5 Iustin Pop
    return (pri, sec)
1583 8bf9e9a5 Iustin Pop
1584 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1585 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1586 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1587 c71b049c Michael Hanselmann

1588 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1589 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1590 c71b049c Michael Hanselmann
    @rtype: frozenset
1591 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1592 c71b049c Michael Hanselmann

1593 c71b049c Michael Hanselmann
    """
1594 c71b049c Michael Hanselmann
    if primary_only:
1595 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1596 c71b049c Michael Hanselmann
    else:
1597 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1598 c71b049c Michael Hanselmann
1599 c71b049c Michael Hanselmann
    return frozenset(inst.name
1600 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1601 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1602 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1603 c71b049c Michael Hanselmann
1604 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1605 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1606 a8083063 Iustin Pop

1607 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1608 c41eea6e Iustin Pop
    held.
1609 c41eea6e Iustin Pop

1610 c41eea6e Iustin Pop
    @rtype: list
1611 f78ede4e Guido Trotter

1612 a8083063 Iustin Pop
    """
1613 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1614 a8083063 Iustin Pop
1615 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1616 f78ede4e Guido Trotter
  def GetNodeList(self):
1617 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1618 f78ede4e Guido Trotter

1619 f78ede4e Guido Trotter
    """
1620 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1621 f78ede4e Guido Trotter
1622 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1623 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1624 94a02bb5 Iustin Pop

1625 94a02bb5 Iustin Pop
    """
1626 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1627 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1628 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1629 94a02bb5 Iustin Pop
1630 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1631 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1632 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1633 6819dc49 Iustin Pop

1634 6819dc49 Iustin Pop
    """
1635 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1636 6819dc49 Iustin Pop
1637 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1638 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1639 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1640 075b62ca Iustin Pop

1641 075b62ca Iustin Pop
    """
1642 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1643 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1644 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1645 075b62ca Iustin Pop
1646 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1647 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1648 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1649 8bf9e9a5 Iustin Pop

1650 8bf9e9a5 Iustin Pop
    """
1651 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1652 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1653 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1654 8bf9e9a5 Iustin Pop
1655 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1656 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1657 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1658 f5eaa3c1 Iustin Pop

1659 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1660 f5eaa3c1 Iustin Pop
    @rtype: list
1661 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1662 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1663 f5eaa3c1 Iustin Pop
        order
1664 f5eaa3c1 Iustin Pop

1665 f5eaa3c1 Iustin Pop
    """
1666 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1667 f5eaa3c1 Iustin Pop
1668 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1669 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1670 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1671 d65e5776 Iustin Pop

1672 d65e5776 Iustin Pop
    @rtype: dict
1673 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1674 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1675 d65e5776 Iustin Pop

1676 d65e5776 Iustin Pop
    """
1677 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1678 ee14d800 Michael Hanselmann
1679 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1680 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1681 ee14d800 Michael Hanselmann

1682 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1683 ee14d800 Michael Hanselmann

1684 ee14d800 Michael Hanselmann
    """
1685 ee14d800 Michael Hanselmann
    return dict([(node, self._UnlockedGetNodeInfo(node))
1686 ee14d800 Michael Hanselmann
                 for node in self._UnlockedGetNodeList()])
1687 d65e5776 Iustin Pop
1688 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1689 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1690 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1691 9d5b1371 Michael Hanselmann

1692 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1693 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1694 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1695 9d5b1371 Michael Hanselmann

1696 9d5b1371 Michael Hanselmann
    """
1697 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1698 9d5b1371 Michael Hanselmann
1699 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1700 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1701 ec0292f1 Iustin Pop

1702 23f06b2b Iustin Pop
    @type exceptions: list
1703 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1704 ec0292f1 Iustin Pop
    @rtype: tuple
1705 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1706 ec0292f1 Iustin Pop

1707 ec0292f1 Iustin Pop
    """
1708 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1709 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1710 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1711 23f06b2b Iustin Pop
        continue
1712 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1713 ec0292f1 Iustin Pop
        mc_max += 1
1714 ec0292f1 Iustin Pop
      if node.master_candidate:
1715 ec0292f1 Iustin Pop
        mc_now += 1
1716 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1717 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1718 ec0292f1 Iustin Pop
1719 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1720 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1721 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1722 ec0292f1 Iustin Pop

1723 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1724 ec0292f1 Iustin Pop

1725 23f06b2b Iustin Pop
    @type exceptions: list
1726 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1727 ec0292f1 Iustin Pop
    @rtype: tuple
1728 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1729 ec0292f1 Iustin Pop

1730 ec0292f1 Iustin Pop
    """
1731 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1732 ec0292f1 Iustin Pop
1733 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1734 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1735 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1736 ec0292f1 Iustin Pop

1737 44485f49 Guido Trotter
    @type exceptions: list
1738 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1739 ec0292f1 Iustin Pop
    @rtype: list
1740 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1741 ec0292f1 Iustin Pop

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

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

1785 190e3cb6 Guido Trotter
    """
1786 f936c153 Iustin Pop
    nodegroup = node.group
1787 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1788 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1789 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1790 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1791 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1792 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1793 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1794 190e3cb6 Guido Trotter
    else:
1795 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1796 190e3cb6 Guido Trotter
1797 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
1798 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
1799 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
1800 54c31fd3 Michael Hanselmann

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

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

1873 a8083063 Iustin Pop
    """
1874 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1875 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1876 a8083063 Iustin Pop
1877 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1878 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1879 76d5d3a3 Iustin Pop

1880 76d5d3a3 Iustin Pop
    """
1881 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1882 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1883 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1884 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1885 76d5d3a3 Iustin Pop
1886 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1887 a8083063 Iustin Pop
    """Read the config data from disk.
1888 a8083063 Iustin Pop

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

1928 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1929 76d5d3a3 Iustin Pop
    whole configuration, etc.
1930 76d5d3a3 Iustin Pop

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

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

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

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

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

2072 054596f0 Iustin Pop
    @rtype: dict
2073 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2074 054596f0 Iustin Pop
        associated value
2075 054596f0 Iustin Pop

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

2143 d367b66c Manuel Franceschini
    """
2144 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2145 d367b66c Manuel Franceschini
2146 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2147 a8083063 Iustin Pop
  def GetVGName(self):
2148 a8083063 Iustin Pop
    """Return the volume group name.
2149 a8083063 Iustin Pop

2150 a8083063 Iustin Pop
    """
2151 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2152 a8083063 Iustin Pop
2153 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2154 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2155 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2156 89ff8e15 Manuel Franceschini

2157 89ff8e15 Manuel Franceschini
    """
2158 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2159 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2160 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2161 89ff8e15 Manuel Franceschini
2162 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2163 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2164 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2165 9e33896b Luca Bigliardi

2166 9e33896b Luca Bigliardi
    """
2167 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2168 9e33896b Luca Bigliardi
2169 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2170 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2171 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2172 9e33896b Luca Bigliardi

2173 9e33896b Luca Bigliardi
    """
2174 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2175 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2176 9e33896b Luca Bigliardi
    self._WriteConfig()
2177 9e33896b Luca Bigliardi
2178 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2179 a8083063 Iustin Pop
  def GetMACPrefix(self):
2180 a8083063 Iustin Pop
    """Return the mac prefix.
2181 a8083063 Iustin Pop

2182 a8083063 Iustin Pop
    """
2183 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2184 62779dd0 Iustin Pop
2185 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2186 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2187 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2188 62779dd0 Iustin Pop

2189 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2190 c41eea6e Iustin Pop
    @return: the cluster object
2191 62779dd0 Iustin Pop

2192 62779dd0 Iustin Pop
    """
2193 62779dd0 Iustin Pop
    return self._config_data.cluster
2194 e00fb268 Iustin Pop
2195 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2196 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2197 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2198 51cb1581 Luca Bigliardi

2199 51cb1581 Luca Bigliardi
    """
2200 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2201 51cb1581 Luca Bigliardi
2202 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2203 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
2204 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2205 e00fb268 Iustin Pop

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

2212 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2213 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2214 c41eea6e Iustin Pop
        the cluster
2215 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2216 c41eea6e Iustin Pop

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

2254 73064714 Guido Trotter
    """
2255 d8aee57e Iustin Pop
    for rm in self._all_rms:
2256 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)