Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 8a147bba

History | View | Annotate | Download (73.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 8a147bba René Nussbaumer
    return self._config_data.cluster.SimpleFillDP(nodegroup.diskparams)
246 8a147bba René Nussbaumer
247 8a147bba René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
248 36b66e6e Guido Trotter
  def GenerateMAC(self, ec_id):
249 a8083063 Iustin Pop
    """Generate a MAC for an instance.
250 a8083063 Iustin Pop

251 a8083063 Iustin Pop
    This should check the current instances for duplicates.
252 a8083063 Iustin Pop

253 a8083063 Iustin Pop
    """
254 36b66e6e Guido Trotter
    existing = self._AllMACs()
255 36b66e6e Guido Trotter
    return self._temporary_ids.Generate(existing, self._GenerateOneMAC, ec_id)
256 a8083063 Iustin Pop
257 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
258 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
259 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
260 1862d460 Alexander Schreiber

261 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
262 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
263 1862d460 Alexander Schreiber

264 1862d460 Alexander Schreiber
    """
265 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
266 36b66e6e Guido Trotter
    if mac in all_macs:
267 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
268 36b66e6e Guido Trotter
    else:
269 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
270 1862d460 Alexander Schreiber
271 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
272 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
273 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
274 d8aee57e Iustin Pop

275 d8aee57e Iustin Pop
    @type lv_name: string
276 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
277 d8aee57e Iustin Pop

278 d8aee57e Iustin Pop
    """
279 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
280 d8aee57e Iustin Pop
    if lv_name in all_lvs:
281 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
282 d8aee57e Iustin Pop
    else:
283 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
284 d8aee57e Iustin Pop
285 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
286 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
287 f9518d38 Iustin Pop
    """Generate a DRBD secret.
288 f9518d38 Iustin Pop

289 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
290 f9518d38 Iustin Pop

291 f9518d38 Iustin Pop
    """
292 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
293 afa1386e Guido Trotter
                                            utils.GenerateSecret,
294 afa1386e Guido Trotter
                                            ec_id)
295 8d9c3bef Michael Hanselmann
296 34e54ebc Iustin Pop
  def _AllLVs(self):
297 923b1523 Iustin Pop
    """Compute the list of all LVs.
298 923b1523 Iustin Pop

299 923b1523 Iustin Pop
    """
300 923b1523 Iustin Pop
    lvnames = set()
301 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
302 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
303 923b1523 Iustin Pop
      for lv_list in node_data.values():
304 923b1523 Iustin Pop
        lvnames.update(lv_list)
305 923b1523 Iustin Pop
    return lvnames
306 923b1523 Iustin Pop
307 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
308 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
309 34e54ebc Iustin Pop

310 34e54ebc Iustin Pop
    @type include_temporary: boolean
311 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
312 34e54ebc Iustin Pop
    @rtype: set
313 34e54ebc Iustin Pop
    @return: a set of IDs
314 34e54ebc Iustin Pop

315 34e54ebc Iustin Pop
    """
316 34e54ebc Iustin Pop
    existing = set()
317 34e54ebc Iustin Pop
    if include_temporary:
318 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
319 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
320 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
321 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
322 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
323 34e54ebc Iustin Pop
    return existing
324 34e54ebc Iustin Pop
325 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
326 430b923c Iustin Pop
    """Generate an unique UUID.
327 923b1523 Iustin Pop

328 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
329 923b1523 Iustin Pop
    duplicates.
330 923b1523 Iustin Pop

331 c41eea6e Iustin Pop
    @rtype: string
332 c41eea6e Iustin Pop
    @return: the unique id
333 923b1523 Iustin Pop

334 923b1523 Iustin Pop
    """
335 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
336 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
337 923b1523 Iustin Pop
338 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
339 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
340 430b923c Iustin Pop
    """Generate an unique ID.
341 430b923c Iustin Pop

342 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
343 430b923c Iustin Pop

344 4fae38c5 Guido Trotter
    @type ec_id: string
345 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
346 34d657ba Iustin Pop

347 34d657ba Iustin Pop
    """
348 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
349 34d657ba Iustin Pop
350 a8083063 Iustin Pop
  def _AllMACs(self):
351 a8083063 Iustin Pop
    """Return all MACs present in the config.
352 a8083063 Iustin Pop

353 c41eea6e Iustin Pop
    @rtype: list
354 c41eea6e Iustin Pop
    @return: the list of all MACs
355 c41eea6e Iustin Pop

356 a8083063 Iustin Pop
    """
357 a8083063 Iustin Pop
    result = []
358 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
359 a8083063 Iustin Pop
      for nic in instance.nics:
360 a8083063 Iustin Pop
        result.append(nic.mac)
361 a8083063 Iustin Pop
362 a8083063 Iustin Pop
    return result
363 a8083063 Iustin Pop
364 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
365 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
366 f9518d38 Iustin Pop

367 c41eea6e Iustin Pop
    @rtype: list
368 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
369 c41eea6e Iustin Pop

370 f9518d38 Iustin Pop
    """
371 f9518d38 Iustin Pop
    def helper(disk, result):
372 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
373 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
374 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
375 f9518d38 Iustin Pop
      if disk.children:
376 f9518d38 Iustin Pop
        for child in disk.children:
377 f9518d38 Iustin Pop
          helper(child, result)
378 f9518d38 Iustin Pop
379 f9518d38 Iustin Pop
    result = []
380 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
381 f9518d38 Iustin Pop
      for disk in instance.disks:
382 f9518d38 Iustin Pop
        helper(disk, result)
383 f9518d38 Iustin Pop
384 f9518d38 Iustin Pop
    return result
385 f9518d38 Iustin Pop
386 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
387 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
388 4b98ac29 Iustin Pop

389 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
390 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
391 4b98ac29 Iustin Pop
    @type l_ids: list
392 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
393 4b98ac29 Iustin Pop
    @type p_ids: list
394 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
395 4b98ac29 Iustin Pop
    @rtype: list
396 4b98ac29 Iustin Pop
    @return: a list of error messages
397 4b98ac29 Iustin Pop

398 4b98ac29 Iustin Pop
    """
399 4b98ac29 Iustin Pop
    result = []
400 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
401 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
402 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
403 25ae22e4 Iustin Pop
      else:
404 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
405 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
406 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
407 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
408 25ae22e4 Iustin Pop
      else:
409 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
410 4b98ac29 Iustin Pop
411 4b98ac29 Iustin Pop
    if disk.children:
412 4b98ac29 Iustin Pop
      for child in disk.children:
413 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
414 4b98ac29 Iustin Pop
    return result
415 4b98ac29 Iustin Pop
416 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
417 a8efbb40 Iustin Pop
    """Verify function.
418 a8efbb40 Iustin Pop

419 4a89c54a Iustin Pop
    @rtype: list
420 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
421 4a89c54a Iustin Pop
        configuration errors
422 4a89c54a Iustin Pop

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

675 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
676 4a89c54a Iustin Pop

677 4a89c54a Iustin Pop
    @rtype: list
678 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
679 4a89c54a Iustin Pop
        configuration errors
680 4a89c54a Iustin Pop

681 4a89c54a Iustin Pop
    """
682 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
683 4a89c54a Iustin Pop
684 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
685 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
686 a8083063 Iustin Pop

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

689 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
690 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
691 a8083063 Iustin Pop
    node.
692 a8083063 Iustin Pop

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

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

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

728 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
729 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
730 f78ede4e Guido Trotter
    node.
731 f78ede4e Guido Trotter

732 f78ede4e Guido Trotter
    """
733 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
734 f78ede4e Guido Trotter
735 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
736 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
737 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
738 b2fddf63 Iustin Pop

739 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
740 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
741 3b3b1bca Dimitris Aragiorgis
        configuration is stable
742 3b3b1bca Dimitris Aragiorgis

743 b2fddf63 Iustin Pop
    """
744 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
745 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
746 264bb3c5 Michael Hanselmann
747 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
748 264bb3c5 Michael Hanselmann
749 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
750 b2fddf63 Iustin Pop
  def GetPortList(self):
751 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
752 264bb3c5 Michael Hanselmann

753 264bb3c5 Michael Hanselmann
    """
754 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
755 264bb3c5 Michael Hanselmann
756 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
757 a8083063 Iustin Pop
  def AllocatePort(self):
758 a8083063 Iustin Pop
    """Allocate a port.
759 a8083063 Iustin Pop

760 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
761 b2fddf63 Iustin Pop
    default port range (and in this case we increase
762 b2fddf63 Iustin Pop
    highest_used_port).
763 a8083063 Iustin Pop

764 a8083063 Iustin Pop
    """
765 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
766 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
767 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
768 264bb3c5 Michael Hanselmann
    else:
769 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
770 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
771 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
772 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
773 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
774 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
775 a8083063 Iustin Pop
776 a8083063 Iustin Pop
    self._WriteConfig()
777 a8083063 Iustin Pop
    return port
778 a8083063 Iustin Pop
779 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
780 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
781 a81c53c9 Iustin Pop

782 4a89c54a Iustin Pop
    @rtype: (dict, list)
783 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
784 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
785 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
786 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
787 4a89c54a Iustin Pop
        should raise an exception
788 a81c53c9 Iustin Pop

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

822 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
823 6d2e83d5 Iustin Pop

824 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
825 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
826 6d2e83d5 Iustin Pop
        an empty list).
827 6d2e83d5 Iustin Pop

828 6d2e83d5 Iustin Pop
    """
829 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
830 4a89c54a Iustin Pop
    if duplicates:
831 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
832 4a89c54a Iustin Pop
                                      str(duplicates))
833 4a89c54a Iustin Pop
    return d_map
834 6d2e83d5 Iustin Pop
835 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
836 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
837 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
838 a81c53c9 Iustin Pop

839 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
840 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
841 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
842 a81c53c9 Iustin Pop
    order as the passed nodes.
843 a81c53c9 Iustin Pop

844 32388e6d Iustin Pop
    @type instance: string
845 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
846 32388e6d Iustin Pop

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

894 a81c53c9 Iustin Pop
    @type instance: string
895 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
896 a81c53c9 Iustin Pop
                     released
897 a81c53c9 Iustin Pop

898 a81c53c9 Iustin Pop
    """
899 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
900 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
901 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
902 a81c53c9 Iustin Pop
      if name == instance:
903 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
904 a81c53c9 Iustin Pop
905 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
906 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
907 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
908 61cf6b5e Iustin Pop

909 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
910 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
911 61cf6b5e Iustin Pop
    functions.
912 61cf6b5e Iustin Pop

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

915 61cf6b5e Iustin Pop
    @type instance: string
916 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
917 61cf6b5e Iustin Pop
                     released
918 61cf6b5e Iustin Pop

919 61cf6b5e Iustin Pop
    """
920 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
921 61cf6b5e Iustin Pop
922 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
923 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
924 4a8b186a Michael Hanselmann
    """Get the configuration version.
925 4a8b186a Michael Hanselmann

926 4a8b186a Michael Hanselmann
    @return: Config version
927 4a8b186a Michael Hanselmann

928 4a8b186a Michael Hanselmann
    """
929 4a8b186a Michael Hanselmann
    return self._config_data.version
930 4a8b186a Michael Hanselmann
931 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
932 4a8b186a Michael Hanselmann
  def GetClusterName(self):
933 4a8b186a Michael Hanselmann
    """Get cluster name.
934 4a8b186a Michael Hanselmann

935 4a8b186a Michael Hanselmann
    @return: Cluster name
936 4a8b186a Michael Hanselmann

937 4a8b186a Michael Hanselmann
    """
938 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
939 4a8b186a Michael Hanselmann
940 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
941 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
942 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
943 4a8b186a Michael Hanselmann

944 4a8b186a Michael Hanselmann
    @return: Master hostname
945 4a8b186a Michael Hanselmann

946 4a8b186a Michael Hanselmann
    """
947 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
948 4a8b186a Michael Hanselmann
949 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
950 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
951 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
952 4a8b186a Michael Hanselmann

953 4a8b186a Michael Hanselmann
    @return: Master IP
954 4a8b186a Michael Hanselmann

955 4a8b186a Michael Hanselmann
    """
956 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
957 4a8b186a Michael Hanselmann
958 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
959 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
960 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
961 4a8b186a Michael Hanselmann

962 4a8b186a Michael Hanselmann
    """
963 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
964 4a8b186a Michael Hanselmann
965 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
966 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
967 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
968 5a8648eb Andrea Spadaccini

969 5a8648eb Andrea Spadaccini
    """
970 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
971 5a8648eb Andrea Spadaccini
972 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
973 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
974 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
975 33be7576 Andrea Spadaccini

976 33be7576 Andrea Spadaccini
    """
977 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
978 33be7576 Andrea Spadaccini
979 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
980 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
981 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
982 4a8b186a Michael Hanselmann

983 4a8b186a Michael Hanselmann
    """
984 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
985 4a8b186a Michael Hanselmann
986 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
987 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
988 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
989 4b97f902 Apollon Oikonomopoulos

990 4b97f902 Apollon Oikonomopoulos
    """
991 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
992 4b97f902 Apollon Oikonomopoulos
993 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
994 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
995 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
996 4a8b186a Michael Hanselmann

997 4a8b186a Michael Hanselmann
    """
998 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
999 4a8b186a Michael Hanselmann
1000 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1001 a8083063 Iustin Pop
  def GetHostKey(self):
1002 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1003 a8083063 Iustin Pop

1004 c41eea6e Iustin Pop
    @rtype: string
1005 c41eea6e Iustin Pop
    @return: the rsa hostkey
1006 a8083063 Iustin Pop

1007 a8083063 Iustin Pop
    """
1008 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1009 a8083063 Iustin Pop
1010 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1011 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1012 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1013 bf4af505 Apollon Oikonomopoulos

1014 bf4af505 Apollon Oikonomopoulos
    """
1015 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1016 bf4af505 Apollon Oikonomopoulos
1017 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1018 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1019 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1020 868a98ca Manuel Franceschini

1021 868a98ca Manuel Franceschini
    @return: primary ip family
1022 868a98ca Manuel Franceschini

1023 868a98ca Manuel Franceschini
    """
1024 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1025 868a98ca Manuel Franceschini
1026 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1027 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1028 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1029 c9f4b8e6 Andrea Spadaccini

1030 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1031 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1032 c9f4b8e6 Andrea Spadaccini

1033 c9f4b8e6 Andrea Spadaccini
    """
1034 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1035 c79198a0 Andrea Spadaccini
    result = objects.MasterNetworkParameters(name=cluster.master_node,
1036 c79198a0 Andrea Spadaccini
      ip=cluster.master_ip,
1037 c79198a0 Andrea Spadaccini
      netmask=cluster.master_netmask,
1038 c79198a0 Andrea Spadaccini
      netdev=cluster.master_netdev,
1039 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1040 c9f4b8e6 Andrea Spadaccini
1041 f9d20654 Andrea Spadaccini
    return result
1042 f9d20654 Andrea Spadaccini
1043 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1044 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1045 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1046 e11a1b77 Adeodato Simo

1047 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1048 90e99856 Adeodato Simo
    according to their default values.
1049 90e99856 Adeodato Simo

1050 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1051 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1052 e11a1b77 Adeodato Simo
    @type ec_id: string
1053 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1054 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1055 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1056 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1057 e11a1b77 Adeodato Simo
                       configuration already
1058 e11a1b77 Adeodato Simo

1059 e11a1b77 Adeodato Simo
    """
1060 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1061 e11a1b77 Adeodato Simo
    self._WriteConfig()
1062 e11a1b77 Adeodato Simo
1063 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1064 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1065 e11a1b77 Adeodato Simo

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

1096 e11a1b77 Adeodato Simo
    @type group_uuid: string
1097 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1098 e11a1b77 Adeodato Simo

1099 e11a1b77 Adeodato Simo
    """
1100 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1101 e11a1b77 Adeodato Simo
1102 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1103 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1104 e11a1b77 Adeodato Simo
1105 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1106 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1107 0389c42a Stephen Shirley
1108 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1109 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1110 e11a1b77 Adeodato Simo
    self._WriteConfig()
1111 e11a1b77 Adeodato Simo
1112 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1113 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1114 eaa98a04 Guido Trotter

1115 eaa98a04 Guido Trotter
    @type target: string or None
1116 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1117 eaa98a04 Guido Trotter
    @rtype: string
1118 412b3531 Guido Trotter
    @return: nodegroup UUID
1119 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1120 eaa98a04 Guido Trotter

1121 eaa98a04 Guido Trotter
    """
1122 eaa98a04 Guido Trotter
    if target is None:
1123 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1124 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1125 eaa98a04 Guido Trotter
                                   " group must be specified explicitely.")
1126 eaa98a04 Guido Trotter
      else:
1127 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1128 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1129 eaa98a04 Guido Trotter
      return target
1130 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1131 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1132 eaa98a04 Guido Trotter
        return nodegroup.uuid
1133 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1134 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1135 eaa98a04 Guido Trotter
1136 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1137 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1138 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1139 e85d8982 Stephen Shirley

1140 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1141 e85d8982 Stephen Shirley

1142 e85d8982 Stephen Shirley
    @type target: string or None
1143 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1144 e85d8982 Stephen Shirley
    @rtype: string
1145 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1146 e85d8982 Stephen Shirley

1147 e85d8982 Stephen Shirley
    """
1148 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1149 e85d8982 Stephen Shirley
1150 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1151 648e4196 Guido Trotter
    """Lookup a node group.
1152 648e4196 Guido Trotter

1153 648e4196 Guido Trotter
    @type uuid: string
1154 648e4196 Guido Trotter
    @param uuid: group UUID
1155 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1156 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1157 648e4196 Guido Trotter

1158 648e4196 Guido Trotter
    """
1159 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1160 648e4196 Guido Trotter
      return None
1161 648e4196 Guido Trotter
1162 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1163 648e4196 Guido Trotter
1164 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1165 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1166 5768e6a6 René Nussbaumer
    """Lookup a node group.
1167 5768e6a6 René Nussbaumer

1168 5768e6a6 René Nussbaumer
    @type uuid: string
1169 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1170 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1171 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1172 5768e6a6 René Nussbaumer

1173 5768e6a6 René Nussbaumer
    """
1174 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1175 5768e6a6 René Nussbaumer
1176 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1177 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1178 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1179 622444e5 Iustin Pop

1180 622444e5 Iustin Pop
    """
1181 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1182 622444e5 Iustin Pop
1183 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1184 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1185 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1186 1ac6f2ad Guido Trotter

1187 1ac6f2ad Guido Trotter
    """
1188 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1189 1ac6f2ad Guido Trotter
1190 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1191 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1192 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1193 dac81741 Michael Hanselmann

1194 dac81741 Michael Hanselmann
    """
1195 dac81741 Michael Hanselmann
    ngfn = lambda node_name: self._UnlockedGetNodeInfo(node_name).group
1196 dac81741 Michael Hanselmann
    return frozenset(member_name
1197 dac81741 Michael Hanselmann
                     for node_name in nodes
1198 dac81741 Michael Hanselmann
                     for member_name in
1199 dac81741 Michael Hanselmann
                       self._UnlockedGetNodeGroup(ngfn(node_name)).members)
1200 dac81741 Michael Hanselmann
1201 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1202 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1203 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1204 080fbeea Michael Hanselmann

1205 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1206 080fbeea Michael Hanselmann
    @rtype: list
1207 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1208 080fbeea Michael Hanselmann

1209 080fbeea Michael Hanselmann
    """
1210 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1211 080fbeea Michael Hanselmann
1212 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1213 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1214 a8083063 Iustin Pop
    """Add an instance to the config.
1215 a8083063 Iustin Pop

1216 a8083063 Iustin Pop
    This should be used after creating a new instance.
1217 a8083063 Iustin Pop

1218 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1219 c41eea6e Iustin Pop
    @param instance: the instance object
1220 c41eea6e Iustin Pop

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

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

1251 430b923c Iustin Pop
    """
1252 430b923c Iustin Pop
    if not item.uuid:
1253 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1254 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1255 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1256 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1257 430b923c Iustin Pop
1258 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1259 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1260 a8083063 Iustin Pop

1261 a8083063 Iustin Pop
    """
1262 2e04d454 Agata Murawska
    assert status in constants.ADMINST_ALL, \
1263 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1264 a8083063 Iustin Pop
1265 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1266 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1267 3ecf6786 Iustin Pop
                                      instance_name)
1268 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1269 9ca8a7c5 Agata Murawska
    if instance.admin_state != status:
1270 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1271 b989e85d Iustin Pop
      instance.serial_no += 1
1272 d693c864 Iustin Pop
      instance.mtime = time.time()
1273 455a3445 Iustin Pop
      self._WriteConfig()
1274 a8083063 Iustin Pop
1275 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1276 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1277 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1278 6a408fb2 Iustin Pop

1279 6a408fb2 Iustin Pop
    """
1280 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_UP)
1281 57de31c0 Agata Murawska
1282 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1283 57de31c0 Agata Murawska
  def MarkInstanceOffline(self, instance_name):
1284 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1285 57de31c0 Agata Murawska

1286 57de31c0 Agata Murawska
    """
1287 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_OFFLINE)
1288 6a408fb2 Iustin Pop
1289 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1290 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1291 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1292 a8083063 Iustin Pop

1293 a8083063 Iustin Pop
    """
1294 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1295 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1296 f396ad8c Vangelis Koukis
1297 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1298 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1299 f396ad8c Vangelis Koukis
    inst = self._config_data.instances[instance_name]
1300 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1301 f396ad8c Vangelis Koukis
    if network_port is not None:
1302 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1303 f396ad8c Vangelis Koukis
1304 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1305 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1306 a8083063 Iustin Pop
    self._WriteConfig()
1307 a8083063 Iustin Pop
1308 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1309 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1310 fc95f88f Iustin Pop
    """Rename an instance.
1311 fc95f88f Iustin Pop

1312 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1313 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1314 fc95f88f Iustin Pop
    rename.
1315 fc95f88f Iustin Pop

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

1346 a8083063 Iustin Pop
    """
1347 2e04d454 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_DOWN)
1348 a8083063 Iustin Pop
1349 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1350 94bbfece Iustin Pop
    """Get the list of instances.
1351 94bbfece Iustin Pop

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

1354 94bbfece Iustin Pop
    """
1355 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1356 94bbfece Iustin Pop
1357 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1358 a8083063 Iustin Pop
  def GetInstanceList(self):
1359 a8083063 Iustin Pop
    """Get the list of instances.
1360 a8083063 Iustin Pop

1361 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1362 c41eea6e Iustin Pop
        'instance1.example.com']
1363 a8083063 Iustin Pop

1364 a8083063 Iustin Pop
    """
1365 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1366 a8083063 Iustin Pop
1367 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1368 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1369 a8083063 Iustin Pop

1370 a8083063 Iustin Pop
    """
1371 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1372 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1373 a8083063 Iustin Pop
1374 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1375 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1376 94bbfece Iustin Pop

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

1379 94bbfece Iustin Pop
    """
1380 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1381 94bbfece Iustin Pop
      return None
1382 94bbfece Iustin Pop
1383 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1384 94bbfece Iustin Pop
1385 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1386 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1387 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1388 a8083063 Iustin Pop

1389 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1390 a8083063 Iustin Pop
    an instance are taken from the live systems.
1391 a8083063 Iustin Pop

1392 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1393 c41eea6e Iustin Pop
        I{instance1.example.com}
1394 a8083063 Iustin Pop

1395 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1396 c41eea6e Iustin Pop
    @return: the instance object
1397 a8083063 Iustin Pop

1398 a8083063 Iustin Pop
    """
1399 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1400 a8083063 Iustin Pop
1401 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1402 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1403 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1404 2674690b Michael Hanselmann

1405 2674690b Michael Hanselmann
    @rtype: frozenset
1406 2674690b Michael Hanselmann

1407 2674690b Michael Hanselmann
    """
1408 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1409 2674690b Michael Hanselmann
    if not instance:
1410 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1411 2674690b Michael Hanselmann
1412 2674690b Michael Hanselmann
    if primary_only:
1413 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1414 2674690b Michael Hanselmann
    else:
1415 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1416 2674690b Michael Hanselmann
1417 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1418 2674690b Michael Hanselmann
                     for node_name in nodes)
1419 2674690b Michael Hanselmann
1420 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1421 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1422 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1423 71333cb9 Iustin Pop

1424 71333cb9 Iustin Pop
    @param instances: list of instance names
1425 71333cb9 Iustin Pop
    @rtype: list
1426 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1427 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1428 71333cb9 Iustin Pop
        node, while keeping the original order
1429 71333cb9 Iustin Pop

1430 71333cb9 Iustin Pop
    """
1431 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1432 71333cb9 Iustin Pop
1433 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1434 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1435 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1436 0b2de758 Iustin Pop

1437 0b2de758 Iustin Pop
    @rtype: dict
1438 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1439 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1440 0b2de758 Iustin Pop

1441 0b2de758 Iustin Pop
    """
1442 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1443 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1444 0b2de758 Iustin Pop
    return my_dict
1445 0b2de758 Iustin Pop
1446 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1447 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1448 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1449 cc19798f Michael Hanselmann

1450 cc19798f Michael Hanselmann
    @type filter_fn: callable
1451 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1452 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1453 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1454 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1455 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1456 cc19798f Michael Hanselmann

1457 cc19798f Michael Hanselmann
    """
1458 cc19798f Michael Hanselmann
    return dict((name, inst)
1459 cc19798f Michael Hanselmann
                for (name, inst) in self._config_data.instances.items()
1460 cc19798f Michael Hanselmann
                if filter_fn(inst))
1461 cc19798f Michael Hanselmann
1462 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1463 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1464 a8083063 Iustin Pop
    """Add a node to the configuration.
1465 a8083063 Iustin Pop

1466 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1467 c41eea6e Iustin Pop
    @param node: a Node instance
1468 a8083063 Iustin Pop

1469 a8083063 Iustin Pop
    """
1470 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1471 d8470559 Michael Hanselmann
1472 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1473 430b923c Iustin Pop
1474 b989e85d Iustin Pop
    node.serial_no = 1
1475 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1476 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1477 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1478 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1479 a8083063 Iustin Pop
    self._WriteConfig()
1480 a8083063 Iustin Pop
1481 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1482 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1483 a8083063 Iustin Pop
    """Remove a node from the configuration.
1484 a8083063 Iustin Pop

1485 a8083063 Iustin Pop
    """
1486 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1487 d8470559 Michael Hanselmann
1488 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1489 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1490 a8083063 Iustin Pop
1491 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1492 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1493 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1494 a8083063 Iustin Pop
    self._WriteConfig()
1495 a8083063 Iustin Pop
1496 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1497 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1498 a8083063 Iustin Pop

1499 a8083063 Iustin Pop
    """
1500 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1501 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1502 a8083063 Iustin Pop
1503 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1504 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1505 a8083063 Iustin Pop

1506 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1507 c41eea6e Iustin Pop
    held.
1508 f78ede4e Guido Trotter

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

1511 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1512 c41eea6e Iustin Pop
    @return: the node object
1513 a8083063 Iustin Pop

1514 a8083063 Iustin Pop
    """
1515 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1516 a8083063 Iustin Pop
      return None
1517 a8083063 Iustin Pop
1518 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1519 a8083063 Iustin Pop
1520 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1521 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1522 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1523 f78ede4e Guido Trotter

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

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

1528 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1529 c41eea6e Iustin Pop
    @return: the node object
1530 f78ede4e Guido Trotter

1531 f78ede4e Guido Trotter
    """
1532 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1533 f78ede4e Guido Trotter
1534 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1535 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1536 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1537 8bf9e9a5 Iustin Pop

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

1540 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1541 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1542 8bf9e9a5 Iustin Pop

1543 8bf9e9a5 Iustin Pop
    """
1544 8bf9e9a5 Iustin Pop
    pri = []
1545 8bf9e9a5 Iustin Pop
    sec = []
1546 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1547 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1548 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1549 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1550 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1551 8bf9e9a5 Iustin Pop
    return (pri, sec)
1552 8bf9e9a5 Iustin Pop
1553 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1554 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1555 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1556 c71b049c Michael Hanselmann

1557 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1558 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1559 c71b049c Michael Hanselmann
    @rtype: frozenset
1560 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1561 c71b049c Michael Hanselmann

1562 c71b049c Michael Hanselmann
    """
1563 c71b049c Michael Hanselmann
    if primary_only:
1564 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1565 c71b049c Michael Hanselmann
    else:
1566 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1567 c71b049c Michael Hanselmann
1568 c71b049c Michael Hanselmann
    return frozenset(inst.name
1569 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1570 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1571 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1572 c71b049c Michael Hanselmann
1573 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1574 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1575 a8083063 Iustin Pop

1576 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1577 c41eea6e Iustin Pop
    held.
1578 c41eea6e Iustin Pop

1579 c41eea6e Iustin Pop
    @rtype: list
1580 f78ede4e Guido Trotter

1581 a8083063 Iustin Pop
    """
1582 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1583 a8083063 Iustin Pop
1584 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1585 f78ede4e Guido Trotter
  def GetNodeList(self):
1586 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1587 f78ede4e Guido Trotter

1588 f78ede4e Guido Trotter
    """
1589 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1590 f78ede4e Guido Trotter
1591 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1592 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1593 94a02bb5 Iustin Pop

1594 94a02bb5 Iustin Pop
    """
1595 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1596 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1597 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1598 94a02bb5 Iustin Pop
1599 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1600 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1601 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1602 6819dc49 Iustin Pop

1603 6819dc49 Iustin Pop
    """
1604 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1605 6819dc49 Iustin Pop
1606 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1607 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1608 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1609 075b62ca Iustin Pop

1610 075b62ca Iustin Pop
    """
1611 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1612 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1613 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1614 075b62ca Iustin Pop
1615 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1616 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1617 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1618 8bf9e9a5 Iustin Pop

1619 8bf9e9a5 Iustin Pop
    """
1620 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1621 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1622 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1623 8bf9e9a5 Iustin Pop
1624 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1625 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1626 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1627 f5eaa3c1 Iustin Pop

1628 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1629 f5eaa3c1 Iustin Pop
    @rtype: list
1630 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1631 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1632 f5eaa3c1 Iustin Pop
        order
1633 f5eaa3c1 Iustin Pop

1634 f5eaa3c1 Iustin Pop
    """
1635 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1636 f5eaa3c1 Iustin Pop
1637 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1638 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1639 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1640 d65e5776 Iustin Pop

1641 d65e5776 Iustin Pop
    @rtype: dict
1642 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1643 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1644 d65e5776 Iustin Pop

1645 d65e5776 Iustin Pop
    """
1646 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1647 ee14d800 Michael Hanselmann
1648 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1649 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1650 ee14d800 Michael Hanselmann

1651 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1652 ee14d800 Michael Hanselmann

1653 ee14d800 Michael Hanselmann
    """
1654 ee14d800 Michael Hanselmann
    return dict([(node, self._UnlockedGetNodeInfo(node))
1655 ee14d800 Michael Hanselmann
                 for node in self._UnlockedGetNodeList()])
1656 d65e5776 Iustin Pop
1657 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1658 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1659 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1660 9d5b1371 Michael Hanselmann

1661 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1662 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1663 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1664 9d5b1371 Michael Hanselmann

1665 9d5b1371 Michael Hanselmann
    """
1666 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1667 9d5b1371 Michael Hanselmann
1668 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1669 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1670 ec0292f1 Iustin Pop

1671 23f06b2b Iustin Pop
    @type exceptions: list
1672 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1673 ec0292f1 Iustin Pop
    @rtype: tuple
1674 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1675 ec0292f1 Iustin Pop

1676 ec0292f1 Iustin Pop
    """
1677 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1678 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1679 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1680 23f06b2b Iustin Pop
        continue
1681 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1682 ec0292f1 Iustin Pop
        mc_max += 1
1683 ec0292f1 Iustin Pop
      if node.master_candidate:
1684 ec0292f1 Iustin Pop
        mc_now += 1
1685 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1686 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1687 ec0292f1 Iustin Pop
1688 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1689 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1690 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1691 ec0292f1 Iustin Pop

1692 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1693 ec0292f1 Iustin Pop

1694 23f06b2b Iustin Pop
    @type exceptions: list
1695 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1696 ec0292f1 Iustin Pop
    @rtype: tuple
1697 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1698 ec0292f1 Iustin Pop

1699 ec0292f1 Iustin Pop
    """
1700 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1701 ec0292f1 Iustin Pop
1702 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1703 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1704 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1705 ec0292f1 Iustin Pop

1706 44485f49 Guido Trotter
    @type exceptions: list
1707 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1708 ec0292f1 Iustin Pop
    @rtype: list
1709 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1710 ec0292f1 Iustin Pop

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

1741 190e3cb6 Guido Trotter
    """
1742 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1743 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1744 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1745 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1746 190e3cb6 Guido Trotter
      # is not found anymore.
1747 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1748 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1749 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1750 190e3cb6 Guido Trotter
1751 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1752 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1753 190e3cb6 Guido Trotter

1754 190e3cb6 Guido Trotter
    """
1755 f936c153 Iustin Pop
    nodegroup = node.group
1756 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1757 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1758 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1759 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1760 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1761 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1762 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1763 190e3cb6 Guido Trotter
    else:
1764 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1765 190e3cb6 Guido Trotter
1766 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
1767 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
1768 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
1769 54c31fd3 Michael Hanselmann

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

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

1842 a8083063 Iustin Pop
    """
1843 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1844 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1845 a8083063 Iustin Pop
1846 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1847 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1848 76d5d3a3 Iustin Pop

1849 76d5d3a3 Iustin Pop
    """
1850 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1851 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1852 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
1853 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1854 76d5d3a3 Iustin Pop
1855 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
1856 a8083063 Iustin Pop
    """Read the config data from disk.
1857 a8083063 Iustin Pop

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

1897 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1898 76d5d3a3 Iustin Pop
    whole configuration, etc.
1899 76d5d3a3 Iustin Pop

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

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

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

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

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

2041 054596f0 Iustin Pop
    @rtype: dict
2042 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2043 054596f0 Iustin Pop
        associated value
2044 054596f0 Iustin Pop

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

2112 d367b66c Manuel Franceschini
    """
2113 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2114 d367b66c Manuel Franceschini
2115 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2116 a8083063 Iustin Pop
  def GetVGName(self):
2117 a8083063 Iustin Pop
    """Return the volume group name.
2118 a8083063 Iustin Pop

2119 a8083063 Iustin Pop
    """
2120 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2121 a8083063 Iustin Pop
2122 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2123 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2124 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2125 89ff8e15 Manuel Franceschini

2126 89ff8e15 Manuel Franceschini
    """
2127 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2128 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2129 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2130 89ff8e15 Manuel Franceschini
2131 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2132 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2133 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2134 9e33896b Luca Bigliardi

2135 9e33896b Luca Bigliardi
    """
2136 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2137 9e33896b Luca Bigliardi
2138 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2139 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2140 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2141 9e33896b Luca Bigliardi

2142 9e33896b Luca Bigliardi
    """
2143 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2144 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2145 9e33896b Luca Bigliardi
    self._WriteConfig()
2146 9e33896b Luca Bigliardi
2147 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2148 a8083063 Iustin Pop
  def GetMACPrefix(self):
2149 a8083063 Iustin Pop
    """Return the mac prefix.
2150 a8083063 Iustin Pop

2151 a8083063 Iustin Pop
    """
2152 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2153 62779dd0 Iustin Pop
2154 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2155 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2156 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2157 62779dd0 Iustin Pop

2158 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2159 c41eea6e Iustin Pop
    @return: the cluster object
2160 62779dd0 Iustin Pop

2161 62779dd0 Iustin Pop
    """
2162 62779dd0 Iustin Pop
    return self._config_data.cluster
2163 e00fb268 Iustin Pop
2164 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2165 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2166 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2167 51cb1581 Luca Bigliardi

2168 51cb1581 Luca Bigliardi
    """
2169 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2170 51cb1581 Luca Bigliardi
2171 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2172 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
2173 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2174 e00fb268 Iustin Pop

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

2181 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2182 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2183 c41eea6e Iustin Pop
        the cluster
2184 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2185 c41eea6e Iustin Pop

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

2223 73064714 Guido Trotter
    """
2224 d8aee57e Iustin Pop
    for rm in self._all_rms:
2225 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)