Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ afa1386e

History | View | Annotate | Download (45.7 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 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 a8083063 Iustin Pop
import os
35 a8083063 Iustin Pop
import random
36 d8470559 Michael Hanselmann
import logging
37 d693c864 Iustin Pop
import time
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
from ganeti import errors
40 f78ede4e Guido Trotter
from ganeti import locking
41 a8083063 Iustin Pop
from ganeti import utils
42 a8083063 Iustin Pop
from ganeti import constants
43 a8083063 Iustin Pop
from ganeti import rpc
44 a8083063 Iustin Pop
from ganeti import objects
45 8d14b30d Iustin Pop
from ganeti import serializer
46 243cdbcc Michael Hanselmann
47 243cdbcc Michael Hanselmann
48 f78ede4e Guido Trotter
_config_lock = locking.SharedLock()
49 f78ede4e Guido Trotter
50 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
51 4fae38c5 Guido Trotter
_UPGRADE_CONFIG_JID="jid-cfg-upgrade"
52 4fae38c5 Guido Trotter
53 f78ede4e Guido Trotter
54 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
55 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
56 c41eea6e Iustin Pop

57 c41eea6e Iustin Pop
  This only verifies the version of the configuration.
58 c41eea6e Iustin Pop

59 c41eea6e Iustin Pop
  @raise errors.ConfigurationError: if the version differs from what
60 c41eea6e Iustin Pop
      we expect
61 c41eea6e Iustin Pop

62 c41eea6e Iustin Pop
  """
63 5b263ed7 Michael Hanselmann
  if data.version != constants.CONFIG_VERSION:
64 243cdbcc Michael Hanselmann
    raise errors.ConfigurationError("Cluster configuration version"
65 243cdbcc Michael Hanselmann
                                    " mismatch, got %s instead of %s" %
66 5b263ed7 Michael Hanselmann
                                    (data.version,
67 243cdbcc Michael Hanselmann
                                     constants.CONFIG_VERSION))
68 a8083063 Iustin Pop
69 319856a9 Michael Hanselmann
70 013da361 Guido Trotter
class TemporaryReservationManager:
71 013da361 Guido Trotter
  """A temporary resource reservation manager.
72 013da361 Guido Trotter

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

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

108 013da361 Guido Trotter
    """
109 013da361 Guido Trotter
    assert callable(generate_one_fn)
110 013da361 Guido Trotter
111 013da361 Guido Trotter
    all_elems = self.GetReserved()
112 013da361 Guido Trotter
    all_elems.update(existing)
113 013da361 Guido Trotter
    retries = 64
114 013da361 Guido Trotter
    while retries > 0:
115 013da361 Guido Trotter
      new_resource = generate_one_fn()
116 013da361 Guido Trotter
      if new_resource is not None and new_resource not in all_elems:
117 013da361 Guido Trotter
        break
118 013da361 Guido Trotter
    else:
119 013da361 Guido Trotter
      raise errors.ConfigurationError("Not able generate new resource"
120 013da361 Guido Trotter
                                      " (last tried: %s)" % new_resource)
121 013da361 Guido Trotter
    self.Reserve(ec_id, new_resource)
122 013da361 Guido Trotter
    return new_resource
123 013da361 Guido Trotter
124 013da361 Guido Trotter
125 a8083063 Iustin Pop
class ConfigWriter:
126 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
127 a8083063 Iustin Pop

128 098c0958 Michael Hanselmann
  """
129 a8083063 Iustin Pop
  def __init__(self, cfg_file=None, offline=False):
130 14e15659 Iustin Pop
    self.write_count = 0
131 f78ede4e Guido Trotter
    self._lock = _config_lock
132 a8083063 Iustin Pop
    self._config_data = None
133 a8083063 Iustin Pop
    self._offline = offline
134 a8083063 Iustin Pop
    if cfg_file is None:
135 a8083063 Iustin Pop
      self._cfg_file = constants.CLUSTER_CONF_FILE
136 a8083063 Iustin Pop
    else:
137 a8083063 Iustin Pop
      self._cfg_file = cfg_file
138 4fae38c5 Guido Trotter
    self._temporary_ids = TemporaryReservationManager()
139 a81c53c9 Iustin Pop
    self._temporary_drbds = {}
140 36b66e6e Guido Trotter
    self._temporary_macs = TemporaryReservationManager()
141 afa1386e Guido Trotter
    self._temporary_secrets = TemporaryReservationManager()
142 89e1fc26 Iustin Pop
    # Note: in order to prevent errors when resolving our name in
143 89e1fc26 Iustin Pop
    # _DistributeConfig, we compute it here once and reuse it; it's
144 89e1fc26 Iustin Pop
    # better to raise an error before starting to modify the config
145 89e1fc26 Iustin Pop
    # file than after it was modified
146 89e1fc26 Iustin Pop
    self._my_hostname = utils.HostInfo().name
147 3c7f6c44 Iustin Pop
    self._last_cluster_serial = -1
148 3d3a04bc Iustin Pop
    self._OpenConfig()
149 a8083063 Iustin Pop
150 a8083063 Iustin Pop
  # this method needs to be static, so that we can call it on the class
151 a8083063 Iustin Pop
  @staticmethod
152 a8083063 Iustin Pop
  def IsCluster():
153 a8083063 Iustin Pop
    """Check if the cluster is configured.
154 a8083063 Iustin Pop

155 a8083063 Iustin Pop
    """
156 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
157 a8083063 Iustin Pop
158 36b66e6e Guido Trotter
  def _GenerateOneMAC(self):
159 36b66e6e Guido Trotter
    """Generate one mac address
160 36b66e6e Guido Trotter

161 36b66e6e Guido Trotter
    """
162 36b66e6e Guido Trotter
    prefix = self._config_data.cluster.mac_prefix
163 36b66e6e Guido Trotter
    byte1 = random.randrange(0, 256)
164 36b66e6e Guido Trotter
    byte2 = random.randrange(0, 256)
165 36b66e6e Guido Trotter
    byte3 = random.randrange(0, 256)
166 36b66e6e Guido Trotter
    mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
167 36b66e6e Guido Trotter
    return mac
168 36b66e6e Guido Trotter
169 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
170 36b66e6e Guido Trotter
  def GenerateMAC(self, ec_id):
171 a8083063 Iustin Pop
    """Generate a MAC for an instance.
172 a8083063 Iustin Pop

173 a8083063 Iustin Pop
    This should check the current instances for duplicates.
174 a8083063 Iustin Pop

175 a8083063 Iustin Pop
    """
176 36b66e6e Guido Trotter
    existing = self._AllMACs()
177 36b66e6e Guido Trotter
    return self._temporary_ids.Generate(existing, self._GenerateOneMAC, ec_id)
178 a8083063 Iustin Pop
179 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
180 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
181 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
182 1862d460 Alexander Schreiber

183 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
184 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
185 1862d460 Alexander Schreiber

186 1862d460 Alexander Schreiber
    """
187 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
188 36b66e6e Guido Trotter
    if mac in all_macs:
189 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
190 36b66e6e Guido Trotter
    else:
191 36b66e6e Guido Trotter
      self._temporary_macs.Reserve(mac, ec_id)
192 1862d460 Alexander Schreiber
193 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
194 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
195 f9518d38 Iustin Pop
    """Generate a DRBD secret.
196 f9518d38 Iustin Pop

197 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
198 f9518d38 Iustin Pop

199 f9518d38 Iustin Pop
    """
200 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
201 afa1386e Guido Trotter
                                            utils.GenerateSecret,
202 afa1386e Guido Trotter
                                            ec_id)
203 34e54ebc Iustin Pop
  def _AllLVs(self):
204 923b1523 Iustin Pop
    """Compute the list of all LVs.
205 923b1523 Iustin Pop

206 923b1523 Iustin Pop
    """
207 923b1523 Iustin Pop
    lvnames = set()
208 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
209 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
210 923b1523 Iustin Pop
      for lv_list in node_data.values():
211 923b1523 Iustin Pop
        lvnames.update(lv_list)
212 923b1523 Iustin Pop
    return lvnames
213 923b1523 Iustin Pop
214 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
215 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
216 34e54ebc Iustin Pop

217 34e54ebc Iustin Pop
    @type include_temporary: boolean
218 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
219 34e54ebc Iustin Pop
    @rtype: set
220 34e54ebc Iustin Pop
    @return: a set of IDs
221 34e54ebc Iustin Pop

222 34e54ebc Iustin Pop
    """
223 34e54ebc Iustin Pop
    existing = set()
224 34e54ebc Iustin Pop
    if include_temporary:
225 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
226 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
227 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
228 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
229 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
230 34e54ebc Iustin Pop
    return existing
231 34e54ebc Iustin Pop
232 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
233 430b923c Iustin Pop
    """Generate an unique UUID.
234 923b1523 Iustin Pop

235 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
236 923b1523 Iustin Pop
    duplicates.
237 923b1523 Iustin Pop

238 c41eea6e Iustin Pop
    @rtype: string
239 c41eea6e Iustin Pop
    @return: the unique id
240 923b1523 Iustin Pop

241 923b1523 Iustin Pop
    """
242 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
243 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
244 923b1523 Iustin Pop
245 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
246 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
247 430b923c Iustin Pop
    """Generate an unique ID.
248 430b923c Iustin Pop

249 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
250 430b923c Iustin Pop

251 4fae38c5 Guido Trotter
    @type ec_id: string
252 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
253 34d657ba Iustin Pop

254 34d657ba Iustin Pop
    """
255 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
256 34d657ba Iustin Pop
257 a8083063 Iustin Pop
  def _AllMACs(self):
258 a8083063 Iustin Pop
    """Return all MACs present in the config.
259 a8083063 Iustin Pop

260 c41eea6e Iustin Pop
    @rtype: list
261 c41eea6e Iustin Pop
    @return: the list of all MACs
262 c41eea6e Iustin Pop

263 a8083063 Iustin Pop
    """
264 a8083063 Iustin Pop
    result = []
265 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
266 a8083063 Iustin Pop
      for nic in instance.nics:
267 a8083063 Iustin Pop
        result.append(nic.mac)
268 a8083063 Iustin Pop
269 a8083063 Iustin Pop
    return result
270 a8083063 Iustin Pop
271 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
272 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
273 f9518d38 Iustin Pop

274 c41eea6e Iustin Pop
    @rtype: list
275 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
276 c41eea6e Iustin Pop

277 f9518d38 Iustin Pop
    """
278 f9518d38 Iustin Pop
    def helper(disk, result):
279 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
280 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
281 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
282 f9518d38 Iustin Pop
      if disk.children:
283 f9518d38 Iustin Pop
        for child in disk.children:
284 f9518d38 Iustin Pop
          helper(child, result)
285 f9518d38 Iustin Pop
286 f9518d38 Iustin Pop
    result = []
287 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
288 f9518d38 Iustin Pop
      for disk in instance.disks:
289 f9518d38 Iustin Pop
        helper(disk, result)
290 f9518d38 Iustin Pop
291 f9518d38 Iustin Pop
    return result
292 f9518d38 Iustin Pop
293 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
294 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
295 4b98ac29 Iustin Pop

296 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
297 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
298 4b98ac29 Iustin Pop
    @type l_ids: list
299 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
300 4b98ac29 Iustin Pop
    @type p_ids: list
301 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
302 4b98ac29 Iustin Pop
    @rtype: list
303 4b98ac29 Iustin Pop
    @return: a list of error messages
304 4b98ac29 Iustin Pop

305 4b98ac29 Iustin Pop
    """
306 4b98ac29 Iustin Pop
    result = []
307 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
308 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
309 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
310 25ae22e4 Iustin Pop
      else:
311 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
312 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
313 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
314 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
315 25ae22e4 Iustin Pop
      else:
316 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
317 4b98ac29 Iustin Pop
318 4b98ac29 Iustin Pop
    if disk.children:
319 4b98ac29 Iustin Pop
      for child in disk.children:
320 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
321 4b98ac29 Iustin Pop
    return result
322 4b98ac29 Iustin Pop
323 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
324 a8efbb40 Iustin Pop
    """Verify function.
325 a8efbb40 Iustin Pop

326 4a89c54a Iustin Pop
    @rtype: list
327 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
328 4a89c54a Iustin Pop
        configuration errors
329 4a89c54a Iustin Pop

330 a8083063 Iustin Pop
    """
331 a8083063 Iustin Pop
    result = []
332 a8083063 Iustin Pop
    seen_macs = []
333 48ce9fd9 Iustin Pop
    ports = {}
334 a8083063 Iustin Pop
    data = self._config_data
335 4b98ac29 Iustin Pop
    seen_lids = []
336 4b98ac29 Iustin Pop
    seen_pids = []
337 9a5fba23 Guido Trotter
338 9a5fba23 Guido Trotter
    # global cluster checks
339 9a5fba23 Guido Trotter
    if not data.cluster.enabled_hypervisors:
340 9a5fba23 Guido Trotter
      result.append("enabled hypervisors list doesn't have any entries")
341 9a5fba23 Guido Trotter
    invalid_hvs = set(data.cluster.enabled_hypervisors) - constants.HYPER_TYPES
342 9a5fba23 Guido Trotter
    if invalid_hvs:
343 9a5fba23 Guido Trotter
      result.append("enabled hypervisors contains invalid entries: %s" %
344 9a5fba23 Guido Trotter
                    invalid_hvs)
345 9a5fba23 Guido Trotter
346 9a5fba23 Guido Trotter
    if data.cluster.master_node not in data.nodes:
347 9a5fba23 Guido Trotter
      result.append("cluster has invalid primary node '%s'" %
348 9a5fba23 Guido Trotter
                    data.cluster.master_node)
349 9a5fba23 Guido Trotter
350 9a5fba23 Guido Trotter
    # per-instance checks
351 a8083063 Iustin Pop
    for instance_name in data.instances:
352 a8083063 Iustin Pop
      instance = data.instances[instance_name]
353 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
354 8522ceeb Iustin Pop
        result.append("instance '%s' has invalid primary node '%s'" %
355 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
356 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
357 a8083063 Iustin Pop
        if snode not in data.nodes:
358 8522ceeb Iustin Pop
          result.append("instance '%s' has invalid secondary node '%s'" %
359 a8083063 Iustin Pop
                        (instance_name, snode))
360 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
361 a8083063 Iustin Pop
        if nic.mac in seen_macs:
362 8522ceeb Iustin Pop
          result.append("instance '%s' has NIC %d mac %s duplicate" %
363 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
364 a8083063 Iustin Pop
        else:
365 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
366 48ce9fd9 Iustin Pop
367 48ce9fd9 Iustin Pop
      # gather the drbd ports for duplicate checks
368 48ce9fd9 Iustin Pop
      for dsk in instance.disks:
369 48ce9fd9 Iustin Pop
        if dsk.dev_type in constants.LDS_DRBD:
370 48ce9fd9 Iustin Pop
          tcp_port = dsk.logical_id[2]
371 48ce9fd9 Iustin Pop
          if tcp_port not in ports:
372 48ce9fd9 Iustin Pop
            ports[tcp_port] = []
373 48ce9fd9 Iustin Pop
          ports[tcp_port].append((instance.name, "drbd disk %s" % dsk.iv_name))
374 48ce9fd9 Iustin Pop
      # gather network port reservation
375 48ce9fd9 Iustin Pop
      net_port = getattr(instance, "network_port", None)
376 48ce9fd9 Iustin Pop
      if net_port is not None:
377 48ce9fd9 Iustin Pop
        if net_port not in ports:
378 48ce9fd9 Iustin Pop
          ports[net_port] = []
379 48ce9fd9 Iustin Pop
        ports[net_port].append((instance.name, "network port"))
380 48ce9fd9 Iustin Pop
381 332d0e37 Iustin Pop
      # instance disk verify
382 332d0e37 Iustin Pop
      for idx, disk in enumerate(instance.disks):
383 332d0e37 Iustin Pop
        result.extend(["instance '%s' disk %d error: %s" %
384 332d0e37 Iustin Pop
                       (instance.name, idx, msg) for msg in disk.Verify()])
385 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(disk, seen_lids, seen_pids))
386 332d0e37 Iustin Pop
387 48ce9fd9 Iustin Pop
    # cluster-wide pool of free ports
388 a8efbb40 Iustin Pop
    for free_port in data.cluster.tcpudp_port_pool:
389 48ce9fd9 Iustin Pop
      if free_port not in ports:
390 48ce9fd9 Iustin Pop
        ports[free_port] = []
391 48ce9fd9 Iustin Pop
      ports[free_port].append(("cluster", "port marked as free"))
392 48ce9fd9 Iustin Pop
393 48ce9fd9 Iustin Pop
    # compute tcp/udp duplicate ports
394 48ce9fd9 Iustin Pop
    keys = ports.keys()
395 48ce9fd9 Iustin Pop
    keys.sort()
396 48ce9fd9 Iustin Pop
    for pnum in keys:
397 48ce9fd9 Iustin Pop
      pdata = ports[pnum]
398 48ce9fd9 Iustin Pop
      if len(pdata) > 1:
399 48ce9fd9 Iustin Pop
        txt = ", ".join(["%s/%s" % val for val in pdata])
400 48ce9fd9 Iustin Pop
        result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
401 48ce9fd9 Iustin Pop
402 48ce9fd9 Iustin Pop
    # highest used tcp port check
403 48ce9fd9 Iustin Pop
    if keys:
404 a8efbb40 Iustin Pop
      if keys[-1] > data.cluster.highest_used_port:
405 48ce9fd9 Iustin Pop
        result.append("Highest used port mismatch, saved %s, computed %s" %
406 a8efbb40 Iustin Pop
                      (data.cluster.highest_used_port, keys[-1]))
407 a8efbb40 Iustin Pop
408 3a26773f Iustin Pop
    if not data.nodes[data.cluster.master_node].master_candidate:
409 3a26773f Iustin Pop
      result.append("Master node is not a master candidate")
410 3a26773f Iustin Pop
411 4a89c54a Iustin Pop
    # master candidate checks
412 e623dbe3 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats()
413 ec0292f1 Iustin Pop
    if mc_now < mc_max:
414 ec0292f1 Iustin Pop
      result.append("Not enough master candidates: actual %d, target %d" %
415 ec0292f1 Iustin Pop
                    (mc_now, mc_max))
416 48ce9fd9 Iustin Pop
417 5bf07049 Iustin Pop
    # node checks
418 5bf07049 Iustin Pop
    for node in data.nodes.values():
419 5bf07049 Iustin Pop
      if [node.master_candidate, node.drained, node.offline].count(True) > 1:
420 5bf07049 Iustin Pop
        result.append("Node %s state is invalid: master_candidate=%s,"
421 5bf07049 Iustin Pop
                      " drain=%s, offline=%s" %
422 5bf07049 Iustin Pop
                      (node.name, node.master_candidate, node.drain,
423 5bf07049 Iustin Pop
                       node.offline))
424 5bf07049 Iustin Pop
425 4a89c54a Iustin Pop
    # drbd minors check
426 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
427 4a89c54a Iustin Pop
    for node, minor, instance_a, instance_b in duplicates:
428 4a89c54a Iustin Pop
      result.append("DRBD minor %d on node %s is assigned twice to instances"
429 4a89c54a Iustin Pop
                    " %s and %s" % (minor, node, instance_a, instance_b))
430 4a89c54a Iustin Pop
431 0ce8f948 Iustin Pop
    # IP checks
432 0ce8f948 Iustin Pop
    ips = { data.cluster.master_ip: ["cluster_ip"] }
433 0ce8f948 Iustin Pop
    def _helper(ip, name):
434 0ce8f948 Iustin Pop
      if ip in ips:
435 0ce8f948 Iustin Pop
        ips[ip].append(name)
436 0ce8f948 Iustin Pop
      else:
437 0ce8f948 Iustin Pop
        ips[ip] = [name]
438 0ce8f948 Iustin Pop
439 0ce8f948 Iustin Pop
    for node in data.nodes.values():
440 0ce8f948 Iustin Pop
      _helper(node.primary_ip, "node:%s/primary" % node.name)
441 0ce8f948 Iustin Pop
      if node.secondary_ip != node.primary_ip:
442 0ce8f948 Iustin Pop
        _helper(node.secondary_ip, "node:%s/secondary" % node.name)
443 0ce8f948 Iustin Pop
444 0ce8f948 Iustin Pop
    for ip, owners in ips.items():
445 0ce8f948 Iustin Pop
      if len(owners) > 1:
446 0ce8f948 Iustin Pop
        result.append("IP address %s is used by multiple owners: %s" %
447 0ce8f948 Iustin Pop
                      (ip, ", ".join(owners)))
448 a8083063 Iustin Pop
    return result
449 a8083063 Iustin Pop
450 4a89c54a Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
451 4a89c54a Iustin Pop
  def VerifyConfig(self):
452 4a89c54a Iustin Pop
    """Verify function.
453 4a89c54a Iustin Pop

454 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
455 4a89c54a Iustin Pop

456 4a89c54a Iustin Pop
    @rtype: list
457 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
458 4a89c54a Iustin Pop
        configuration errors
459 4a89c54a Iustin Pop

460 4a89c54a Iustin Pop
    """
461 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
462 4a89c54a Iustin Pop
463 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
464 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
465 a8083063 Iustin Pop

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

468 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
469 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
470 a8083063 Iustin Pop
    node.
471 a8083063 Iustin Pop

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

474 a8083063 Iustin Pop
    """
475 a8083063 Iustin Pop
    if disk.children:
476 a8083063 Iustin Pop
      for child in disk.children:
477 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
478 a8083063 Iustin Pop
479 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
480 a8083063 Iustin Pop
      return
481 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
482 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
483 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
484 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
485 3ecf6786 Iustin Pop
                                        node_name)
486 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
487 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
488 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
489 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
490 a8083063 Iustin Pop
                                        " for %s" % str(disk))
491 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
492 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
493 a8083063 Iustin Pop
      if pnode == node_name:
494 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
495 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
496 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
497 a8083063 Iustin Pop
    else:
498 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
499 a8083063 Iustin Pop
    return
500 a8083063 Iustin Pop
501 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
502 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
503 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
504 f78ede4e Guido Trotter

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

507 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
508 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
509 f78ede4e Guido Trotter
    node.
510 f78ede4e Guido Trotter

511 f78ede4e Guido Trotter
    """
512 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
513 f78ede4e Guido Trotter
514 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
515 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
516 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
517 b2fddf63 Iustin Pop

518 b2fddf63 Iustin Pop
    """
519 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
520 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
521 264bb3c5 Michael Hanselmann
522 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
523 264bb3c5 Michael Hanselmann
    self._WriteConfig()
524 264bb3c5 Michael Hanselmann
525 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
526 b2fddf63 Iustin Pop
  def GetPortList(self):
527 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
528 264bb3c5 Michael Hanselmann

529 264bb3c5 Michael Hanselmann
    """
530 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
531 264bb3c5 Michael Hanselmann
532 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
533 a8083063 Iustin Pop
  def AllocatePort(self):
534 a8083063 Iustin Pop
    """Allocate a port.
535 a8083063 Iustin Pop

536 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
537 b2fddf63 Iustin Pop
    default port range (and in this case we increase
538 b2fddf63 Iustin Pop
    highest_used_port).
539 a8083063 Iustin Pop

540 a8083063 Iustin Pop
    """
541 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
542 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
543 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
544 264bb3c5 Michael Hanselmann
    else:
545 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
546 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
547 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
548 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
549 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
550 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
551 a8083063 Iustin Pop
552 a8083063 Iustin Pop
    self._WriteConfig()
553 a8083063 Iustin Pop
    return port
554 a8083063 Iustin Pop
555 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
556 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
557 a81c53c9 Iustin Pop

558 4a89c54a Iustin Pop
    @rtype: (dict, list)
559 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
560 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
561 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
562 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
563 4a89c54a Iustin Pop
        should raise an exception
564 a81c53c9 Iustin Pop

565 a81c53c9 Iustin Pop
    """
566 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
567 4a89c54a Iustin Pop
      duplicates = []
568 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
569 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
570 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
571 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
572 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
573 a81c53c9 Iustin Pop
          if port in used[node]:
574 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
575 4a89c54a Iustin Pop
          else:
576 4a89c54a Iustin Pop
            used[node][port] = instance_name
577 a81c53c9 Iustin Pop
      if disk.children:
578 a81c53c9 Iustin Pop
        for child in disk.children:
579 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
580 4a89c54a Iustin Pop
      return duplicates
581 a81c53c9 Iustin Pop
582 4a89c54a Iustin Pop
    duplicates = []
583 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
584 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
585 79b26a7a Iustin Pop
      for disk in instance.disks:
586 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
587 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
588 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
589 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
590 4a89c54a Iustin Pop
      else:
591 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
592 4a89c54a Iustin Pop
    return my_dict, duplicates
593 a81c53c9 Iustin Pop
594 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
595 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
596 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
597 6d2e83d5 Iustin Pop

598 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
599 6d2e83d5 Iustin Pop

600 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
601 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
602 6d2e83d5 Iustin Pop
        an empty list).
603 6d2e83d5 Iustin Pop

604 6d2e83d5 Iustin Pop
    """
605 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
606 4a89c54a Iustin Pop
    if duplicates:
607 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
608 4a89c54a Iustin Pop
                                      str(duplicates))
609 4a89c54a Iustin Pop
    return d_map
610 6d2e83d5 Iustin Pop
611 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
612 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
613 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
614 a81c53c9 Iustin Pop

615 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
616 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
617 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
618 a81c53c9 Iustin Pop
    order as the passed nodes.
619 a81c53c9 Iustin Pop

620 32388e6d Iustin Pop
    @type instance: string
621 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
622 32388e6d Iustin Pop

623 a81c53c9 Iustin Pop
    """
624 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
625 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
626 32388e6d Iustin Pop
627 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
628 4a89c54a Iustin Pop
    if duplicates:
629 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
630 4a89c54a Iustin Pop
                                      str(duplicates))
631 a81c53c9 Iustin Pop
    result = []
632 a81c53c9 Iustin Pop
    for nname in nodes:
633 a81c53c9 Iustin Pop
      ndata = d_map[nname]
634 a81c53c9 Iustin Pop
      if not ndata:
635 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
636 a81c53c9 Iustin Pop
        result.append(0)
637 a81c53c9 Iustin Pop
        ndata[0] = instance
638 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
639 a81c53c9 Iustin Pop
        continue
640 a81c53c9 Iustin Pop
      keys = ndata.keys()
641 a81c53c9 Iustin Pop
      keys.sort()
642 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
643 a81c53c9 Iustin Pop
      if ffree is None:
644 a81c53c9 Iustin Pop
        # return the next minor
645 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
646 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
647 a81c53c9 Iustin Pop
      else:
648 a81c53c9 Iustin Pop
        minor = ffree
649 4a89c54a Iustin Pop
      # double-check minor against current instances
650 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
651 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
652 4a89c54a Iustin Pop
              " already allocated to instance %s" %
653 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
654 a81c53c9 Iustin Pop
      ndata[minor] = instance
655 4a89c54a Iustin Pop
      # double-check minor against reservation
656 4a89c54a Iustin Pop
      r_key = (nname, minor)
657 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
658 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
659 4a89c54a Iustin Pop
              " reserved for instance %s" %
660 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
661 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
662 4a89c54a Iustin Pop
      result.append(minor)
663 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
664 a81c53c9 Iustin Pop
                  nodes, result)
665 a81c53c9 Iustin Pop
    return result
666 a81c53c9 Iustin Pop
667 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
668 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
669 a81c53c9 Iustin Pop

670 a81c53c9 Iustin Pop
    @type instance: string
671 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
672 a81c53c9 Iustin Pop
                     released
673 a81c53c9 Iustin Pop

674 a81c53c9 Iustin Pop
    """
675 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
676 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
677 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
678 a81c53c9 Iustin Pop
      if name == instance:
679 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
680 a81c53c9 Iustin Pop
681 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
682 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
683 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
684 61cf6b5e Iustin Pop

685 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
686 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
687 61cf6b5e Iustin Pop
    functions.
688 61cf6b5e Iustin Pop

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

691 61cf6b5e Iustin Pop
    @type instance: string
692 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
693 61cf6b5e Iustin Pop
                     released
694 61cf6b5e Iustin Pop

695 61cf6b5e Iustin Pop
    """
696 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
697 61cf6b5e Iustin Pop
698 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
699 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
700 4a8b186a Michael Hanselmann
    """Get the configuration version.
701 4a8b186a Michael Hanselmann

702 4a8b186a Michael Hanselmann
    @return: Config version
703 4a8b186a Michael Hanselmann

704 4a8b186a Michael Hanselmann
    """
705 4a8b186a Michael Hanselmann
    return self._config_data.version
706 4a8b186a Michael Hanselmann
707 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
708 4a8b186a Michael Hanselmann
  def GetClusterName(self):
709 4a8b186a Michael Hanselmann
    """Get cluster name.
710 4a8b186a Michael Hanselmann

711 4a8b186a Michael Hanselmann
    @return: Cluster name
712 4a8b186a Michael Hanselmann

713 4a8b186a Michael Hanselmann
    """
714 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
715 4a8b186a Michael Hanselmann
716 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
717 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
718 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
719 4a8b186a Michael Hanselmann

720 4a8b186a Michael Hanselmann
    @return: Master hostname
721 4a8b186a Michael Hanselmann

722 4a8b186a Michael Hanselmann
    """
723 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
724 4a8b186a Michael Hanselmann
725 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
726 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
727 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
728 4a8b186a Michael Hanselmann

729 4a8b186a Michael Hanselmann
    @return: Master IP
730 4a8b186a Michael Hanselmann

731 4a8b186a Michael Hanselmann
    """
732 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
733 4a8b186a Michael Hanselmann
734 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
735 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
736 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
737 4a8b186a Michael Hanselmann

738 4a8b186a Michael Hanselmann
    """
739 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
740 4a8b186a Michael Hanselmann
741 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
742 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
743 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
744 4a8b186a Michael Hanselmann

745 4a8b186a Michael Hanselmann
    """
746 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
747 4a8b186a Michael Hanselmann
748 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
749 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
750 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
751 4a8b186a Michael Hanselmann

752 4a8b186a Michael Hanselmann
    """
753 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
754 4a8b186a Michael Hanselmann
755 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
756 a8083063 Iustin Pop
  def GetHostKey(self):
757 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
758 a8083063 Iustin Pop

759 c41eea6e Iustin Pop
    @rtype: string
760 c41eea6e Iustin Pop
    @return: the rsa hostkey
761 a8083063 Iustin Pop

762 a8083063 Iustin Pop
    """
763 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
764 a8083063 Iustin Pop
765 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
766 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
767 a8083063 Iustin Pop
    """Add an instance to the config.
768 a8083063 Iustin Pop

769 a8083063 Iustin Pop
    This should be used after creating a new instance.
770 a8083063 Iustin Pop

771 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
772 c41eea6e Iustin Pop
    @param instance: the instance object
773 c41eea6e Iustin Pop

774 a8083063 Iustin Pop
    """
775 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
776 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
777 a8083063 Iustin Pop
778 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
779 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
780 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
781 923b1523 Iustin Pop
782 e4640214 Guido Trotter
    all_macs = self._AllMACs()
783 e4640214 Guido Trotter
    for nic in instance.nics:
784 e4640214 Guido Trotter
      if nic.mac in all_macs:
785 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
786 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
787 430b923c Iustin Pop
                                        (instance.name, nic.mac))
788 430b923c Iustin Pop
789 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
790 e4640214 Guido Trotter
791 b989e85d Iustin Pop
    instance.serial_no = 1
792 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
793 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
794 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
795 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
796 a8083063 Iustin Pop
    self._WriteConfig()
797 a8083063 Iustin Pop
798 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
799 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
800 430b923c Iustin Pop

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

804 430b923c Iustin Pop
    """
805 430b923c Iustin Pop
    if not item.uuid:
806 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
807 430b923c Iustin Pop
    elif item.uuid in self._AllIDs(temporary=True):
808 430b923c Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID already in use" %
809 430b923c Iustin Pop
                                      (item.name, item.uuid))
810 430b923c Iustin Pop
811 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
812 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
813 a8083063 Iustin Pop

814 a8083063 Iustin Pop
    """
815 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
816 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
817 a8083063 Iustin Pop
818 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
819 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
820 3ecf6786 Iustin Pop
                                      instance_name)
821 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
822 0d68c45d Iustin Pop
    if instance.admin_up != status:
823 0d68c45d Iustin Pop
      instance.admin_up = status
824 b989e85d Iustin Pop
      instance.serial_no += 1
825 d693c864 Iustin Pop
      instance.mtime = time.time()
826 455a3445 Iustin Pop
      self._WriteConfig()
827 a8083063 Iustin Pop
828 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
829 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
830 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
831 6a408fb2 Iustin Pop

832 6a408fb2 Iustin Pop
    """
833 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
834 6a408fb2 Iustin Pop
835 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
836 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
837 a8083063 Iustin Pop
    """Remove the instance from the configuration.
838 a8083063 Iustin Pop

839 a8083063 Iustin Pop
    """
840 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
841 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
842 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
843 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
844 a8083063 Iustin Pop
    self._WriteConfig()
845 a8083063 Iustin Pop
846 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
847 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
848 fc95f88f Iustin Pop
    """Rename an instance.
849 fc95f88f Iustin Pop

850 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
851 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
852 fc95f88f Iustin Pop
    rename.
853 fc95f88f Iustin Pop

854 fc95f88f Iustin Pop
    """
855 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
856 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
857 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
858 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
859 fc95f88f Iustin Pop
    inst.name = new_name
860 b23c4333 Manuel Franceschini
861 b23c4333 Manuel Franceschini
    for disk in inst.disks:
862 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
863 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
864 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
865 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
866 b23c4333 Manuel Franceschini
                                              os.path.join(file_storage_dir,
867 b23c4333 Manuel Franceschini
                                                           inst.name,
868 b23c4333 Manuel Franceschini
                                                           disk.iv_name))
869 b23c4333 Manuel Franceschini
870 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
871 fc95f88f Iustin Pop
    self._WriteConfig()
872 fc95f88f Iustin Pop
873 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
874 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
875 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
876 a8083063 Iustin Pop

877 a8083063 Iustin Pop
    """
878 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
879 a8083063 Iustin Pop
880 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
881 94bbfece Iustin Pop
    """Get the list of instances.
882 94bbfece Iustin Pop

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

885 94bbfece Iustin Pop
    """
886 94bbfece Iustin Pop
    return self._config_data.instances.keys()
887 94bbfece Iustin Pop
888 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
889 a8083063 Iustin Pop
  def GetInstanceList(self):
890 a8083063 Iustin Pop
    """Get the list of instances.
891 a8083063 Iustin Pop

892 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
893 c41eea6e Iustin Pop
        'instance1.example.com']
894 a8083063 Iustin Pop

895 a8083063 Iustin Pop
    """
896 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
897 a8083063 Iustin Pop
898 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
899 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
900 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
901 a8083063 Iustin Pop

902 a8083063 Iustin Pop
    """
903 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
904 bcdf16d7 Guido Trotter
                                    self._config_data.instances.keys(),
905 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
906 a8083063 Iustin Pop
907 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
908 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
909 94bbfece Iustin Pop

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

912 94bbfece Iustin Pop
    """
913 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
914 94bbfece Iustin Pop
      return None
915 94bbfece Iustin Pop
916 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
917 94bbfece Iustin Pop
918 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
919 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
920 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
921 a8083063 Iustin Pop

922 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
923 a8083063 Iustin Pop
    an instance are taken from the live systems.
924 a8083063 Iustin Pop

925 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
926 c41eea6e Iustin Pop
        I{instance1.example.com}
927 a8083063 Iustin Pop

928 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
929 c41eea6e Iustin Pop
    @return: the instance object
930 a8083063 Iustin Pop

931 a8083063 Iustin Pop
    """
932 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
933 a8083063 Iustin Pop
934 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
935 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
936 0b2de758 Iustin Pop
    """Get the configuration of all instances.
937 0b2de758 Iustin Pop

938 0b2de758 Iustin Pop
    @rtype: dict
939 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
940 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
941 0b2de758 Iustin Pop

942 0b2de758 Iustin Pop
    """
943 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
944 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
945 0b2de758 Iustin Pop
    return my_dict
946 0b2de758 Iustin Pop
947 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
948 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
949 a8083063 Iustin Pop
    """Add a node to the configuration.
950 a8083063 Iustin Pop

951 c41eea6e Iustin Pop
    @type node: L{objects.Node}
952 c41eea6e Iustin Pop
    @param node: a Node instance
953 a8083063 Iustin Pop

954 a8083063 Iustin Pop
    """
955 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
956 d8470559 Michael Hanselmann
957 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
958 430b923c Iustin Pop
959 b989e85d Iustin Pop
    node.serial_no = 1
960 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
961 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
962 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
963 a8083063 Iustin Pop
    self._WriteConfig()
964 a8083063 Iustin Pop
965 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
966 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
967 a8083063 Iustin Pop
    """Remove a node from the configuration.
968 a8083063 Iustin Pop

969 a8083063 Iustin Pop
    """
970 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
971 d8470559 Michael Hanselmann
972 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
973 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
974 a8083063 Iustin Pop
975 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
976 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
977 a8083063 Iustin Pop
    self._WriteConfig()
978 a8083063 Iustin Pop
979 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
980 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
981 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
982 a8083063 Iustin Pop

983 a8083063 Iustin Pop
    """
984 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
985 bcdf16d7 Guido Trotter
                                    self._config_data.nodes.keys(),
986 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
987 a8083063 Iustin Pop
988 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
989 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
990 a8083063 Iustin Pop

991 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
992 c41eea6e Iustin Pop
    held.
993 f78ede4e Guido Trotter

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

996 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
997 c41eea6e Iustin Pop
    @return: the node object
998 a8083063 Iustin Pop

999 a8083063 Iustin Pop
    """
1000 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1001 a8083063 Iustin Pop
      return None
1002 a8083063 Iustin Pop
1003 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1004 a8083063 Iustin Pop
1005 f78ede4e Guido Trotter
1006 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1007 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1008 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1009 f78ede4e Guido Trotter

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

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

1014 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1015 c41eea6e Iustin Pop
    @return: the node object
1016 f78ede4e Guido Trotter

1017 f78ede4e Guido Trotter
    """
1018 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1019 f78ede4e Guido Trotter
1020 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1021 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1022 a8083063 Iustin Pop

1023 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1024 c41eea6e Iustin Pop
    held.
1025 c41eea6e Iustin Pop

1026 c41eea6e Iustin Pop
    @rtype: list
1027 f78ede4e Guido Trotter

1028 a8083063 Iustin Pop
    """
1029 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1030 a8083063 Iustin Pop
1031 f78ede4e Guido Trotter
1032 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1033 f78ede4e Guido Trotter
  def GetNodeList(self):
1034 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1035 f78ede4e Guido Trotter

1036 f78ede4e Guido Trotter
    """
1037 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1038 f78ede4e Guido Trotter
1039 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1040 94a02bb5 Iustin Pop
  def GetOnlineNodeList(self):
1041 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1042 94a02bb5 Iustin Pop

1043 94a02bb5 Iustin Pop
    """
1044 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1045 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1046 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1047 94a02bb5 Iustin Pop
1048 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1049 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1050 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1051 d65e5776 Iustin Pop

1052 d65e5776 Iustin Pop
    @rtype: dict
1053 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1054 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1055 d65e5776 Iustin Pop

1056 d65e5776 Iustin Pop
    """
1057 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1058 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1059 d65e5776 Iustin Pop
    return my_dict
1060 d65e5776 Iustin Pop
1061 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1062 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1063 ec0292f1 Iustin Pop

1064 23f06b2b Iustin Pop
    @type exceptions: list
1065 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1066 ec0292f1 Iustin Pop
    @rtype: tuple
1067 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1068 ec0292f1 Iustin Pop

1069 ec0292f1 Iustin Pop
    """
1070 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1071 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1072 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1073 23f06b2b Iustin Pop
        continue
1074 5bf07049 Iustin Pop
      if not (node.offline or node.drained):
1075 ec0292f1 Iustin Pop
        mc_max += 1
1076 ec0292f1 Iustin Pop
      if node.master_candidate:
1077 ec0292f1 Iustin Pop
        mc_now += 1
1078 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1079 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1080 ec0292f1 Iustin Pop
1081 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1082 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1083 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1084 ec0292f1 Iustin Pop

1085 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1086 ec0292f1 Iustin Pop

1087 23f06b2b Iustin Pop
    @type exceptions: list
1088 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1089 ec0292f1 Iustin Pop
    @rtype: tuple
1090 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1091 ec0292f1 Iustin Pop

1092 ec0292f1 Iustin Pop
    """
1093 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1094 ec0292f1 Iustin Pop
1095 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1096 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1097 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1098 ec0292f1 Iustin Pop

1099 44485f49 Guido Trotter
    @type exceptions: list
1100 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1101 ec0292f1 Iustin Pop
    @rtype: list
1102 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1103 ec0292f1 Iustin Pop

1104 ec0292f1 Iustin Pop
    """
1105 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1106 ec0292f1 Iustin Pop
    mod_list = []
1107 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1108 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1109 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1110 ec0292f1 Iustin Pop
      for name in node_list:
1111 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1112 ec0292f1 Iustin Pop
          break
1113 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1114 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1115 44485f49 Guido Trotter
            node.name in exceptions):
1116 ec0292f1 Iustin Pop
          continue
1117 ee513a66 Iustin Pop
        mod_list.append(node)
1118 ec0292f1 Iustin Pop
        node.master_candidate = True
1119 ec0292f1 Iustin Pop
        node.serial_no += 1
1120 ec0292f1 Iustin Pop
        mc_now += 1
1121 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1122 ec0292f1 Iustin Pop
        # this should not happen
1123 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1124 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1125 ec0292f1 Iustin Pop
      if mod_list:
1126 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1127 ec0292f1 Iustin Pop
        self._WriteConfig()
1128 ec0292f1 Iustin Pop
1129 ec0292f1 Iustin Pop
    return mod_list
1130 ec0292f1 Iustin Pop
1131 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1132 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1133 a8083063 Iustin Pop

1134 a8083063 Iustin Pop
    """
1135 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1136 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1137 a8083063 Iustin Pop
1138 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1139 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1140 76d5d3a3 Iustin Pop

1141 76d5d3a3 Iustin Pop
    """
1142 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1143 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1144 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1145 76d5d3a3 Iustin Pop
1146 a8083063 Iustin Pop
  def _OpenConfig(self):
1147 a8083063 Iustin Pop
    """Read the config data from disk.
1148 a8083063 Iustin Pop

1149 a8083063 Iustin Pop
    """
1150 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1151 13998ef2 Michael Hanselmann
1152 a8083063 Iustin Pop
    try:
1153 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1154 13998ef2 Michael Hanselmann
    except Exception, err:
1155 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1156 5b263ed7 Michael Hanselmann
1157 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1158 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1159 5b263ed7 Michael Hanselmann
1160 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1161 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1162 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1163 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1164 90d726a8 Iustin Pop
1165 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1166 90d726a8 Iustin Pop
    data.UpgradeConfig()
1167 90d726a8 Iustin Pop
1168 a8083063 Iustin Pop
    self._config_data = data
1169 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1170 0779e3aa Iustin Pop
    # ssconf update
1171 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1172 a8083063 Iustin Pop
1173 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1174 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1175 76d5d3a3 Iustin Pop
1176 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1177 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1178 76d5d3a3 Iustin Pop

1179 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1180 76d5d3a3 Iustin Pop
    whole configuration, etc.
1181 76d5d3a3 Iustin Pop

1182 76d5d3a3 Iustin Pop
    @warning: this function will call L{_WriteConfig()}, so it needs
1183 76d5d3a3 Iustin Pop
        to either be called with the lock held or from a safe place
1184 76d5d3a3 Iustin Pop
        (the constructor)
1185 76d5d3a3 Iustin Pop

1186 76d5d3a3 Iustin Pop
    """
1187 76d5d3a3 Iustin Pop
    modified = False
1188 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1189 76d5d3a3 Iustin Pop
      if item.uuid is None:
1190 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1191 76d5d3a3 Iustin Pop
        modified = True
1192 76d5d3a3 Iustin Pop
    if modified:
1193 76d5d3a3 Iustin Pop
      self._WriteConfig()
1194 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1195 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1196 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1197 4fae38c5 Guido Trotter
1198 76d5d3a3 Iustin Pop
1199 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1200 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1201 a8083063 Iustin Pop

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

1205 a8083063 Iustin Pop
    """
1206 a8083063 Iustin Pop
    if self._offline:
1207 a8083063 Iustin Pop
      return True
1208 a4eae71f Michael Hanselmann
1209 a8083063 Iustin Pop
    bad = False
1210 a8083063 Iustin Pop
1211 6a5b8b4b Iustin Pop
    node_list = []
1212 6a5b8b4b Iustin Pop
    addr_list = []
1213 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1214 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1215 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1216 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1217 6b294c53 Iustin Pop
    # in between
1218 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1219 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1220 6a5b8b4b Iustin Pop
        continue
1221 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1222 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1223 6a5b8b4b Iustin Pop
        continue
1224 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1225 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1226 6b294c53 Iustin Pop
1227 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1228 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1229 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1230 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1231 1b54fc6c Guido Trotter
      if msg:
1232 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1233 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1234 1b54fc6c Guido Trotter
        logging.error(msg)
1235 a4eae71f Michael Hanselmann
1236 a4eae71f Michael Hanselmann
        if feedback_fn:
1237 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1238 a4eae71f Michael Hanselmann
1239 a8083063 Iustin Pop
        bad = True
1240 a4eae71f Michael Hanselmann
1241 a8083063 Iustin Pop
    return not bad
1242 a8083063 Iustin Pop
1243 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1244 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1245 a8083063 Iustin Pop

1246 a8083063 Iustin Pop
    """
1247 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
1248 a4eae71f Michael Hanselmann
1249 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
1250 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
1251 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
1252 d2231b8c Iustin Pop
    # recovery to the user
1253 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1254 4a89c54a Iustin Pop
    if config_errors:
1255 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
1256 d2231b8c Iustin Pop
                (", ".join(config_errors)))
1257 d2231b8c Iustin Pop
      logging.critical(errmsg)
1258 d2231b8c Iustin Pop
      if feedback_fn:
1259 d2231b8c Iustin Pop
        feedback_fn(errmsg)
1260 d2231b8c Iustin Pop
1261 a8083063 Iustin Pop
    if destination is None:
1262 a8083063 Iustin Pop
      destination = self._cfg_file
1263 a8083063 Iustin Pop
    self._BumpSerialNo()
1264 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1265 13998ef2 Michael Hanselmann
1266 13998ef2 Michael Hanselmann
    utils.WriteFile(destination, data=txt)
1267 13998ef2 Michael Hanselmann
1268 14e15659 Iustin Pop
    self.write_count += 1
1269 3d3a04bc Iustin Pop
1270 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
1271 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
1272 a8083063 Iustin Pop
1273 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
1274 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
1275 d9a855f1 Michael Hanselmann
      if not self._offline:
1276 cd34faf2 Michael Hanselmann
        result = rpc.RpcRunner.call_write_ssconf_files(
1277 e1e75d00 Iustin Pop
          self._UnlockedGetNodeList(),
1278 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
1279 a4eae71f Michael Hanselmann
1280 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
1281 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
1282 e1e75d00 Iustin Pop
          if msg:
1283 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
1284 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
1285 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
1286 a4eae71f Michael Hanselmann
1287 a4eae71f Michael Hanselmann
            if feedback_fn:
1288 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
1289 a4eae71f Michael Hanselmann
1290 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
1291 54d1a06e Michael Hanselmann
1292 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
1293 054596f0 Iustin Pop
    """Return the values needed by ssconf.
1294 054596f0 Iustin Pop

1295 054596f0 Iustin Pop
    @rtype: dict
1296 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1297 054596f0 Iustin Pop
        associated value
1298 054596f0 Iustin Pop

1299 054596f0 Iustin Pop
    """
1300 a3316e4a Iustin Pop
    fn = "\n".join
1301 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1302 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1303 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1304 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1305 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1306 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1307 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1308 a3316e4a Iustin Pop
1309 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1310 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1311 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1312 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1313 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1314 8113a52e Luca Bigliardi
                     if node.master_candidate)
1315 a3316e4a Iustin Pop
    node_data = fn(node_names)
1316 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1317 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1318 f56618e0 Iustin Pop
1319 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1320 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1321 03d1dba2 Michael Hanselmann
    return {
1322 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1323 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1324 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1325 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1326 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1327 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1328 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1329 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1330 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1331 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1332 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1333 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1334 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1335 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1336 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1337 03d1dba2 Michael Hanselmann
      }
1338 03d1dba2 Michael Hanselmann
1339 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1340 a8083063 Iustin Pop
  def GetVGName(self):
1341 a8083063 Iustin Pop
    """Return the volume group name.
1342 a8083063 Iustin Pop

1343 a8083063 Iustin Pop
    """
1344 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1345 a8083063 Iustin Pop
1346 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1347 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1348 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1349 89ff8e15 Manuel Franceschini

1350 89ff8e15 Manuel Franceschini
    """
1351 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1352 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1353 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1354 89ff8e15 Manuel Franceschini
1355 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1356 a8083063 Iustin Pop
  def GetMACPrefix(self):
1357 a8083063 Iustin Pop
    """Return the mac prefix.
1358 a8083063 Iustin Pop

1359 a8083063 Iustin Pop
    """
1360 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1361 62779dd0 Iustin Pop
1362 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1363 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1364 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1365 62779dd0 Iustin Pop

1366 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1367 c41eea6e Iustin Pop
    @return: the cluster object
1368 62779dd0 Iustin Pop

1369 62779dd0 Iustin Pop
    """
1370 62779dd0 Iustin Pop
    return self._config_data.cluster
1371 e00fb268 Iustin Pop
1372 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1373 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1374 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1375 e00fb268 Iustin Pop

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

1382 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1383 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1384 c41eea6e Iustin Pop
        the cluster
1385 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1386 c41eea6e Iustin Pop

1387 e00fb268 Iustin Pop
    """
1388 e00fb268 Iustin Pop
    if self._config_data is None:
1389 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1390 3ecf6786 Iustin Pop
                                   " cannot save.")
1391 f34901f8 Iustin Pop
    update_serial = False
1392 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1393 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1394 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1395 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1396 f34901f8 Iustin Pop
      update_serial = True
1397 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1398 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1399 e00fb268 Iustin Pop
    else:
1400 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1401 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1402 e00fb268 Iustin Pop
    if not test:
1403 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1404 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1405 f34901f8 Iustin Pop
    target.serial_no += 1
1406 d693c864 Iustin Pop
    target.mtime = now = time.time()
1407 f34901f8 Iustin Pop
1408 cff4c037 Iustin Pop
    if update_serial:
1409 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1410 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1411 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1412 b989e85d Iustin Pop
1413 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1414 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1415 61cf6b5e Iustin Pop
1416 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
1417 73064714 Guido Trotter
1418 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
1419 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
1420 73064714 Guido Trotter
    """Drop per-execution-context reservations
1421 73064714 Guido Trotter

1422 73064714 Guido Trotter
    """
1423 4fae38c5 Guido Trotter
    self._temporary_ids.DropECReservations(ec_id)
1424 36b66e6e Guido Trotter
    self._temporary_macs.DropECReservations(ec_id)
1425 afa1386e Guido Trotter
    self._temporary_secrets.DropECReservations(ec_id)