Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 76d5d3a3

History | View | Annotate | Download (42.4 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 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
179 34e54ebc Iustin Pop
    return existing
180 34e54ebc Iustin Pop
181 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
182 923b1523 Iustin Pop
  def GenerateUniqueID(self, exceptions=None):
183 923b1523 Iustin Pop
    """Generate an unique disk name.
184 923b1523 Iustin Pop

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

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

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

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

214 34d657ba Iustin Pop
    """
215 34d657ba Iustin Pop
    existing = self._AllIDs(include_temporary=False)
216 34d657ba Iustin Pop
    self._temporary_ids = self._temporary_ids - existing
217 34d657ba Iustin Pop
218 a8083063 Iustin Pop
  def _AllMACs(self):
219 a8083063 Iustin Pop
    """Return all MACs present in the config.
220 a8083063 Iustin Pop

221 c41eea6e Iustin Pop
    @rtype: list
222 c41eea6e Iustin Pop
    @return: the list of all MACs
223 c41eea6e Iustin Pop

224 a8083063 Iustin Pop
    """
225 a8083063 Iustin Pop
    result = []
226 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
227 a8083063 Iustin Pop
      for nic in instance.nics:
228 a8083063 Iustin Pop
        result.append(nic.mac)
229 a8083063 Iustin Pop
230 a8083063 Iustin Pop
    return result
231 a8083063 Iustin Pop
232 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
233 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
234 f9518d38 Iustin Pop

235 c41eea6e Iustin Pop
    @rtype: list
236 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
237 c41eea6e Iustin Pop

238 f9518d38 Iustin Pop
    """
239 f9518d38 Iustin Pop
    def helper(disk, result):
240 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
241 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
242 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
243 f9518d38 Iustin Pop
      if disk.children:
244 f9518d38 Iustin Pop
        for child in disk.children:
245 f9518d38 Iustin Pop
          helper(child, result)
246 f9518d38 Iustin Pop
247 f9518d38 Iustin Pop
    result = []
248 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
249 f9518d38 Iustin Pop
      for disk in instance.disks:
250 f9518d38 Iustin Pop
        helper(disk, result)
251 f9518d38 Iustin Pop
252 f9518d38 Iustin Pop
    return result
253 f9518d38 Iustin Pop
254 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
255 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
256 4b98ac29 Iustin Pop

257 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
258 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
259 4b98ac29 Iustin Pop
    @type l_ids: list
260 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
261 4b98ac29 Iustin Pop
    @type p_ids: list
262 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
263 4b98ac29 Iustin Pop
    @rtype: list
264 4b98ac29 Iustin Pop
    @return: a list of error messages
265 4b98ac29 Iustin Pop

266 4b98ac29 Iustin Pop
    """
267 4b98ac29 Iustin Pop
    result = []
268 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
269 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
270 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
271 25ae22e4 Iustin Pop
      else:
272 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
273 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
274 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
275 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
276 25ae22e4 Iustin Pop
      else:
277 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
278 4b98ac29 Iustin Pop
279 4b98ac29 Iustin Pop
    if disk.children:
280 4b98ac29 Iustin Pop
      for child in disk.children:
281 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
282 4b98ac29 Iustin Pop
    return result
283 4b98ac29 Iustin Pop
284 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
285 a8efbb40 Iustin Pop
    """Verify function.
286 a8efbb40 Iustin Pop

287 4a89c54a Iustin Pop
    @rtype: list
288 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
289 4a89c54a Iustin Pop
        configuration errors
290 4a89c54a Iustin Pop

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

398 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
399 4a89c54a Iustin Pop

400 4a89c54a Iustin Pop
    @rtype: list
401 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
402 4a89c54a Iustin Pop
        configuration errors
403 4a89c54a Iustin Pop

404 4a89c54a Iustin Pop
    """
405 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
406 4a89c54a Iustin Pop
407 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
408 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
409 a8083063 Iustin Pop

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

412 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
413 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
414 a8083063 Iustin Pop
    node.
415 a8083063 Iustin Pop

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

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

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

451 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
452 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
453 f78ede4e Guido Trotter
    node.
454 f78ede4e Guido Trotter

455 f78ede4e Guido Trotter
    """
456 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
457 f78ede4e Guido Trotter
458 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
459 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
460 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
461 b2fddf63 Iustin Pop

462 b2fddf63 Iustin Pop
    """
463 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
464 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
465 264bb3c5 Michael Hanselmann
466 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
467 264bb3c5 Michael Hanselmann
    self._WriteConfig()
468 264bb3c5 Michael Hanselmann
469 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
470 b2fddf63 Iustin Pop
  def GetPortList(self):
471 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
472 264bb3c5 Michael Hanselmann

473 264bb3c5 Michael Hanselmann
    """
474 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
475 264bb3c5 Michael Hanselmann
476 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
477 a8083063 Iustin Pop
  def AllocatePort(self):
478 a8083063 Iustin Pop
    """Allocate a port.
479 a8083063 Iustin Pop

480 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
481 b2fddf63 Iustin Pop
    default port range (and in this case we increase
482 b2fddf63 Iustin Pop
    highest_used_port).
483 a8083063 Iustin Pop

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

502 4a89c54a Iustin Pop
    @rtype: (dict, list)
503 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
504 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
505 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
506 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
507 4a89c54a Iustin Pop
        should raise an exception
508 a81c53c9 Iustin Pop

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

542 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
543 6d2e83d5 Iustin Pop

544 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
545 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
546 6d2e83d5 Iustin Pop
        an empty list).
547 6d2e83d5 Iustin Pop

548 6d2e83d5 Iustin Pop
    """
549 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
550 4a89c54a Iustin Pop
    if duplicates:
551 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
552 4a89c54a Iustin Pop
                                      str(duplicates))
553 4a89c54a Iustin Pop
    return d_map
554 6d2e83d5 Iustin Pop
555 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
556 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
557 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
558 a81c53c9 Iustin Pop

559 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
560 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
561 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
562 a81c53c9 Iustin Pop
    order as the passed nodes.
563 a81c53c9 Iustin Pop

564 32388e6d Iustin Pop
    @type instance: string
565 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
566 32388e6d Iustin Pop

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

614 a81c53c9 Iustin Pop
    @type instance: string
615 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
616 a81c53c9 Iustin Pop
                     released
617 a81c53c9 Iustin Pop

618 a81c53c9 Iustin Pop
    """
619 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
620 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
621 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
622 a81c53c9 Iustin Pop
      if name == instance:
623 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
624 a81c53c9 Iustin Pop
625 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
626 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
627 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
628 61cf6b5e Iustin Pop

629 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
630 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
631 61cf6b5e Iustin Pop
    functions.
632 61cf6b5e Iustin Pop

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

635 61cf6b5e Iustin Pop
    @type instance: string
636 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
637 61cf6b5e Iustin Pop
                     released
638 61cf6b5e Iustin Pop

639 61cf6b5e Iustin Pop
    """
640 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
641 61cf6b5e Iustin Pop
642 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
643 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
644 4a8b186a Michael Hanselmann
    """Get the configuration version.
645 4a8b186a Michael Hanselmann

646 4a8b186a Michael Hanselmann
    @return: Config version
647 4a8b186a Michael Hanselmann

648 4a8b186a Michael Hanselmann
    """
649 4a8b186a Michael Hanselmann
    return self._config_data.version
650 4a8b186a Michael Hanselmann
651 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
652 4a8b186a Michael Hanselmann
  def GetClusterName(self):
653 4a8b186a Michael Hanselmann
    """Get cluster name.
654 4a8b186a Michael Hanselmann

655 4a8b186a Michael Hanselmann
    @return: Cluster name
656 4a8b186a Michael Hanselmann

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

664 4a8b186a Michael Hanselmann
    @return: Master hostname
665 4a8b186a Michael Hanselmann

666 4a8b186a Michael Hanselmann
    """
667 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
668 4a8b186a Michael Hanselmann
669 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
670 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
671 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
672 4a8b186a Michael Hanselmann

673 4a8b186a Michael Hanselmann
    @return: Master IP
674 4a8b186a Michael Hanselmann

675 4a8b186a Michael Hanselmann
    """
676 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
677 4a8b186a Michael Hanselmann
678 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
679 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
680 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
681 4a8b186a Michael Hanselmann

682 4a8b186a Michael Hanselmann
    """
683 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
684 4a8b186a Michael Hanselmann
685 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
686 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
687 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
688 4a8b186a Michael Hanselmann

689 4a8b186a Michael Hanselmann
    """
690 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
691 4a8b186a Michael Hanselmann
692 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
693 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
694 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
695 4a8b186a Michael Hanselmann

696 4a8b186a Michael Hanselmann
    """
697 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
698 4a8b186a Michael Hanselmann
699 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
700 a8083063 Iustin Pop
  def GetHostKey(self):
701 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
702 a8083063 Iustin Pop

703 c41eea6e Iustin Pop
    @rtype: string
704 c41eea6e Iustin Pop
    @return: the rsa hostkey
705 a8083063 Iustin Pop

706 a8083063 Iustin Pop
    """
707 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
708 a8083063 Iustin Pop
709 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
710 a8083063 Iustin Pop
  def AddInstance(self, instance):
711 a8083063 Iustin Pop
    """Add an instance to the config.
712 a8083063 Iustin Pop

713 a8083063 Iustin Pop
    This should be used after creating a new instance.
714 a8083063 Iustin Pop

715 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
716 c41eea6e Iustin Pop
    @param instance: the instance object
717 c41eea6e Iustin Pop

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

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

762 6a408fb2 Iustin Pop
    """
763 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
764 6a408fb2 Iustin Pop
765 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
766 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
767 a8083063 Iustin Pop
    """Remove the instance from the configuration.
768 a8083063 Iustin Pop

769 a8083063 Iustin Pop
    """
770 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
771 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
772 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
773 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
774 a8083063 Iustin Pop
    self._WriteConfig()
775 a8083063 Iustin Pop
776 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
777 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
778 fc95f88f Iustin Pop
    """Rename an instance.
779 fc95f88f Iustin Pop

780 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
781 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
782 fc95f88f Iustin Pop
    rename.
783 fc95f88f Iustin Pop

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

807 a8083063 Iustin Pop
    """
808 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
809 a8083063 Iustin Pop
810 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
811 94bbfece Iustin Pop
    """Get the list of instances.
812 94bbfece Iustin Pop

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

815 94bbfece Iustin Pop
    """
816 94bbfece Iustin Pop
    return self._config_data.instances.keys()
817 94bbfece Iustin Pop
818 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
819 a8083063 Iustin Pop
  def GetInstanceList(self):
820 a8083063 Iustin Pop
    """Get the list of instances.
821 a8083063 Iustin Pop

822 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
823 c41eea6e Iustin Pop
        'instance1.example.com']
824 a8083063 Iustin Pop

825 a8083063 Iustin Pop
    """
826 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
827 a8083063 Iustin Pop
828 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
829 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
830 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
831 a8083063 Iustin Pop

832 a8083063 Iustin Pop
    """
833 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
834 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
835 a8083063 Iustin Pop
836 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
837 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
838 94bbfece Iustin Pop

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

841 94bbfece Iustin Pop
    """
842 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
843 94bbfece Iustin Pop
      return None
844 94bbfece Iustin Pop
845 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
846 94bbfece Iustin Pop
847 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
848 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
849 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
850 a8083063 Iustin Pop

851 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
852 a8083063 Iustin Pop
    an instance are taken from the live systems.
853 a8083063 Iustin Pop

854 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
855 c41eea6e Iustin Pop
        I{instance1.example.com}
856 a8083063 Iustin Pop

857 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
858 c41eea6e Iustin Pop
    @return: the instance object
859 a8083063 Iustin Pop

860 a8083063 Iustin Pop
    """
861 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
862 a8083063 Iustin Pop
863 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
864 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
865 0b2de758 Iustin Pop
    """Get the configuration of all instances.
866 0b2de758 Iustin Pop

867 0b2de758 Iustin Pop
    @rtype: dict
868 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
869 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
870 0b2de758 Iustin Pop

871 0b2de758 Iustin Pop
    """
872 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
873 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
874 0b2de758 Iustin Pop
    return my_dict
875 0b2de758 Iustin Pop
876 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
877 a8083063 Iustin Pop
  def AddNode(self, node):
878 a8083063 Iustin Pop
    """Add a node to the configuration.
879 a8083063 Iustin Pop

880 c41eea6e Iustin Pop
    @type node: L{objects.Node}
881 c41eea6e Iustin Pop
    @param node: a Node instance
882 a8083063 Iustin Pop

883 a8083063 Iustin Pop
    """
884 d8470559 Michael Hanselmann
    logging.info("Adding node %s to configuration" % node.name)
885 d8470559 Michael Hanselmann
886 b989e85d Iustin Pop
    node.serial_no = 1
887 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
888 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
889 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
890 a8083063 Iustin Pop
    self._WriteConfig()
891 a8083063 Iustin Pop
892 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
893 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
894 a8083063 Iustin Pop
    """Remove a node from the configuration.
895 a8083063 Iustin Pop

896 a8083063 Iustin Pop
    """
897 d8470559 Michael Hanselmann
    logging.info("Removing node %s from configuration" % node_name)
898 d8470559 Michael Hanselmann
899 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
900 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
901 a8083063 Iustin Pop
902 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
903 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
904 a8083063 Iustin Pop
    self._WriteConfig()
905 a8083063 Iustin Pop
906 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
907 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
908 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
909 a8083063 Iustin Pop

910 a8083063 Iustin Pop
    """
911 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
912 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
913 a8083063 Iustin Pop
914 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
915 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
916 a8083063 Iustin Pop

917 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
918 c41eea6e Iustin Pop
    held.
919 f78ede4e Guido Trotter

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

922 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
923 c41eea6e Iustin Pop
    @return: the node object
924 a8083063 Iustin Pop

925 a8083063 Iustin Pop
    """
926 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
927 a8083063 Iustin Pop
      return None
928 a8083063 Iustin Pop
929 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
930 a8083063 Iustin Pop
931 f78ede4e Guido Trotter
932 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
933 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
934 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
935 f78ede4e Guido Trotter

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

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

940 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
941 c41eea6e Iustin Pop
    @return: the node object
942 f78ede4e Guido Trotter

943 f78ede4e Guido Trotter
    """
944 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
945 f78ede4e Guido Trotter
946 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
947 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
948 a8083063 Iustin Pop

949 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
950 c41eea6e Iustin Pop
    held.
951 c41eea6e Iustin Pop

952 c41eea6e Iustin Pop
    @rtype: list
953 f78ede4e Guido Trotter

954 a8083063 Iustin Pop
    """
955 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
956 a8083063 Iustin Pop
957 f78ede4e Guido Trotter
958 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
959 f78ede4e Guido Trotter
  def GetNodeList(self):
960 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
961 f78ede4e Guido Trotter

962 f78ede4e Guido Trotter
    """
963 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
964 f78ede4e Guido Trotter
965 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
966 94a02bb5 Iustin Pop
  def GetOnlineNodeList(self):
967 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
968 94a02bb5 Iustin Pop

969 94a02bb5 Iustin Pop
    """
970 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
971 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
972 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
973 94a02bb5 Iustin Pop
974 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
975 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
976 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
977 d65e5776 Iustin Pop

978 d65e5776 Iustin Pop
    @rtype: dict
979 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
980 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
981 d65e5776 Iustin Pop

982 d65e5776 Iustin Pop
    """
983 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
984 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
985 d65e5776 Iustin Pop
    return my_dict
986 d65e5776 Iustin Pop
987 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
988 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
989 ec0292f1 Iustin Pop

990 23f06b2b Iustin Pop
    @type exceptions: list
991 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
992 ec0292f1 Iustin Pop
    @rtype: tuple
993 ec0292f1 Iustin Pop
    @return: tuple of (current, desired and possible)
994 ec0292f1 Iustin Pop

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

1011 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1012 ec0292f1 Iustin Pop

1013 23f06b2b Iustin Pop
    @type exceptions: list
1014 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1015 ec0292f1 Iustin Pop
    @rtype: tuple
1016 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1017 ec0292f1 Iustin Pop

1018 ec0292f1 Iustin Pop
    """
1019 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1020 ec0292f1 Iustin Pop
1021 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1022 ec0292f1 Iustin Pop
  def MaintainCandidatePool(self):
1023 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1024 ec0292f1 Iustin Pop

1025 ec0292f1 Iustin Pop
    @rtype: list
1026 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1027 ec0292f1 Iustin Pop

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

1057 a8083063 Iustin Pop
    """
1058 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1059 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1060 a8083063 Iustin Pop
1061 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
1062 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
1063 76d5d3a3 Iustin Pop

1064 76d5d3a3 Iustin Pop
    """
1065 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
1066 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
1067 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
1068 76d5d3a3 Iustin Pop
1069 a8083063 Iustin Pop
  def _OpenConfig(self):
1070 a8083063 Iustin Pop
    """Read the config data from disk.
1071 a8083063 Iustin Pop

1072 a8083063 Iustin Pop
    """
1073 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1074 13998ef2 Michael Hanselmann
1075 a8083063 Iustin Pop
    try:
1076 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1077 13998ef2 Michael Hanselmann
    except Exception, err:
1078 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1079 5b263ed7 Michael Hanselmann
1080 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1081 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1082 5b263ed7 Michael Hanselmann
1083 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1084 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1085 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1086 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1087 90d726a8 Iustin Pop
1088 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1089 90d726a8 Iustin Pop
    data.UpgradeConfig()
1090 90d726a8 Iustin Pop
1091 a8083063 Iustin Pop
    self._config_data = data
1092 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1093 0779e3aa Iustin Pop
    # ssconf update
1094 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1095 a8083063 Iustin Pop
1096 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
1097 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
1098 76d5d3a3 Iustin Pop
1099 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
1100 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
1101 76d5d3a3 Iustin Pop

1102 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
1103 76d5d3a3 Iustin Pop
    whole configuration, etc.
1104 76d5d3a3 Iustin Pop

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

1109 76d5d3a3 Iustin Pop
    """
1110 76d5d3a3 Iustin Pop
    modified = False
1111 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
1112 76d5d3a3 Iustin Pop
      if item.uuid is None:
1113 76d5d3a3 Iustin Pop
        item.uuid = self.GenerateUniqueID()
1114 76d5d3a3 Iustin Pop
        modified = True
1115 76d5d3a3 Iustin Pop
    if modified:
1116 76d5d3a3 Iustin Pop
      self._WriteConfig()
1117 76d5d3a3 Iustin Pop
1118 a8083063 Iustin Pop
  def _DistributeConfig(self):
1119 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1120 a8083063 Iustin Pop

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

1124 a8083063 Iustin Pop
    """
1125 a8083063 Iustin Pop
    if self._offline:
1126 a8083063 Iustin Pop
      return True
1127 a8083063 Iustin Pop
    bad = False
1128 a8083063 Iustin Pop
1129 6a5b8b4b Iustin Pop
    node_list = []
1130 6a5b8b4b Iustin Pop
    addr_list = []
1131 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1132 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1133 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1134 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1135 6b294c53 Iustin Pop
    # in between
1136 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1137 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1138 6a5b8b4b Iustin Pop
        continue
1139 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1140 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1141 6a5b8b4b Iustin Pop
        continue
1142 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1143 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1144 6b294c53 Iustin Pop
1145 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1146 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1147 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
1148 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
1149 1b54fc6c Guido Trotter
      if msg:
1150 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
1151 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
1152 1b54fc6c Guido Trotter
        logging.error(msg)
1153 a8083063 Iustin Pop
        bad = True
1154 a8083063 Iustin Pop
    return not bad
1155 a8083063 Iustin Pop
1156 a8083063 Iustin Pop
  def _WriteConfig(self, destination=None):
1157 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1158 a8083063 Iustin Pop

1159 a8083063 Iustin Pop
    """
1160 34d657ba Iustin Pop
    # first, cleanup the _temporary_ids set, if an ID is now in the
1161 34d657ba Iustin Pop
    # other objects it should be discarded to prevent unbounded growth
1162 34d657ba Iustin Pop
    # of that structure
1163 34d657ba Iustin Pop
    self._CleanupTemporaryIDs()
1164 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1165 4a89c54a Iustin Pop
    if config_errors:
1166 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Configuration data is not"
1167 4a89c54a Iustin Pop
                                      " consistent: %s" %
1168 4a89c54a Iustin Pop
                                      (", ".join(config_errors)))
1169 a8083063 Iustin Pop
    if destination is None:
1170 a8083063 Iustin Pop
      destination = self._cfg_file
1171 a8083063 Iustin Pop
    self._BumpSerialNo()
1172 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1173 13998ef2 Michael Hanselmann
1174 13998ef2 Michael Hanselmann
    utils.WriteFile(destination, data=txt)
1175 13998ef2 Michael Hanselmann
1176 14e15659 Iustin Pop
    self.write_count += 1
1177 3d3a04bc Iustin Pop
1178 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
1179 a8083063 Iustin Pop
    self._DistributeConfig()
1180 a8083063 Iustin Pop
1181 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
1182 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
1183 d9a855f1 Michael Hanselmann
      if not self._offline:
1184 e1e75d00 Iustin Pop
        result = rpc.RpcRunner.call_write_ssconf_files(\
1185 e1e75d00 Iustin Pop
          self._UnlockedGetNodeList(),
1186 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
1187 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
1188 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
1189 e1e75d00 Iustin Pop
          if msg:
1190 e1e75d00 Iustin Pop
            logging.warning("Error while uploading ssconf files to"
1191 e1e75d00 Iustin Pop
                            " node %s: %s", nname, msg)
1192 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
1193 54d1a06e Michael Hanselmann
1194 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
1195 054596f0 Iustin Pop
    """Return the values needed by ssconf.
1196 054596f0 Iustin Pop

1197 054596f0 Iustin Pop
    @rtype: dict
1198 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1199 054596f0 Iustin Pop
        associated value
1200 054596f0 Iustin Pop

1201 054596f0 Iustin Pop
    """
1202 a3316e4a Iustin Pop
    fn = "\n".join
1203 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1204 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1205 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1206 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
1207 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1208 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
1209 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
1210 a3316e4a Iustin Pop
1211 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1212 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1213 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1214 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1215 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
1216 8113a52e Luca Bigliardi
                     if node.master_candidate)
1217 a3316e4a Iustin Pop
    node_data = fn(node_names)
1218 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
1219 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
1220 f56618e0 Iustin Pop
1221 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1222 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
1223 03d1dba2 Michael Hanselmann
    return {
1224 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1225 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
1226 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1227 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1228 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
1229 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1230 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1231 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1232 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1233 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
1234 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
1235 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1236 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1237 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1238 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1239 03d1dba2 Michael Hanselmann
      }
1240 03d1dba2 Michael Hanselmann
1241 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1242 a8083063 Iustin Pop
  def GetVGName(self):
1243 a8083063 Iustin Pop
    """Return the volume group name.
1244 a8083063 Iustin Pop

1245 a8083063 Iustin Pop
    """
1246 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1247 a8083063 Iustin Pop
1248 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1249 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1250 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1251 89ff8e15 Manuel Franceschini

1252 89ff8e15 Manuel Franceschini
    """
1253 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1254 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1255 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1256 89ff8e15 Manuel Franceschini
1257 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1258 a8083063 Iustin Pop
  def GetMACPrefix(self):
1259 a8083063 Iustin Pop
    """Return the mac prefix.
1260 a8083063 Iustin Pop

1261 a8083063 Iustin Pop
    """
1262 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1263 62779dd0 Iustin Pop
1264 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1265 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1266 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1267 62779dd0 Iustin Pop

1268 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1269 c41eea6e Iustin Pop
    @return: the cluster object
1270 62779dd0 Iustin Pop

1271 62779dd0 Iustin Pop
    """
1272 62779dd0 Iustin Pop
    return self._config_data.cluster
1273 e00fb268 Iustin Pop
1274 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1275 e00fb268 Iustin Pop
  def Update(self, target):
1276 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1277 e00fb268 Iustin Pop

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

1284 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1285 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1286 c41eea6e Iustin Pop
        the cluster
1287 c41eea6e Iustin Pop

1288 e00fb268 Iustin Pop
    """
1289 e00fb268 Iustin Pop
    if self._config_data is None:
1290 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1291 3ecf6786 Iustin Pop
                                   " cannot save.")
1292 f34901f8 Iustin Pop
    update_serial = False
1293 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1294 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1295 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1296 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1297 f34901f8 Iustin Pop
      update_serial = True
1298 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1299 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1300 e00fb268 Iustin Pop
    else:
1301 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1302 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1303 e00fb268 Iustin Pop
    if not test:
1304 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1305 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1306 f34901f8 Iustin Pop
    target.serial_no += 1
1307 d693c864 Iustin Pop
    target.mtime = now = time.time()
1308 f34901f8 Iustin Pop
1309 cff4c037 Iustin Pop
    if update_serial:
1310 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1311 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1312 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
1313 b989e85d Iustin Pop
1314 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1315 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1316 e7d81ba0 Iustin Pop
      for nic in target.nics:
1317 e7d81ba0 Iustin Pop
        self._temporary_macs.discard(nic.mac)
1318 61cf6b5e Iustin Pop
1319 e00fb268 Iustin Pop
    self._WriteConfig()