Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 89b70f39

History | View | Annotate | Download (46.5 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 8d9c3bef Michael Hanselmann
_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 8d9c3bef Michael Hanselmann
204 34e54ebc Iustin Pop
  def _AllLVs(self):
205 923b1523 Iustin Pop
    """Compute the list of all LVs.
206 923b1523 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

476 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
477 4a89c54a Iustin Pop

478 4a89c54a Iustin Pop
    @rtype: list
479 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
480 4a89c54a Iustin Pop
        configuration errors
481 4a89c54a Iustin Pop

482 4a89c54a Iustin Pop
    """
483 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
484 4a89c54a Iustin Pop
485 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
486 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
487 a8083063 Iustin Pop

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

490 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
491 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
492 a8083063 Iustin Pop
    node.
493 a8083063 Iustin Pop

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

496 a8083063 Iustin Pop
    """
497 a8083063 Iustin Pop
    if disk.children:
498 a8083063 Iustin Pop
      for child in disk.children:
499 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
500 a8083063 Iustin Pop
501 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
502 a8083063 Iustin Pop
      return
503 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
504 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
505 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
506 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
507 3ecf6786 Iustin Pop
                                        node_name)
508 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
509 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
510 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
511 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
512 a8083063 Iustin Pop
                                        " for %s" % str(disk))
513 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
514 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
515 a8083063 Iustin Pop
      if pnode == node_name:
516 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
517 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
518 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
519 a8083063 Iustin Pop
    else:
520 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
521 a8083063 Iustin Pop
    return
522 a8083063 Iustin Pop
523 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
524 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
525 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
526 f78ede4e Guido Trotter

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

529 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
530 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
531 f78ede4e Guido Trotter
    node.
532 f78ede4e Guido Trotter

533 f78ede4e Guido Trotter
    """
534 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
535 f78ede4e Guido Trotter
536 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
537 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
538 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
539 b2fddf63 Iustin Pop

540 b2fddf63 Iustin Pop
    """
541 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
542 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
543 264bb3c5 Michael Hanselmann
544 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
545 264bb3c5 Michael Hanselmann
    self._WriteConfig()
546 264bb3c5 Michael Hanselmann
547 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
548 b2fddf63 Iustin Pop
  def GetPortList(self):
549 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
550 264bb3c5 Michael Hanselmann

551 264bb3c5 Michael Hanselmann
    """
552 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
553 264bb3c5 Michael Hanselmann
554 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
555 a8083063 Iustin Pop
  def AllocatePort(self):
556 a8083063 Iustin Pop
    """Allocate a port.
557 a8083063 Iustin Pop

558 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
559 b2fddf63 Iustin Pop
    default port range (and in this case we increase
560 b2fddf63 Iustin Pop
    highest_used_port).
561 a8083063 Iustin Pop

562 a8083063 Iustin Pop
    """
563 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
564 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
565 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
566 264bb3c5 Michael Hanselmann
    else:
567 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
568 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
569 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
570 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
571 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
572 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
573 a8083063 Iustin Pop
574 a8083063 Iustin Pop
    self._WriteConfig()
575 a8083063 Iustin Pop
    return port
576 a8083063 Iustin Pop
577 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
578 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
579 a81c53c9 Iustin Pop

580 4a89c54a Iustin Pop
    @rtype: (dict, list)
581 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
582 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
583 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
584 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
585 4a89c54a Iustin Pop
        should raise an exception
586 a81c53c9 Iustin Pop

587 a81c53c9 Iustin Pop
    """
588 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
589 4a89c54a Iustin Pop
      duplicates = []
590 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
591 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
592 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
593 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
594 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
595 a81c53c9 Iustin Pop
          if port in used[node]:
596 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
597 4a89c54a Iustin Pop
          else:
598 4a89c54a Iustin Pop
            used[node][port] = instance_name
599 a81c53c9 Iustin Pop
      if disk.children:
600 a81c53c9 Iustin Pop
        for child in disk.children:
601 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
602 4a89c54a Iustin Pop
      return duplicates
603 a81c53c9 Iustin Pop
604 4a89c54a Iustin Pop
    duplicates = []
605 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
606 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
607 79b26a7a Iustin Pop
      for disk in instance.disks:
608 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
609 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
610 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
611 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
612 4a89c54a Iustin Pop
      else:
613 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
614 4a89c54a Iustin Pop
    return my_dict, duplicates
615 a81c53c9 Iustin Pop
616 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
617 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
618 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
619 6d2e83d5 Iustin Pop

620 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
621 6d2e83d5 Iustin Pop

622 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
623 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
624 6d2e83d5 Iustin Pop
        an empty list).
625 6d2e83d5 Iustin Pop

626 6d2e83d5 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 4a89c54a Iustin Pop
    return d_map
632 6d2e83d5 Iustin Pop
633 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
634 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
635 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
636 a81c53c9 Iustin Pop

637 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
638 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
639 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
640 a81c53c9 Iustin Pop
    order as the passed nodes.
641 a81c53c9 Iustin Pop

642 32388e6d Iustin Pop
    @type instance: string
643 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
644 32388e6d Iustin Pop

645 a81c53c9 Iustin Pop
    """
646 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
647 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
648 32388e6d Iustin Pop
649 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
650 4a89c54a Iustin Pop
    if duplicates:
651 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
652 4a89c54a Iustin Pop
                                      str(duplicates))
653 a81c53c9 Iustin Pop
    result = []
654 a81c53c9 Iustin Pop
    for nname in nodes:
655 a81c53c9 Iustin Pop
      ndata = d_map[nname]
656 a81c53c9 Iustin Pop
      if not ndata:
657 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
658 a81c53c9 Iustin Pop
        result.append(0)
659 a81c53c9 Iustin Pop
        ndata[0] = instance
660 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
661 a81c53c9 Iustin Pop
        continue
662 a81c53c9 Iustin Pop
      keys = ndata.keys()
663 a81c53c9 Iustin Pop
      keys.sort()
664 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
665 a81c53c9 Iustin Pop
      if ffree is None:
666 a81c53c9 Iustin Pop
        # return the next minor
667 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
668 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
669 a81c53c9 Iustin Pop
      else:
670 a81c53c9 Iustin Pop
        minor = ffree
671 4a89c54a Iustin Pop
      # double-check minor against current instances
672 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
673 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
674 4a89c54a Iustin Pop
              " already allocated to instance %s" %
675 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
676 a81c53c9 Iustin Pop
      ndata[minor] = instance
677 4a89c54a Iustin Pop
      # double-check minor against reservation
678 4a89c54a Iustin Pop
      r_key = (nname, minor)
679 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
680 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
681 4a89c54a Iustin Pop
              " reserved for instance %s" %
682 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
683 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
684 4a89c54a Iustin Pop
      result.append(minor)
685 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
686 a81c53c9 Iustin Pop
                  nodes, result)
687 a81c53c9 Iustin Pop
    return result
688 a81c53c9 Iustin Pop
689 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
690 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
691 a81c53c9 Iustin Pop

692 a81c53c9 Iustin Pop
    @type instance: string
693 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
694 a81c53c9 Iustin Pop
                     released
695 a81c53c9 Iustin Pop

696 a81c53c9 Iustin Pop
    """
697 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
698 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
699 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
700 a81c53c9 Iustin Pop
      if name == instance:
701 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
702 a81c53c9 Iustin Pop
703 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
704 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
705 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
706 61cf6b5e Iustin Pop

707 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
708 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
709 61cf6b5e Iustin Pop
    functions.
710 61cf6b5e Iustin Pop

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

713 61cf6b5e Iustin Pop
    @type instance: string
714 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
715 61cf6b5e Iustin Pop
                     released
716 61cf6b5e Iustin Pop

717 61cf6b5e Iustin Pop
    """
718 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
719 61cf6b5e Iustin Pop
720 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
721 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
722 4a8b186a Michael Hanselmann
    """Get the configuration version.
723 4a8b186a Michael Hanselmann

724 4a8b186a Michael Hanselmann
    @return: Config version
725 4a8b186a Michael Hanselmann

726 4a8b186a Michael Hanselmann
    """
727 4a8b186a Michael Hanselmann
    return self._config_data.version
728 4a8b186a Michael Hanselmann
729 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
730 4a8b186a Michael Hanselmann
  def GetClusterName(self):
731 4a8b186a Michael Hanselmann
    """Get cluster name.
732 4a8b186a Michael Hanselmann

733 4a8b186a Michael Hanselmann
    @return: Cluster name
734 4a8b186a Michael Hanselmann

735 4a8b186a Michael Hanselmann
    """
736 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
737 4a8b186a Michael Hanselmann
738 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
739 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
740 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
741 4a8b186a Michael Hanselmann

742 4a8b186a Michael Hanselmann
    @return: Master hostname
743 4a8b186a Michael Hanselmann

744 4a8b186a Michael Hanselmann
    """
745 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
746 4a8b186a Michael Hanselmann
747 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
748 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
749 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
750 4a8b186a Michael Hanselmann

751 4a8b186a Michael Hanselmann
    @return: Master IP
752 4a8b186a Michael Hanselmann

753 4a8b186a Michael Hanselmann
    """
754 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
755 4a8b186a Michael Hanselmann
756 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
757 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
758 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
759 4a8b186a Michael Hanselmann

760 4a8b186a Michael Hanselmann
    """
761 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
762 4a8b186a Michael Hanselmann
763 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
764 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
765 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
766 4a8b186a Michael Hanselmann

767 4a8b186a Michael Hanselmann
    """
768 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
769 4a8b186a Michael Hanselmann
770 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
771 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
772 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
773 4a8b186a Michael Hanselmann

774 4a8b186a Michael Hanselmann
    """
775 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
776 4a8b186a Michael Hanselmann
777 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
778 a8083063 Iustin Pop
  def GetHostKey(self):
779 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
780 a8083063 Iustin Pop

781 c41eea6e Iustin Pop
    @rtype: string
782 c41eea6e Iustin Pop
    @return: the rsa hostkey
783 a8083063 Iustin Pop

784 a8083063 Iustin Pop
    """
785 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
786 a8083063 Iustin Pop
787 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
788 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
789 a8083063 Iustin Pop
    """Add an instance to the config.
790 a8083063 Iustin Pop

791 a8083063 Iustin Pop
    This should be used after creating a new instance.
792 a8083063 Iustin Pop

793 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
794 c41eea6e Iustin Pop
    @param instance: the instance object
795 c41eea6e Iustin Pop

796 a8083063 Iustin Pop
    """
797 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
798 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
799 a8083063 Iustin Pop
800 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
801 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
802 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
803 923b1523 Iustin Pop
804 e4640214 Guido Trotter
    all_macs = self._AllMACs()
805 e4640214 Guido Trotter
    for nic in instance.nics:
806 e4640214 Guido Trotter
      if nic.mac in all_macs:
807 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
808 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
809 430b923c Iustin Pop
                                        (instance.name, nic.mac))
810 430b923c Iustin Pop
811 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
812 e4640214 Guido Trotter
813 b989e85d Iustin Pop
    instance.serial_no = 1
814 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
815 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
816 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
817 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
818 a8083063 Iustin Pop
    self._WriteConfig()
819 a8083063 Iustin Pop
820 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
821 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
822 430b923c Iustin Pop

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

826 430b923c Iustin Pop
    """
827 430b923c Iustin Pop
    if not item.uuid:
828 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
829 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
830 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
831 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
832 430b923c Iustin Pop
833 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
834 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
835 a8083063 Iustin Pop

836 a8083063 Iustin Pop
    """
837 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
838 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
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'" %
842 3ecf6786 Iustin Pop
                                      instance_name)
843 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
844 0d68c45d Iustin Pop
    if instance.admin_up != status:
845 0d68c45d Iustin Pop
      instance.admin_up = status
846 b989e85d Iustin Pop
      instance.serial_no += 1
847 d693c864 Iustin Pop
      instance.mtime = time.time()
848 455a3445 Iustin Pop
      self._WriteConfig()
849 a8083063 Iustin Pop
850 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
851 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
852 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
853 6a408fb2 Iustin Pop

854 6a408fb2 Iustin Pop
    """
855 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
856 6a408fb2 Iustin Pop
857 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
858 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
859 a8083063 Iustin Pop
    """Remove the instance from the configuration.
860 a8083063 Iustin Pop

861 a8083063 Iustin Pop
    """
862 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
863 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
864 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
865 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
866 a8083063 Iustin Pop
    self._WriteConfig()
867 a8083063 Iustin Pop
868 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
869 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
870 fc95f88f Iustin Pop
    """Rename an instance.
871 fc95f88f Iustin Pop

872 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
873 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
874 fc95f88f Iustin Pop
    rename.
875 fc95f88f Iustin Pop

876 fc95f88f Iustin Pop
    """
877 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
878 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
879 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
880 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
881 fc95f88f Iustin Pop
    inst.name = new_name
882 b23c4333 Manuel Franceschini
883 b23c4333 Manuel Franceschini
    for disk in inst.disks:
884 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
885 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
886 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
887 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
888 b23c4333 Manuel Franceschini
                                              os.path.join(file_storage_dir,
889 b23c4333 Manuel Franceschini
                                                           inst.name,
890 b23c4333 Manuel Franceschini
                                                           disk.iv_name))
891 b23c4333 Manuel Franceschini
892 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
893 fc95f88f Iustin Pop
    self._WriteConfig()
894 fc95f88f Iustin Pop
895 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
896 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
897 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
898 a8083063 Iustin Pop

899 a8083063 Iustin Pop
    """
900 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
901 a8083063 Iustin Pop
902 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
903 94bbfece Iustin Pop
    """Get the list of instances.
904 94bbfece Iustin Pop

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

907 94bbfece Iustin Pop
    """
908 94bbfece Iustin Pop
    return self._config_data.instances.keys()
909 94bbfece Iustin Pop
910 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
911 a8083063 Iustin Pop
  def GetInstanceList(self):
912 a8083063 Iustin Pop
    """Get the list of instances.
913 a8083063 Iustin Pop

914 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
915 c41eea6e Iustin Pop
        'instance1.example.com']
916 a8083063 Iustin Pop

917 a8083063 Iustin Pop
    """
918 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
919 a8083063 Iustin Pop
920 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
921 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
922 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
923 a8083063 Iustin Pop

924 a8083063 Iustin Pop
    """
925 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
926 bcdf16d7 Guido Trotter
                                    self._config_data.instances.keys(),
927 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
928 a8083063 Iustin Pop
929 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
930 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
931 94bbfece Iustin Pop

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

934 94bbfece Iustin Pop
    """
935 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
936 94bbfece Iustin Pop
      return None
937 94bbfece Iustin Pop
938 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
939 94bbfece Iustin Pop
940 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
941 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
942 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
943 a8083063 Iustin Pop

944 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
945 a8083063 Iustin Pop
    an instance are taken from the live systems.
946 a8083063 Iustin Pop

947 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
948 c41eea6e Iustin Pop
        I{instance1.example.com}
949 a8083063 Iustin Pop

950 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
951 c41eea6e Iustin Pop
    @return: the instance object
952 a8083063 Iustin Pop

953 a8083063 Iustin Pop
    """
954 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
955 a8083063 Iustin Pop
956 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
957 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
958 0b2de758 Iustin Pop
    """Get the configuration of all instances.
959 0b2de758 Iustin Pop

960 0b2de758 Iustin Pop
    @rtype: dict
961 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
962 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
963 0b2de758 Iustin Pop

964 0b2de758 Iustin Pop
    """
965 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
966 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
967 0b2de758 Iustin Pop
    return my_dict
968 0b2de758 Iustin Pop
969 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
970 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
971 a8083063 Iustin Pop
    """Add a node to the configuration.
972 a8083063 Iustin Pop

973 c41eea6e Iustin Pop
    @type node: L{objects.Node}
974 c41eea6e Iustin Pop
    @param node: a Node instance
975 a8083063 Iustin Pop

976 a8083063 Iustin Pop
    """
977 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
978 d8470559 Michael Hanselmann
979 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
980 430b923c Iustin Pop
981 b989e85d Iustin Pop
    node.serial_no = 1
982 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
983 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
984 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
985 a8083063 Iustin Pop
    self._WriteConfig()
986 a8083063 Iustin Pop
987 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
988 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
989 a8083063 Iustin Pop
    """Remove a node from the configuration.
990 a8083063 Iustin Pop

991 a8083063 Iustin Pop
    """
992 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
993 d8470559 Michael Hanselmann
994 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
995 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
996 a8083063 Iustin Pop
997 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
998 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
999 a8083063 Iustin Pop
    self._WriteConfig()
1000 a8083063 Iustin Pop
1001 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1002 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1003 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1004 a8083063 Iustin Pop

1005 a8083063 Iustin Pop
    """
1006 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
1007 bcdf16d7 Guido Trotter
                                    self._config_data.nodes.keys(),
1008 bcdf16d7 Guido Trotter
                                    case_sensitive=False)
1009 a8083063 Iustin Pop
1010 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1011 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1012 a8083063 Iustin Pop

1013 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1014 c41eea6e Iustin Pop
    held.
1015 f78ede4e Guido Trotter

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

1018 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1019 c41eea6e Iustin Pop
    @return: the node object
1020 a8083063 Iustin Pop

1021 a8083063 Iustin Pop
    """
1022 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1023 a8083063 Iustin Pop
      return None
1024 a8083063 Iustin Pop
1025 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1026 a8083063 Iustin Pop
1027 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1028 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1029 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1030 f78ede4e Guido Trotter

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

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

1035 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1036 c41eea6e Iustin Pop
    @return: the node object
1037 f78ede4e Guido Trotter

1038 f78ede4e Guido Trotter
    """
1039 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1040 f78ede4e Guido Trotter
1041 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1042 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1043 a8083063 Iustin Pop

1044 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1045 c41eea6e Iustin Pop
    held.
1046 c41eea6e Iustin Pop

1047 c41eea6e Iustin Pop
    @rtype: list
1048 f78ede4e Guido Trotter

1049 a8083063 Iustin Pop
    """
1050 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1051 a8083063 Iustin Pop
1052 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1053 f78ede4e Guido Trotter
  def GetNodeList(self):
1054 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1055 f78ede4e Guido Trotter

1056 f78ede4e Guido Trotter
    """
1057 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1058 f78ede4e Guido Trotter
1059 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1060 94a02bb5 Iustin Pop
  def GetOnlineNodeList(self):
1061 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1062 94a02bb5 Iustin Pop

1063 94a02bb5 Iustin Pop
    """
1064 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1065 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1066 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1067 94a02bb5 Iustin Pop
1068 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1069 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1070 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1071 d65e5776 Iustin Pop

1072 d65e5776 Iustin Pop
    @rtype: dict
1073 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1074 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1075 d65e5776 Iustin Pop

1076 d65e5776 Iustin Pop
    """
1077 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
1078 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
1079 d65e5776 Iustin Pop
    return my_dict
1080 d65e5776 Iustin Pop
1081 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1082 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1083 ec0292f1 Iustin Pop

1084 23f06b2b Iustin Pop
    @type exceptions: list
1085 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1086 ec0292f1 Iustin Pop
    @rtype: tuple
1087 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1088 ec0292f1 Iustin Pop

1089 ec0292f1 Iustin Pop
    """
1090 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1091 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1092 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1093 23f06b2b Iustin Pop
        continue
1094 5bf07049 Iustin Pop
      if not (node.offline or node.drained):
1095 ec0292f1 Iustin Pop
        mc_max += 1
1096 ec0292f1 Iustin Pop
      if node.master_candidate:
1097 ec0292f1 Iustin Pop
        mc_now += 1
1098 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1099 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1100 ec0292f1 Iustin Pop
1101 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1102 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1103 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1104 ec0292f1 Iustin Pop

1105 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1106 ec0292f1 Iustin Pop

1107 23f06b2b Iustin Pop
    @type exceptions: list
1108 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1109 ec0292f1 Iustin Pop
    @rtype: tuple
1110 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1111 ec0292f1 Iustin Pop

1112 ec0292f1 Iustin Pop
    """
1113 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1114 ec0292f1 Iustin Pop
1115 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1116 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1117 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1118 ec0292f1 Iustin Pop

1119 44485f49 Guido Trotter
    @type exceptions: list
1120 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1121 ec0292f1 Iustin Pop
    @rtype: list
1122 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1123 ec0292f1 Iustin Pop

1124 ec0292f1 Iustin Pop
    """
1125 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1126 ec0292f1 Iustin Pop
    mod_list = []
1127 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1128 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1129 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1130 ec0292f1 Iustin Pop
      for name in node_list:
1131 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1132 ec0292f1 Iustin Pop
          break
1133 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1134 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1135 44485f49 Guido Trotter
            node.name in exceptions):
1136 ec0292f1 Iustin Pop
          continue
1137 ee513a66 Iustin Pop
        mod_list.append(node)
1138 ec0292f1 Iustin Pop
        node.master_candidate = True
1139 ec0292f1 Iustin Pop
        node.serial_no += 1
1140 ec0292f1 Iustin Pop
        mc_now += 1
1141 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1142 ec0292f1 Iustin Pop
        # this should not happen
1143 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1144 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1145 ec0292f1 Iustin Pop
      if mod_list:
1146 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1147 ec0292f1 Iustin Pop
        self._WriteConfig()
1148 ec0292f1 Iustin Pop
1149 ec0292f1 Iustin Pop
    return mod_list
1150 ec0292f1 Iustin Pop
1151 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1152 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1153 a8083063 Iustin Pop

1154 a8083063 Iustin Pop
    """
1155 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1156 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1157 a8083063 Iustin Pop
1158 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1159 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1160 76d5d3a3 Iustin Pop

1161 76d5d3a3 Iustin Pop
    """
1162 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1163 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1164 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1165 76d5d3a3 Iustin Pop
1166 a8083063 Iustin Pop
  def _OpenConfig(self):
1167 a8083063 Iustin Pop
    """Read the config data from disk.
1168 a8083063 Iustin Pop

1169 a8083063 Iustin Pop
    """
1170 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1171 13998ef2 Michael Hanselmann
1172 a8083063 Iustin Pop
    try:
1173 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1174 13998ef2 Michael Hanselmann
    except Exception, err:
1175 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1176 5b263ed7 Michael Hanselmann
1177 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1178 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1179 5b263ed7 Michael Hanselmann
1180 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1181 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1182 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1183 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1184 90d726a8 Iustin Pop
1185 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1186 90d726a8 Iustin Pop
    data.UpgradeConfig()
1187 90d726a8 Iustin Pop
1188 a8083063 Iustin Pop
    self._config_data = data
1189 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1190 0779e3aa Iustin Pop
    # ssconf update
1191 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1192 a8083063 Iustin Pop
1193 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1194 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1195 76d5d3a3 Iustin Pop
1196 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1197 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1198 76d5d3a3 Iustin Pop

1199 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1200 76d5d3a3 Iustin Pop
    whole configuration, etc.
1201 76d5d3a3 Iustin Pop

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

1206 76d5d3a3 Iustin Pop
    """
1207 76d5d3a3 Iustin Pop
    modified = False
1208 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1209 76d5d3a3 Iustin Pop
      if item.uuid is None:
1210 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
1211 76d5d3a3 Iustin Pop
        modified = True
1212 76d5d3a3 Iustin Pop
    if modified:
1213 76d5d3a3 Iustin Pop
      self._WriteConfig()
1214 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
1215 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
1216 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
1217 4fae38c5 Guido Trotter
1218 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
1219 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1220 a8083063 Iustin Pop

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

1224 a8083063 Iustin Pop
    """
1225 a8083063 Iustin Pop
    if self._offline:
1226 a8083063 Iustin Pop
      return True
1227 a4eae71f Michael Hanselmann
1228 a8083063 Iustin Pop
    bad = False
1229 a8083063 Iustin Pop
1230 6a5b8b4b Iustin Pop
    node_list = []
1231 6a5b8b4b Iustin Pop
    addr_list = []
1232 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1233 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1234 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1235 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1236 6b294c53 Iustin Pop
    # in between
1237 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1238 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1239 6a5b8b4b Iustin Pop
        continue
1240 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1241 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1242 6a5b8b4b Iustin Pop
        continue
1243 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1244 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1245 6b294c53 Iustin Pop
1246 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1247 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1248 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1249 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1250 1b54fc6c Guido Trotter
      if msg:
1251 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1252 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1253 1b54fc6c Guido Trotter
        logging.error(msg)
1254 a4eae71f Michael Hanselmann
1255 a4eae71f Michael Hanselmann
        if feedback_fn:
1256 a4eae71f Michael Hanselmann
          feedback_fn(msg)
1257 a4eae71f Michael Hanselmann
1258 a8083063 Iustin Pop
        bad = True
1259 a4eae71f Michael Hanselmann
1260 a8083063 Iustin Pop
    return not bad
1261 a8083063 Iustin Pop
1262 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
1263 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1264 a8083063 Iustin Pop

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

1314 054596f0 Iustin Pop
    @rtype: dict
1315 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1316 054596f0 Iustin Pop
        associated value
1317 054596f0 Iustin Pop

1318 054596f0 Iustin Pop
    """
1319 a3316e4a Iustin Pop
    fn = "\n".join
1320 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1321 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1322 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1323 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1324 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1325 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1326 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1327 a3316e4a Iustin Pop
1328 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1329 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1330 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1331 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1332 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1333 8113a52e Luca Bigliardi
                     if node.master_candidate)
1334 a3316e4a Iustin Pop
    node_data = fn(node_names)
1335 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1336 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1337 f56618e0 Iustin Pop
1338 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1339 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1340 03d1dba2 Michael Hanselmann
    return {
1341 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1342 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1343 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1344 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1345 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1346 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1347 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1348 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1349 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1350 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1351 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1352 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1353 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1354 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1355 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1356 03d1dba2 Michael Hanselmann
      }
1357 03d1dba2 Michael Hanselmann
1358 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1359 a8083063 Iustin Pop
  def GetVGName(self):
1360 a8083063 Iustin Pop
    """Return the volume group name.
1361 a8083063 Iustin Pop

1362 a8083063 Iustin Pop
    """
1363 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1364 a8083063 Iustin Pop
1365 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1366 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1367 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1368 89ff8e15 Manuel Franceschini

1369 89ff8e15 Manuel Franceschini
    """
1370 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1371 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1372 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1373 89ff8e15 Manuel Franceschini
1374 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1375 a8083063 Iustin Pop
  def GetMACPrefix(self):
1376 a8083063 Iustin Pop
    """Return the mac prefix.
1377 a8083063 Iustin Pop

1378 a8083063 Iustin Pop
    """
1379 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1380 62779dd0 Iustin Pop
1381 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1382 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1383 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1384 62779dd0 Iustin Pop

1385 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1386 c41eea6e Iustin Pop
    @return: the cluster object
1387 62779dd0 Iustin Pop

1388 62779dd0 Iustin Pop
    """
1389 62779dd0 Iustin Pop
    return self._config_data.cluster
1390 e00fb268 Iustin Pop
1391 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1392 a4eae71f Michael Hanselmann
  def Update(self, target, feedback_fn):
1393 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1394 e00fb268 Iustin Pop

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

1401 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1402 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1403 c41eea6e Iustin Pop
        the cluster
1404 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
1405 c41eea6e Iustin Pop

1406 e00fb268 Iustin Pop
    """
1407 e00fb268 Iustin Pop
    if self._config_data is None:
1408 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1409 3ecf6786 Iustin Pop
                                   " cannot save.")
1410 f34901f8 Iustin Pop
    update_serial = False
1411 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1412 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1413 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1414 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1415 f34901f8 Iustin Pop
      update_serial = True
1416 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1417 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1418 e00fb268 Iustin Pop
    else:
1419 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1420 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1421 e00fb268 Iustin Pop
    if not test:
1422 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1423 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1424 f34901f8 Iustin Pop
    target.serial_no += 1
1425 d693c864 Iustin Pop
    target.mtime = now = time.time()
1426 f34901f8 Iustin Pop
1427 cff4c037 Iustin Pop
    if update_serial:
1428 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1429 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1430 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1431 b989e85d Iustin Pop
1432 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1433 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1434 61cf6b5e Iustin Pop
1435 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
1436 73064714 Guido Trotter
1437 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
1438 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
1439 73064714 Guido Trotter
    """Drop per-execution-context reservations
1440 73064714 Guido Trotter

1441 73064714 Guido Trotter
    """
1442 4fae38c5 Guido Trotter
    self._temporary_ids.DropECReservations(ec_id)
1443 36b66e6e Guido Trotter
    self._temporary_macs.DropECReservations(ec_id)
1444 afa1386e Guido Trotter
    self._temporary_secrets.DropECReservations(ec_id)