Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 34e54ebc

History | View | Annotate | Download (41.1 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 tempfile
36 a8083063 Iustin Pop
import random
37 d8470559 Michael Hanselmann
import logging
38 d693c864 Iustin Pop
import time
39 a8083063 Iustin Pop
40 a8083063 Iustin Pop
from ganeti import errors
41 f78ede4e Guido Trotter
from ganeti import locking
42 a8083063 Iustin Pop
from ganeti import utils
43 a8083063 Iustin Pop
from ganeti import constants
44 a8083063 Iustin Pop
from ganeti import rpc
45 a8083063 Iustin Pop
from ganeti import objects
46 8d14b30d Iustin Pop
from ganeti import serializer
47 243cdbcc Michael Hanselmann
48 243cdbcc Michael Hanselmann
49 f78ede4e Guido Trotter
_config_lock = locking.SharedLock()
50 f78ede4e Guido Trotter
51 f78ede4e Guido Trotter
52 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
53 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
54 c41eea6e Iustin Pop

55 c41eea6e Iustin Pop
  This only verifies the version of the configuration.
56 c41eea6e Iustin Pop

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

60 c41eea6e Iustin Pop
  """
61 5b263ed7 Michael Hanselmann
  if data.version != constants.CONFIG_VERSION:
62 243cdbcc Michael Hanselmann
    raise errors.ConfigurationError("Cluster configuration version"
63 243cdbcc Michael Hanselmann
                                    " mismatch, got %s instead of %s" %
64 5b263ed7 Michael Hanselmann
                                    (data.version,
65 243cdbcc Michael Hanselmann
                                     constants.CONFIG_VERSION))
66 a8083063 Iustin Pop
67 319856a9 Michael Hanselmann
68 a8083063 Iustin Pop
class ConfigWriter:
69 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
70 a8083063 Iustin Pop

71 098c0958 Michael Hanselmann
  """
72 a8083063 Iustin Pop
  def __init__(self, cfg_file=None, offline=False):
73 14e15659 Iustin Pop
    self.write_count = 0
74 f78ede4e Guido Trotter
    self._lock = _config_lock
75 a8083063 Iustin Pop
    self._config_data = None
76 a8083063 Iustin Pop
    self._offline = offline
77 a8083063 Iustin Pop
    if cfg_file is None:
78 a8083063 Iustin Pop
      self._cfg_file = constants.CLUSTER_CONF_FILE
79 a8083063 Iustin Pop
    else:
80 a8083063 Iustin Pop
      self._cfg_file = cfg_file
81 923b1523 Iustin Pop
    self._temporary_ids = set()
82 a81c53c9 Iustin Pop
    self._temporary_drbds = {}
83 e7d81ba0 Iustin Pop
    self._temporary_macs = set()
84 89e1fc26 Iustin Pop
    # Note: in order to prevent errors when resolving our name in
85 89e1fc26 Iustin Pop
    # _DistributeConfig, we compute it here once and reuse it; it's
86 89e1fc26 Iustin Pop
    # better to raise an error before starting to modify the config
87 89e1fc26 Iustin Pop
    # file than after it was modified
88 89e1fc26 Iustin Pop
    self._my_hostname = utils.HostInfo().name
89 3c7f6c44 Iustin Pop
    self._last_cluster_serial = -1
90 3d3a04bc Iustin Pop
    self._OpenConfig()
91 a8083063 Iustin Pop
92 a8083063 Iustin Pop
  # this method needs to be static, so that we can call it on the class
93 a8083063 Iustin Pop
  @staticmethod
94 a8083063 Iustin Pop
  def IsCluster():
95 a8083063 Iustin Pop
    """Check if the cluster is configured.
96 a8083063 Iustin Pop

97 a8083063 Iustin Pop
    """
98 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
99 a8083063 Iustin Pop
100 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
101 a8083063 Iustin Pop
  def GenerateMAC(self):
102 a8083063 Iustin Pop
    """Generate a MAC for an instance.
103 a8083063 Iustin Pop

104 a8083063 Iustin Pop
    This should check the current instances for duplicates.
105 a8083063 Iustin Pop

106 a8083063 Iustin Pop
    """
107 a8083063 Iustin Pop
    prefix = self._config_data.cluster.mac_prefix
108 a8083063 Iustin Pop
    all_macs = self._AllMACs()
109 a8083063 Iustin Pop
    retries = 64
110 a8083063 Iustin Pop
    while retries > 0:
111 a8083063 Iustin Pop
      byte1 = random.randrange(0, 256)
112 a8083063 Iustin Pop
      byte2 = random.randrange(0, 256)
113 a8083063 Iustin Pop
      byte3 = random.randrange(0, 256)
114 a8083063 Iustin Pop
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
115 e7d81ba0 Iustin Pop
      if mac not in all_macs and mac not in self._temporary_macs:
116 a8083063 Iustin Pop
        break
117 a8083063 Iustin Pop
      retries -= 1
118 a8083063 Iustin Pop
    else:
119 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't generate unique MAC")
120 e7d81ba0 Iustin Pop
    self._temporary_macs.add(mac)
121 a8083063 Iustin Pop
    return mac
122 a8083063 Iustin Pop
123 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
124 1862d460 Alexander Schreiber
  def IsMacInUse(self, mac):
125 1862d460 Alexander Schreiber
    """Predicate: check if the specified MAC is in use in the Ganeti cluster.
126 1862d460 Alexander Schreiber

127 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
128 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
129 1862d460 Alexander Schreiber

130 1862d460 Alexander Schreiber
    """
131 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
132 e7d81ba0 Iustin Pop
    return mac in all_macs or mac in self._temporary_macs
133 1862d460 Alexander Schreiber
134 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
135 f9518d38 Iustin Pop
  def GenerateDRBDSecret(self):
136 f9518d38 Iustin Pop
    """Generate a DRBD secret.
137 f9518d38 Iustin Pop

138 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
139 f9518d38 Iustin Pop

140 f9518d38 Iustin Pop
    """
141 f9518d38 Iustin Pop
    all_secrets = self._AllDRBDSecrets()
142 f9518d38 Iustin Pop
    retries = 64
143 f9518d38 Iustin Pop
    while retries > 0:
144 f9518d38 Iustin Pop
      secret = utils.GenerateSecret()
145 f9518d38 Iustin Pop
      if secret not in all_secrets:
146 f9518d38 Iustin Pop
        break
147 f9518d38 Iustin Pop
      retries -= 1
148 f9518d38 Iustin Pop
    else:
149 f9518d38 Iustin Pop
      raise errors.ConfigurationError("Can't generate unique DRBD secret")
150 f9518d38 Iustin Pop
    return secret
151 f9518d38 Iustin Pop
152 34e54ebc Iustin Pop
  def _AllLVs(self):
153 923b1523 Iustin Pop
    """Compute the list of all LVs.
154 923b1523 Iustin Pop

155 923b1523 Iustin Pop
    """
156 923b1523 Iustin Pop
    lvnames = set()
157 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
158 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
159 923b1523 Iustin Pop
      for lv_list in node_data.values():
160 923b1523 Iustin Pop
        lvnames.update(lv_list)
161 923b1523 Iustin Pop
    return lvnames
162 923b1523 Iustin Pop
163 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
164 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
165 34e54ebc Iustin Pop

166 34e54ebc Iustin Pop
    @type include_temporary: boolean
167 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
168 34e54ebc Iustin Pop
    @rtype: set
169 34e54ebc Iustin Pop
    @return: a set of IDs
170 34e54ebc Iustin Pop

171 34e54ebc Iustin Pop
    """
172 34e54ebc Iustin Pop
    existing = set()
173 34e54ebc Iustin Pop
    if include_temporary:
174 34e54ebc Iustin Pop
      existing.update(self._temporary_ids)
175 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
176 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
177 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
178 34e54ebc Iustin Pop
    return existing
179 34e54ebc Iustin Pop
180 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
181 923b1523 Iustin Pop
  def GenerateUniqueID(self, exceptions=None):
182 923b1523 Iustin Pop
    """Generate an unique disk name.
183 923b1523 Iustin Pop

184 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
185 923b1523 Iustin Pop
    duplicates.
186 923b1523 Iustin Pop

187 c41eea6e Iustin Pop
    @param exceptions: a list with some other names which should be checked
188 c41eea6e Iustin Pop
        for uniqueness (used for example when you want to get
189 c41eea6e Iustin Pop
        more than one id at one time without adding each one in
190 c41eea6e Iustin Pop
        turn to the config file)
191 923b1523 Iustin Pop

192 c41eea6e Iustin Pop
    @rtype: string
193 c41eea6e Iustin Pop
    @return: the unique id
194 923b1523 Iustin Pop

195 923b1523 Iustin Pop
    """
196 34e54ebc Iustin Pop
    existing = self._AllIDs(include_temporary=True)
197 923b1523 Iustin Pop
    if exceptions is not None:
198 923b1523 Iustin Pop
      existing.update(exceptions)
199 923b1523 Iustin Pop
    retries = 64
200 923b1523 Iustin Pop
    while retries > 0:
201 24818e8f Michael Hanselmann
      unique_id = utils.NewUUID()
202 923b1523 Iustin Pop
      if unique_id not in existing and unique_id is not None:
203 923b1523 Iustin Pop
        break
204 923b1523 Iustin Pop
    else:
205 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Not able generate an unique ID"
206 3ecf6786 Iustin Pop
                                      " (last tried ID: %s" % unique_id)
207 923b1523 Iustin Pop
    self._temporary_ids.add(unique_id)
208 923b1523 Iustin Pop
    return unique_id
209 923b1523 Iustin Pop
210 a8083063 Iustin Pop
  def _AllMACs(self):
211 a8083063 Iustin Pop
    """Return all MACs present in the config.
212 a8083063 Iustin Pop

213 c41eea6e Iustin Pop
    @rtype: list
214 c41eea6e Iustin Pop
    @return: the list of all MACs
215 c41eea6e Iustin Pop

216 a8083063 Iustin Pop
    """
217 a8083063 Iustin Pop
    result = []
218 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
219 a8083063 Iustin Pop
      for nic in instance.nics:
220 a8083063 Iustin Pop
        result.append(nic.mac)
221 a8083063 Iustin Pop
222 a8083063 Iustin Pop
    return result
223 a8083063 Iustin Pop
224 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
225 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
226 f9518d38 Iustin Pop

227 c41eea6e Iustin Pop
    @rtype: list
228 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
229 c41eea6e Iustin Pop

230 f9518d38 Iustin Pop
    """
231 f9518d38 Iustin Pop
    def helper(disk, result):
232 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
233 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
234 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
235 f9518d38 Iustin Pop
      if disk.children:
236 f9518d38 Iustin Pop
        for child in disk.children:
237 f9518d38 Iustin Pop
          helper(child, result)
238 f9518d38 Iustin Pop
239 f9518d38 Iustin Pop
    result = []
240 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
241 f9518d38 Iustin Pop
      for disk in instance.disks:
242 f9518d38 Iustin Pop
        helper(disk, result)
243 f9518d38 Iustin Pop
244 f9518d38 Iustin Pop
    return result
245 f9518d38 Iustin Pop
246 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
247 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
248 4b98ac29 Iustin Pop

249 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
250 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
251 4b98ac29 Iustin Pop
    @type l_ids: list
252 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
253 4b98ac29 Iustin Pop
    @type p_ids: list
254 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
255 4b98ac29 Iustin Pop
    @rtype: list
256 4b98ac29 Iustin Pop
    @return: a list of error messages
257 4b98ac29 Iustin Pop

258 4b98ac29 Iustin Pop
    """
259 4b98ac29 Iustin Pop
    result = []
260 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
261 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
262 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
263 25ae22e4 Iustin Pop
      else:
264 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
265 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
266 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
267 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
268 25ae22e4 Iustin Pop
      else:
269 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
270 4b98ac29 Iustin Pop
271 4b98ac29 Iustin Pop
    if disk.children:
272 4b98ac29 Iustin Pop
      for child in disk.children:
273 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
274 4b98ac29 Iustin Pop
    return result
275 4b98ac29 Iustin Pop
276 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
277 a8efbb40 Iustin Pop
    """Verify function.
278 a8efbb40 Iustin Pop

279 4a89c54a Iustin Pop
    @rtype: list
280 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
281 4a89c54a Iustin Pop
        configuration errors
282 4a89c54a Iustin Pop

283 a8083063 Iustin Pop
    """
284 a8083063 Iustin Pop
    result = []
285 a8083063 Iustin Pop
    seen_macs = []
286 48ce9fd9 Iustin Pop
    ports = {}
287 a8083063 Iustin Pop
    data = self._config_data
288 4b98ac29 Iustin Pop
    seen_lids = []
289 4b98ac29 Iustin Pop
    seen_pids = []
290 9a5fba23 Guido Trotter
291 9a5fba23 Guido Trotter
    # global cluster checks
292 9a5fba23 Guido Trotter
    if not data.cluster.enabled_hypervisors:
293 9a5fba23 Guido Trotter
      result.append("enabled hypervisors list doesn't have any entries")
294 9a5fba23 Guido Trotter
    invalid_hvs = set(data.cluster.enabled_hypervisors) - constants.HYPER_TYPES
295 9a5fba23 Guido Trotter
    if invalid_hvs:
296 9a5fba23 Guido Trotter
      result.append("enabled hypervisors contains invalid entries: %s" %
297 9a5fba23 Guido Trotter
                    invalid_hvs)
298 9a5fba23 Guido Trotter
299 9a5fba23 Guido Trotter
    if data.cluster.master_node not in data.nodes:
300 9a5fba23 Guido Trotter
      result.append("cluster has invalid primary node '%s'" %
301 9a5fba23 Guido Trotter
                    data.cluster.master_node)
302 9a5fba23 Guido Trotter
303 9a5fba23 Guido Trotter
    # per-instance checks
304 a8083063 Iustin Pop
    for instance_name in data.instances:
305 a8083063 Iustin Pop
      instance = data.instances[instance_name]
306 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
307 8522ceeb Iustin Pop
        result.append("instance '%s' has invalid primary node '%s'" %
308 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
309 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
310 a8083063 Iustin Pop
        if snode not in data.nodes:
311 8522ceeb Iustin Pop
          result.append("instance '%s' has invalid secondary node '%s'" %
312 a8083063 Iustin Pop
                        (instance_name, snode))
313 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
314 a8083063 Iustin Pop
        if nic.mac in seen_macs:
315 8522ceeb Iustin Pop
          result.append("instance '%s' has NIC %d mac %s duplicate" %
316 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
317 a8083063 Iustin Pop
        else:
318 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
319 48ce9fd9 Iustin Pop
320 48ce9fd9 Iustin Pop
      # gather the drbd ports for duplicate checks
321 48ce9fd9 Iustin Pop
      for dsk in instance.disks:
322 48ce9fd9 Iustin Pop
        if dsk.dev_type in constants.LDS_DRBD:
323 48ce9fd9 Iustin Pop
          tcp_port = dsk.logical_id[2]
324 48ce9fd9 Iustin Pop
          if tcp_port not in ports:
325 48ce9fd9 Iustin Pop
            ports[tcp_port] = []
326 48ce9fd9 Iustin Pop
          ports[tcp_port].append((instance.name, "drbd disk %s" % dsk.iv_name))
327 48ce9fd9 Iustin Pop
      # gather network port reservation
328 48ce9fd9 Iustin Pop
      net_port = getattr(instance, "network_port", None)
329 48ce9fd9 Iustin Pop
      if net_port is not None:
330 48ce9fd9 Iustin Pop
        if net_port not in ports:
331 48ce9fd9 Iustin Pop
          ports[net_port] = []
332 48ce9fd9 Iustin Pop
        ports[net_port].append((instance.name, "network port"))
333 48ce9fd9 Iustin Pop
334 332d0e37 Iustin Pop
      # instance disk verify
335 332d0e37 Iustin Pop
      for idx, disk in enumerate(instance.disks):
336 332d0e37 Iustin Pop
        result.extend(["instance '%s' disk %d error: %s" %
337 332d0e37 Iustin Pop
                       (instance.name, idx, msg) for msg in disk.Verify()])
338 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(disk, seen_lids, seen_pids))
339 332d0e37 Iustin Pop
340 48ce9fd9 Iustin Pop
    # cluster-wide pool of free ports
341 a8efbb40 Iustin Pop
    for free_port in data.cluster.tcpudp_port_pool:
342 48ce9fd9 Iustin Pop
      if free_port not in ports:
343 48ce9fd9 Iustin Pop
        ports[free_port] = []
344 48ce9fd9 Iustin Pop
      ports[free_port].append(("cluster", "port marked as free"))
345 48ce9fd9 Iustin Pop
346 48ce9fd9 Iustin Pop
    # compute tcp/udp duplicate ports
347 48ce9fd9 Iustin Pop
    keys = ports.keys()
348 48ce9fd9 Iustin Pop
    keys.sort()
349 48ce9fd9 Iustin Pop
    for pnum in keys:
350 48ce9fd9 Iustin Pop
      pdata = ports[pnum]
351 48ce9fd9 Iustin Pop
      if len(pdata) > 1:
352 48ce9fd9 Iustin Pop
        txt = ", ".join(["%s/%s" % val for val in pdata])
353 48ce9fd9 Iustin Pop
        result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
354 48ce9fd9 Iustin Pop
355 48ce9fd9 Iustin Pop
    # highest used tcp port check
356 48ce9fd9 Iustin Pop
    if keys:
357 a8efbb40 Iustin Pop
      if keys[-1] > data.cluster.highest_used_port:
358 48ce9fd9 Iustin Pop
        result.append("Highest used port mismatch, saved %s, computed %s" %
359 a8efbb40 Iustin Pop
                      (data.cluster.highest_used_port, keys[-1]))
360 a8efbb40 Iustin Pop
361 3a26773f Iustin Pop
    if not data.nodes[data.cluster.master_node].master_candidate:
362 3a26773f Iustin Pop
      result.append("Master node is not a master candidate")
363 3a26773f Iustin Pop
364 4a89c54a Iustin Pop
    # master candidate checks
365 ec0292f1 Iustin Pop
    mc_now, mc_max = self._UnlockedGetMasterCandidateStats()
366 ec0292f1 Iustin Pop
    if mc_now < mc_max:
367 ec0292f1 Iustin Pop
      result.append("Not enough master candidates: actual %d, target %d" %
368 ec0292f1 Iustin Pop
                    (mc_now, mc_max))
369 48ce9fd9 Iustin Pop
370 5bf07049 Iustin Pop
    # node checks
371 5bf07049 Iustin Pop
    for node in data.nodes.values():
372 5bf07049 Iustin Pop
      if [node.master_candidate, node.drained, node.offline].count(True) > 1:
373 5bf07049 Iustin Pop
        result.append("Node %s state is invalid: master_candidate=%s,"
374 5bf07049 Iustin Pop
                      " drain=%s, offline=%s" %
375 5bf07049 Iustin Pop
                      (node.name, node.master_candidate, node.drain,
376 5bf07049 Iustin Pop
                       node.offline))
377 5bf07049 Iustin Pop
378 4a89c54a Iustin Pop
    # drbd minors check
379 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
380 4a89c54a Iustin Pop
    for node, minor, instance_a, instance_b in duplicates:
381 4a89c54a Iustin Pop
      result.append("DRBD minor %d on node %s is assigned twice to instances"
382 4a89c54a Iustin Pop
                    " %s and %s" % (minor, node, instance_a, instance_b))
383 4a89c54a Iustin Pop
384 a8083063 Iustin Pop
    return result
385 a8083063 Iustin Pop
386 4a89c54a Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
387 4a89c54a Iustin Pop
  def VerifyConfig(self):
388 4a89c54a Iustin Pop
    """Verify function.
389 4a89c54a Iustin Pop

390 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
391 4a89c54a Iustin Pop

392 4a89c54a Iustin Pop
    @rtype: list
393 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
394 4a89c54a Iustin Pop
        configuration errors
395 4a89c54a Iustin Pop

396 4a89c54a Iustin Pop
    """
397 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
398 4a89c54a Iustin Pop
399 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
400 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
401 a8083063 Iustin Pop

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

404 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
405 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
406 a8083063 Iustin Pop
    node.
407 a8083063 Iustin Pop

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

410 a8083063 Iustin Pop
    """
411 a8083063 Iustin Pop
    if disk.children:
412 a8083063 Iustin Pop
      for child in disk.children:
413 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
414 a8083063 Iustin Pop
415 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
416 a8083063 Iustin Pop
      return
417 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
418 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
419 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
420 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
421 3ecf6786 Iustin Pop
                                        node_name)
422 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
423 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
424 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
425 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
426 a8083063 Iustin Pop
                                        " for %s" % str(disk))
427 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
428 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
429 a8083063 Iustin Pop
      if pnode == node_name:
430 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
431 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
432 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
433 a8083063 Iustin Pop
    else:
434 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
435 a8083063 Iustin Pop
    return
436 a8083063 Iustin Pop
437 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
438 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
439 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
440 f78ede4e Guido Trotter

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

443 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
444 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
445 f78ede4e Guido Trotter
    node.
446 f78ede4e Guido Trotter

447 f78ede4e Guido Trotter
    """
448 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
449 f78ede4e Guido Trotter
450 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
451 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
452 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
453 b2fddf63 Iustin Pop

454 b2fddf63 Iustin Pop
    """
455 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
456 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
457 264bb3c5 Michael Hanselmann
458 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
459 264bb3c5 Michael Hanselmann
    self._WriteConfig()
460 264bb3c5 Michael Hanselmann
461 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
462 b2fddf63 Iustin Pop
  def GetPortList(self):
463 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
464 264bb3c5 Michael Hanselmann

465 264bb3c5 Michael Hanselmann
    """
466 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
467 264bb3c5 Michael Hanselmann
468 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
469 a8083063 Iustin Pop
  def AllocatePort(self):
470 a8083063 Iustin Pop
    """Allocate a port.
471 a8083063 Iustin Pop

472 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
473 b2fddf63 Iustin Pop
    default port range (and in this case we increase
474 b2fddf63 Iustin Pop
    highest_used_port).
475 a8083063 Iustin Pop

476 a8083063 Iustin Pop
    """
477 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
478 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
479 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
480 264bb3c5 Michael Hanselmann
    else:
481 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
482 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
483 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
484 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
485 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
486 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
487 a8083063 Iustin Pop
488 a8083063 Iustin Pop
    self._WriteConfig()
489 a8083063 Iustin Pop
    return port
490 a8083063 Iustin Pop
491 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
492 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
493 a81c53c9 Iustin Pop

494 4a89c54a Iustin Pop
    @rtype: (dict, list)
495 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
496 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
497 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
498 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
499 4a89c54a Iustin Pop
        should raise an exception
500 a81c53c9 Iustin Pop

501 a81c53c9 Iustin Pop
    """
502 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
503 4a89c54a Iustin Pop
      duplicates = []
504 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
505 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
506 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
507 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
508 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
509 a81c53c9 Iustin Pop
          if port in used[node]:
510 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
511 4a89c54a Iustin Pop
          else:
512 4a89c54a Iustin Pop
            used[node][port] = instance_name
513 a81c53c9 Iustin Pop
      if disk.children:
514 a81c53c9 Iustin Pop
        for child in disk.children:
515 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
516 4a89c54a Iustin Pop
      return duplicates
517 a81c53c9 Iustin Pop
518 4a89c54a Iustin Pop
    duplicates = []
519 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
520 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
521 79b26a7a Iustin Pop
      for disk in instance.disks:
522 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
523 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
524 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
525 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
526 4a89c54a Iustin Pop
      else:
527 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
528 4a89c54a Iustin Pop
    return my_dict, duplicates
529 a81c53c9 Iustin Pop
530 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
531 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
532 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
533 6d2e83d5 Iustin Pop

534 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
535 6d2e83d5 Iustin Pop

536 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
537 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
538 6d2e83d5 Iustin Pop
        an empty list).
539 6d2e83d5 Iustin Pop

540 6d2e83d5 Iustin Pop
    """
541 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
542 4a89c54a Iustin Pop
    if duplicates:
543 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
544 4a89c54a Iustin Pop
                                      str(duplicates))
545 4a89c54a Iustin Pop
    return d_map
546 6d2e83d5 Iustin Pop
547 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
548 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
549 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
550 a81c53c9 Iustin Pop

551 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
552 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
553 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
554 a81c53c9 Iustin Pop
    order as the passed nodes.
555 a81c53c9 Iustin Pop

556 32388e6d Iustin Pop
    @type instance: string
557 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
558 32388e6d Iustin Pop

559 a81c53c9 Iustin Pop
    """
560 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
561 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
562 32388e6d Iustin Pop
563 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
564 4a89c54a Iustin Pop
    if duplicates:
565 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
566 4a89c54a Iustin Pop
                                      str(duplicates))
567 a81c53c9 Iustin Pop
    result = []
568 a81c53c9 Iustin Pop
    for nname in nodes:
569 a81c53c9 Iustin Pop
      ndata = d_map[nname]
570 a81c53c9 Iustin Pop
      if not ndata:
571 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
572 a81c53c9 Iustin Pop
        result.append(0)
573 a81c53c9 Iustin Pop
        ndata[0] = instance
574 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
575 a81c53c9 Iustin Pop
        continue
576 a81c53c9 Iustin Pop
      keys = ndata.keys()
577 a81c53c9 Iustin Pop
      keys.sort()
578 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
579 a81c53c9 Iustin Pop
      if ffree is None:
580 a81c53c9 Iustin Pop
        # return the next minor
581 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
582 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
583 a81c53c9 Iustin Pop
      else:
584 a81c53c9 Iustin Pop
        minor = ffree
585 4a89c54a Iustin Pop
      # double-check minor against current instances
586 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
587 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
588 4a89c54a Iustin Pop
              " already allocated to instance %s" %
589 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
590 a81c53c9 Iustin Pop
      ndata[minor] = instance
591 4a89c54a Iustin Pop
      # double-check minor against reservation
592 4a89c54a Iustin Pop
      r_key = (nname, minor)
593 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
594 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
595 4a89c54a Iustin Pop
              " reserved for instance %s" %
596 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
597 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
598 4a89c54a Iustin Pop
      result.append(minor)
599 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
600 a81c53c9 Iustin Pop
                  nodes, result)
601 a81c53c9 Iustin Pop
    return result
602 a81c53c9 Iustin Pop
603 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
604 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
605 a81c53c9 Iustin Pop

606 a81c53c9 Iustin Pop
    @type instance: string
607 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
608 a81c53c9 Iustin Pop
                     released
609 a81c53c9 Iustin Pop

610 a81c53c9 Iustin Pop
    """
611 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
612 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
613 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
614 a81c53c9 Iustin Pop
      if name == instance:
615 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
616 a81c53c9 Iustin Pop
617 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
618 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
619 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
620 61cf6b5e Iustin Pop

621 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
622 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
623 61cf6b5e Iustin Pop
    functions.
624 61cf6b5e Iustin Pop

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

627 61cf6b5e Iustin Pop
    @type instance: string
628 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
629 61cf6b5e Iustin Pop
                     released
630 61cf6b5e Iustin Pop

631 61cf6b5e Iustin Pop
    """
632 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
633 61cf6b5e Iustin Pop
634 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
635 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
636 4a8b186a Michael Hanselmann
    """Get the configuration version.
637 4a8b186a Michael Hanselmann

638 4a8b186a Michael Hanselmann
    @return: Config version
639 4a8b186a Michael Hanselmann

640 4a8b186a Michael Hanselmann
    """
641 4a8b186a Michael Hanselmann
    return self._config_data.version
642 4a8b186a Michael Hanselmann
643 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
644 4a8b186a Michael Hanselmann
  def GetClusterName(self):
645 4a8b186a Michael Hanselmann
    """Get cluster name.
646 4a8b186a Michael Hanselmann

647 4a8b186a Michael Hanselmann
    @return: Cluster name
648 4a8b186a Michael Hanselmann

649 4a8b186a Michael Hanselmann
    """
650 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
651 4a8b186a Michael Hanselmann
652 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
653 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
654 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
655 4a8b186a Michael Hanselmann

656 4a8b186a Michael Hanselmann
    @return: Master hostname
657 4a8b186a Michael Hanselmann

658 4a8b186a Michael Hanselmann
    """
659 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
660 4a8b186a Michael Hanselmann
661 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
662 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
663 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
664 4a8b186a Michael Hanselmann

665 4a8b186a Michael Hanselmann
    @return: Master IP
666 4a8b186a Michael Hanselmann

667 4a8b186a Michael Hanselmann
    """
668 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
669 4a8b186a Michael Hanselmann
670 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
671 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
672 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
673 4a8b186a Michael Hanselmann

674 4a8b186a Michael Hanselmann
    """
675 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
676 4a8b186a Michael Hanselmann
677 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
678 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
679 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
680 4a8b186a Michael Hanselmann

681 4a8b186a Michael Hanselmann
    """
682 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
683 4a8b186a Michael Hanselmann
684 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
685 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
686 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
687 4a8b186a Michael Hanselmann

688 4a8b186a Michael Hanselmann
    """
689 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
690 4a8b186a Michael Hanselmann
691 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
692 a8083063 Iustin Pop
  def GetHostKey(self):
693 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
694 a8083063 Iustin Pop

695 c41eea6e Iustin Pop
    @rtype: string
696 c41eea6e Iustin Pop
    @return: the rsa hostkey
697 a8083063 Iustin Pop

698 a8083063 Iustin Pop
    """
699 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
700 a8083063 Iustin Pop
701 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
702 a8083063 Iustin Pop
  def AddInstance(self, instance):
703 a8083063 Iustin Pop
    """Add an instance to the config.
704 a8083063 Iustin Pop

705 a8083063 Iustin Pop
    This should be used after creating a new instance.
706 a8083063 Iustin Pop

707 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
708 c41eea6e Iustin Pop
    @param instance: the instance object
709 c41eea6e Iustin Pop

710 a8083063 Iustin Pop
    """
711 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
712 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
713 a8083063 Iustin Pop
714 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
715 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
716 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
717 923b1523 Iustin Pop
718 e4640214 Guido Trotter
    all_macs = self._AllMACs()
719 e4640214 Guido Trotter
    for nic in instance.nics:
720 e4640214 Guido Trotter
      if nic.mac in all_macs:
721 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
722 e4640214 Guido Trotter
          " MAC address '%s' already in use." % (instance.name, nic.mac))
723 e4640214 Guido Trotter
724 b989e85d Iustin Pop
    instance.serial_no = 1
725 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
726 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
727 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
728 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
729 e7d81ba0 Iustin Pop
    for nic in instance.nics:
730 e7d81ba0 Iustin Pop
      self._temporary_macs.discard(nic.mac)
731 a8083063 Iustin Pop
    self._WriteConfig()
732 a8083063 Iustin Pop
733 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
734 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
735 a8083063 Iustin Pop

736 a8083063 Iustin Pop
    """
737 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
738 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
739 a8083063 Iustin Pop
740 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
741 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
742 3ecf6786 Iustin Pop
                                      instance_name)
743 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
744 0d68c45d Iustin Pop
    if instance.admin_up != status:
745 0d68c45d Iustin Pop
      instance.admin_up = status
746 b989e85d Iustin Pop
      instance.serial_no += 1
747 d693c864 Iustin Pop
      instance.mtime = time.time()
748 455a3445 Iustin Pop
      self._WriteConfig()
749 a8083063 Iustin Pop
750 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
751 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
752 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
753 6a408fb2 Iustin Pop

754 6a408fb2 Iustin Pop
    """
755 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
756 6a408fb2 Iustin Pop
757 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
758 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
759 a8083063 Iustin Pop
    """Remove the instance from the configuration.
760 a8083063 Iustin Pop

761 a8083063 Iustin Pop
    """
762 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
763 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
764 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
765 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
766 a8083063 Iustin Pop
    self._WriteConfig()
767 a8083063 Iustin Pop
768 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
769 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
770 fc95f88f Iustin Pop
    """Rename an instance.
771 fc95f88f Iustin Pop

772 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
773 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
774 fc95f88f Iustin Pop
    rename.
775 fc95f88f Iustin Pop

776 fc95f88f Iustin Pop
    """
777 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
778 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
779 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
780 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
781 fc95f88f Iustin Pop
    inst.name = new_name
782 b23c4333 Manuel Franceschini
783 b23c4333 Manuel Franceschini
    for disk in inst.disks:
784 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
785 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
786 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
787 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
788 b23c4333 Manuel Franceschini
                                              os.path.join(file_storage_dir,
789 b23c4333 Manuel Franceschini
                                                           inst.name,
790 b23c4333 Manuel Franceschini
                                                           disk.iv_name))
791 b23c4333 Manuel Franceschini
792 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
793 fc95f88f Iustin Pop
    self._WriteConfig()
794 fc95f88f Iustin Pop
795 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
796 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
797 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
798 a8083063 Iustin Pop

799 a8083063 Iustin Pop
    """
800 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
801 a8083063 Iustin Pop
802 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
803 94bbfece Iustin Pop
    """Get the list of instances.
804 94bbfece Iustin Pop

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

807 94bbfece Iustin Pop
    """
808 94bbfece Iustin Pop
    return self._config_data.instances.keys()
809 94bbfece Iustin Pop
810 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
811 a8083063 Iustin Pop
  def GetInstanceList(self):
812 a8083063 Iustin Pop
    """Get the list of instances.
813 a8083063 Iustin Pop

814 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
815 c41eea6e Iustin Pop
        'instance1.example.com']
816 a8083063 Iustin Pop

817 a8083063 Iustin Pop
    """
818 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
819 a8083063 Iustin Pop
820 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
821 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
822 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
823 a8083063 Iustin Pop

824 a8083063 Iustin Pop
    """
825 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
826 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
827 a8083063 Iustin Pop
828 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
829 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
830 94bbfece Iustin Pop

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

833 94bbfece Iustin Pop
    """
834 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
835 94bbfece Iustin Pop
      return None
836 94bbfece Iustin Pop
837 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
838 94bbfece Iustin Pop
839 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
840 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
841 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
842 a8083063 Iustin Pop

843 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
844 a8083063 Iustin Pop
    an instance are taken from the live systems.
845 a8083063 Iustin Pop

846 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
847 c41eea6e Iustin Pop
        I{instance1.example.com}
848 a8083063 Iustin Pop

849 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
850 c41eea6e Iustin Pop
    @return: the instance object
851 a8083063 Iustin Pop

852 a8083063 Iustin Pop
    """
853 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
854 a8083063 Iustin Pop
855 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
856 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
857 0b2de758 Iustin Pop
    """Get the configuration of all instances.
858 0b2de758 Iustin Pop

859 0b2de758 Iustin Pop
    @rtype: dict
860 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
861 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
862 0b2de758 Iustin Pop

863 0b2de758 Iustin Pop
    """
864 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
865 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
866 0b2de758 Iustin Pop
    return my_dict
867 0b2de758 Iustin Pop
868 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
869 a8083063 Iustin Pop
  def AddNode(self, node):
870 a8083063 Iustin Pop
    """Add a node to the configuration.
871 a8083063 Iustin Pop

872 c41eea6e Iustin Pop
    @type node: L{objects.Node}
873 c41eea6e Iustin Pop
    @param node: a Node instance
874 a8083063 Iustin Pop

875 a8083063 Iustin Pop
    """
876 d8470559 Michael Hanselmann
    logging.info("Adding node %s to configuration" % node.name)
877 d8470559 Michael Hanselmann
878 b989e85d Iustin Pop
    node.serial_no = 1
879 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
880 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
881 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
882 a8083063 Iustin Pop
    self._WriteConfig()
883 a8083063 Iustin Pop
884 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
885 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
886 a8083063 Iustin Pop
    """Remove a node from the configuration.
887 a8083063 Iustin Pop

888 a8083063 Iustin Pop
    """
889 d8470559 Michael Hanselmann
    logging.info("Removing node %s from configuration" % node_name)
890 d8470559 Michael Hanselmann
891 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
892 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
893 a8083063 Iustin Pop
894 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
895 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
896 a8083063 Iustin Pop
    self._WriteConfig()
897 a8083063 Iustin Pop
898 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
899 a8083063 Iustin Pop
  def ExpandNodeName(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 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
905 a8083063 Iustin Pop
906 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
907 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
908 a8083063 Iustin Pop

909 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
910 c41eea6e Iustin Pop
    held.
911 f78ede4e Guido Trotter

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

914 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
915 c41eea6e Iustin Pop
    @return: the node object
916 a8083063 Iustin Pop

917 a8083063 Iustin Pop
    """
918 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
919 a8083063 Iustin Pop
      return None
920 a8083063 Iustin Pop
921 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
922 a8083063 Iustin Pop
923 f78ede4e Guido Trotter
924 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
925 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
926 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
927 f78ede4e Guido Trotter

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

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

932 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
933 c41eea6e Iustin Pop
    @return: the node object
934 f78ede4e Guido Trotter

935 f78ede4e Guido Trotter
    """
936 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
937 f78ede4e Guido Trotter
938 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
939 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
940 a8083063 Iustin Pop

941 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
942 c41eea6e Iustin Pop
    held.
943 c41eea6e Iustin Pop

944 c41eea6e Iustin Pop
    @rtype: list
945 f78ede4e Guido Trotter

946 a8083063 Iustin Pop
    """
947 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
948 a8083063 Iustin Pop
949 f78ede4e Guido Trotter
950 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
951 f78ede4e Guido Trotter
  def GetNodeList(self):
952 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
953 f78ede4e Guido Trotter

954 f78ede4e Guido Trotter
    """
955 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
956 f78ede4e Guido Trotter
957 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
958 94a02bb5 Iustin Pop
  def GetOnlineNodeList(self):
959 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
960 94a02bb5 Iustin Pop

961 94a02bb5 Iustin Pop
    """
962 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
963 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
964 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
965 94a02bb5 Iustin Pop
966 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
967 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
968 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
969 d65e5776 Iustin Pop

970 d65e5776 Iustin Pop
    @rtype: dict
971 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
972 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
973 d65e5776 Iustin Pop

974 d65e5776 Iustin Pop
    """
975 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
976 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
977 d65e5776 Iustin Pop
    return my_dict
978 d65e5776 Iustin Pop
979 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
980 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
981 ec0292f1 Iustin Pop

982 23f06b2b Iustin Pop
    @type exceptions: list
983 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
984 ec0292f1 Iustin Pop
    @rtype: tuple
985 ec0292f1 Iustin Pop
    @return: tuple of (current, desired and possible)
986 ec0292f1 Iustin Pop

987 ec0292f1 Iustin Pop
    """
988 ec0292f1 Iustin Pop
    mc_now = mc_max = 0
989 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
990 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
991 23f06b2b Iustin Pop
        continue
992 5bf07049 Iustin Pop
      if not (node.offline or node.drained):
993 ec0292f1 Iustin Pop
        mc_max += 1
994 ec0292f1 Iustin Pop
      if node.master_candidate:
995 ec0292f1 Iustin Pop
        mc_now += 1
996 ec0292f1 Iustin Pop
    mc_max = min(mc_max, self._config_data.cluster.candidate_pool_size)
997 ec0292f1 Iustin Pop
    return (mc_now, mc_max)
998 ec0292f1 Iustin Pop
999 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1000 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1001 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1002 ec0292f1 Iustin Pop

1003 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1004 ec0292f1 Iustin Pop

1005 23f06b2b Iustin Pop
    @type exceptions: list
1006 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1007 ec0292f1 Iustin Pop
    @rtype: tuple
1008 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1009 ec0292f1 Iustin Pop

1010 ec0292f1 Iustin Pop
    """
1011 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1012 ec0292f1 Iustin Pop
1013 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1014 ec0292f1 Iustin Pop
  def MaintainCandidatePool(self):
1015 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1016 ec0292f1 Iustin Pop

1017 ec0292f1 Iustin Pop
    @rtype: list
1018 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1019 ec0292f1 Iustin Pop

1020 ec0292f1 Iustin Pop
    """
1021 ec0292f1 Iustin Pop
    mc_now, mc_max = self._UnlockedGetMasterCandidateStats()
1022 ec0292f1 Iustin Pop
    mod_list = []
1023 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1024 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1025 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1026 ec0292f1 Iustin Pop
      for name in node_list:
1027 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1028 ec0292f1 Iustin Pop
          break
1029 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1030 5bf07049 Iustin Pop
        if node.master_candidate or node.offline or node.drained:
1031 ec0292f1 Iustin Pop
          continue
1032 ee513a66 Iustin Pop
        mod_list.append(node)
1033 ec0292f1 Iustin Pop
        node.master_candidate = True
1034 ec0292f1 Iustin Pop
        node.serial_no += 1
1035 ec0292f1 Iustin Pop
        mc_now += 1
1036 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1037 ec0292f1 Iustin Pop
        # this should not happen
1038 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1039 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1040 ec0292f1 Iustin Pop
      if mod_list:
1041 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1042 ec0292f1 Iustin Pop
        self._WriteConfig()
1043 ec0292f1 Iustin Pop
1044 ec0292f1 Iustin Pop
    return mod_list
1045 ec0292f1 Iustin Pop
1046 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1047 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1048 a8083063 Iustin Pop

1049 a8083063 Iustin Pop
    """
1050 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1051 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1052 a8083063 Iustin Pop
1053 a8083063 Iustin Pop
  def _OpenConfig(self):
1054 a8083063 Iustin Pop
    """Read the config data from disk.
1055 a8083063 Iustin Pop

1056 a8083063 Iustin Pop
    """
1057 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1058 13998ef2 Michael Hanselmann
1059 a8083063 Iustin Pop
    try:
1060 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1061 13998ef2 Michael Hanselmann
    except Exception, err:
1062 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1063 5b263ed7 Michael Hanselmann
1064 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1065 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1066 5b263ed7 Michael Hanselmann
1067 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1068 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1069 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1070 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1071 90d726a8 Iustin Pop
1072 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1073 90d726a8 Iustin Pop
    data.UpgradeConfig()
1074 90d726a8 Iustin Pop
1075 a8083063 Iustin Pop
    self._config_data = data
1076 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1077 0779e3aa Iustin Pop
    # ssconf update
1078 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1079 a8083063 Iustin Pop
1080 a8083063 Iustin Pop
  def _DistributeConfig(self):
1081 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1082 a8083063 Iustin Pop

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

1086 a8083063 Iustin Pop
    """
1087 a8083063 Iustin Pop
    if self._offline:
1088 a8083063 Iustin Pop
      return True
1089 a8083063 Iustin Pop
    bad = False
1090 a8083063 Iustin Pop
1091 6a5b8b4b Iustin Pop
    node_list = []
1092 6a5b8b4b Iustin Pop
    addr_list = []
1093 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1094 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1095 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1096 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1097 6b294c53 Iustin Pop
    # in between
1098 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1099 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1100 6a5b8b4b Iustin Pop
        continue
1101 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1102 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1103 6a5b8b4b Iustin Pop
        continue
1104 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1105 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1106 6b294c53 Iustin Pop
1107 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1108 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1109 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1110 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1111 1b54fc6c Guido Trotter
      if msg:
1112 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1113 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1114 1b54fc6c Guido Trotter
        logging.error(msg)
1115 a8083063 Iustin Pop
        bad = True
1116 a8083063 Iustin Pop
    return not bad
1117 a8083063 Iustin Pop
1118 a8083063 Iustin Pop
  def _WriteConfig(self, destination=None):
1119 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1120 a8083063 Iustin Pop

1121 a8083063 Iustin Pop
    """
1122 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1123 4a89c54a Iustin Pop
    if config_errors:
1124 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Configuration data is not"
1125 4a89c54a Iustin Pop
                                      " consistent: %s" %
1126 4a89c54a Iustin Pop
                                      (", ".join(config_errors)))
1127 a8083063 Iustin Pop
    if destination is None:
1128 a8083063 Iustin Pop
      destination = self._cfg_file
1129 a8083063 Iustin Pop
    self._BumpSerialNo()
1130 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1131 13998ef2 Michael Hanselmann
1132 13998ef2 Michael Hanselmann
    utils.WriteFile(destination, data=txt)
1133 13998ef2 Michael Hanselmann
1134 14e15659 Iustin Pop
    self.write_count += 1
1135 3d3a04bc Iustin Pop
1136 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
1137 a8083063 Iustin Pop
    self._DistributeConfig()
1138 a8083063 Iustin Pop
1139 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
1140 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
1141 d9a855f1 Michael Hanselmann
      if not self._offline:
1142 e1e75d00 Iustin Pop
        result = rpc.RpcRunner.call_write_ssconf_files(\
1143 e1e75d00 Iustin Pop
          self._UnlockedGetNodeList(),
1144 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
1145 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
1146 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
1147 e1e75d00 Iustin Pop
          if msg:
1148 e1e75d00 Iustin Pop
            logging.warning("Error while uploading ssconf files to"
1149 e1e75d00 Iustin Pop
                            " node %s: %s", nname, msg)
1150 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
1151 54d1a06e Michael Hanselmann
1152 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
1153 054596f0 Iustin Pop
    """Return the values needed by ssconf.
1154 054596f0 Iustin Pop

1155 054596f0 Iustin Pop
    @rtype: dict
1156 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1157 054596f0 Iustin Pop
        associated value
1158 054596f0 Iustin Pop

1159 054596f0 Iustin Pop
    """
1160 a3316e4a Iustin Pop
    fn = "\n".join
1161 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1162 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1163 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1164 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1165 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1166 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1167 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1168 a3316e4a Iustin Pop
1169 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1170 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1171 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1172 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1173 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1174 8113a52e Luca Bigliardi
                     if node.master_candidate)
1175 a3316e4a Iustin Pop
    node_data = fn(node_names)
1176 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1177 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1178 f56618e0 Iustin Pop
1179 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1180 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1181 03d1dba2 Michael Hanselmann
    return {
1182 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1183 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1184 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1185 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1186 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1187 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1188 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1189 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1190 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1191 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1192 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1193 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1194 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1195 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1196 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1197 03d1dba2 Michael Hanselmann
      }
1198 03d1dba2 Michael Hanselmann
1199 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1200 a8083063 Iustin Pop
  def GetVGName(self):
1201 a8083063 Iustin Pop
    """Return the volume group name.
1202 a8083063 Iustin Pop

1203 a8083063 Iustin Pop
    """
1204 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1205 a8083063 Iustin Pop
1206 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1207 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1208 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1209 89ff8e15 Manuel Franceschini

1210 89ff8e15 Manuel Franceschini
    """
1211 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1212 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1213 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1214 89ff8e15 Manuel Franceschini
1215 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1216 a8083063 Iustin Pop
  def GetMACPrefix(self):
1217 a8083063 Iustin Pop
    """Return the mac prefix.
1218 a8083063 Iustin Pop

1219 a8083063 Iustin Pop
    """
1220 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1221 62779dd0 Iustin Pop
1222 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1223 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1224 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1225 62779dd0 Iustin Pop

1226 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1227 c41eea6e Iustin Pop
    @return: the cluster object
1228 62779dd0 Iustin Pop

1229 62779dd0 Iustin Pop
    """
1230 62779dd0 Iustin Pop
    return self._config_data.cluster
1231 e00fb268 Iustin Pop
1232 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1233 e00fb268 Iustin Pop
  def Update(self, target):
1234 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1235 e00fb268 Iustin Pop

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

1242 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1243 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1244 c41eea6e Iustin Pop
        the cluster
1245 c41eea6e Iustin Pop

1246 e00fb268 Iustin Pop
    """
1247 e00fb268 Iustin Pop
    if self._config_data is None:
1248 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1249 3ecf6786 Iustin Pop
                                   " cannot save.")
1250 f34901f8 Iustin Pop
    update_serial = False
1251 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1252 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1253 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1254 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1255 f34901f8 Iustin Pop
      update_serial = True
1256 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1257 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1258 e00fb268 Iustin Pop
    else:
1259 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1260 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1261 e00fb268 Iustin Pop
    if not test:
1262 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1263 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1264 f34901f8 Iustin Pop
    target.serial_no += 1
1265 d693c864 Iustin Pop
    target.mtime = now = time.time()
1266 f34901f8 Iustin Pop
1267 cff4c037 Iustin Pop
    if update_serial:
1268 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1269 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1270 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1271 b989e85d Iustin Pop
1272 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1273 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1274 e7d81ba0 Iustin Pop
      for nic in target.nics:
1275 e7d81ba0 Iustin Pop
        self._temporary_macs.discard(nic.mac)
1276 61cf6b5e Iustin Pop
1277 e00fb268 Iustin Pop
    self._WriteConfig()