Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 90d726a8

History | View | Annotate | Download (40.8 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 923b1523 Iustin Pop
  def _ComputeAllLVs(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 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
164 923b1523 Iustin Pop
  def GenerateUniqueID(self, exceptions=None):
165 923b1523 Iustin Pop
    """Generate an unique disk name.
166 923b1523 Iustin Pop

167 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
168 923b1523 Iustin Pop
    duplicates.
169 923b1523 Iustin Pop

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

175 c41eea6e Iustin Pop
    @rtype: string
176 c41eea6e Iustin Pop
    @return: the unique id
177 923b1523 Iustin Pop

178 923b1523 Iustin Pop
    """
179 923b1523 Iustin Pop
    existing = set()
180 923b1523 Iustin Pop
    existing.update(self._temporary_ids)
181 923b1523 Iustin Pop
    existing.update(self._ComputeAllLVs())
182 923b1523 Iustin Pop
    existing.update(self._config_data.instances.keys())
183 923b1523 Iustin Pop
    existing.update(self._config_data.nodes.keys())
184 923b1523 Iustin Pop
    if exceptions is not None:
185 923b1523 Iustin Pop
      existing.update(exceptions)
186 923b1523 Iustin Pop
    retries = 64
187 923b1523 Iustin Pop
    while retries > 0:
188 24818e8f Michael Hanselmann
      unique_id = utils.NewUUID()
189 923b1523 Iustin Pop
      if unique_id not in existing and unique_id is not None:
190 923b1523 Iustin Pop
        break
191 923b1523 Iustin Pop
    else:
192 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Not able generate an unique ID"
193 3ecf6786 Iustin Pop
                                      " (last tried ID: %s" % unique_id)
194 923b1523 Iustin Pop
    self._temporary_ids.add(unique_id)
195 923b1523 Iustin Pop
    return unique_id
196 923b1523 Iustin Pop
197 a8083063 Iustin Pop
  def _AllMACs(self):
198 a8083063 Iustin Pop
    """Return all MACs present in the config.
199 a8083063 Iustin Pop

200 c41eea6e Iustin Pop
    @rtype: list
201 c41eea6e Iustin Pop
    @return: the list of all MACs
202 c41eea6e Iustin Pop

203 a8083063 Iustin Pop
    """
204 a8083063 Iustin Pop
    result = []
205 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
206 a8083063 Iustin Pop
      for nic in instance.nics:
207 a8083063 Iustin Pop
        result.append(nic.mac)
208 a8083063 Iustin Pop
209 a8083063 Iustin Pop
    return result
210 a8083063 Iustin Pop
211 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
212 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
213 f9518d38 Iustin Pop

214 c41eea6e Iustin Pop
    @rtype: list
215 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
216 c41eea6e Iustin Pop

217 f9518d38 Iustin Pop
    """
218 f9518d38 Iustin Pop
    def helper(disk, result):
219 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
220 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
221 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
222 f9518d38 Iustin Pop
      if disk.children:
223 f9518d38 Iustin Pop
        for child in disk.children:
224 f9518d38 Iustin Pop
          helper(child, result)
225 f9518d38 Iustin Pop
226 f9518d38 Iustin Pop
    result = []
227 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
228 f9518d38 Iustin Pop
      for disk in instance.disks:
229 f9518d38 Iustin Pop
        helper(disk, result)
230 f9518d38 Iustin Pop
231 f9518d38 Iustin Pop
    return result
232 f9518d38 Iustin Pop
233 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
234 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
235 4b98ac29 Iustin Pop

236 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
237 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
238 4b98ac29 Iustin Pop
    @type l_ids: list
239 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
240 4b98ac29 Iustin Pop
    @type p_ids: list
241 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
242 4b98ac29 Iustin Pop
    @rtype: list
243 4b98ac29 Iustin Pop
    @return: a list of error messages
244 4b98ac29 Iustin Pop

245 4b98ac29 Iustin Pop
    """
246 4b98ac29 Iustin Pop
    result = []
247 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
248 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
249 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
250 25ae22e4 Iustin Pop
      else:
251 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
252 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
253 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
254 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
255 25ae22e4 Iustin Pop
      else:
256 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
257 4b98ac29 Iustin Pop
258 4b98ac29 Iustin Pop
    if disk.children:
259 4b98ac29 Iustin Pop
      for child in disk.children:
260 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
261 4b98ac29 Iustin Pop
    return result
262 4b98ac29 Iustin Pop
263 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
264 a8efbb40 Iustin Pop
    """Verify function.
265 a8efbb40 Iustin Pop

266 4a89c54a Iustin Pop
    @rtype: list
267 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
268 4a89c54a Iustin Pop
        configuration errors
269 4a89c54a Iustin Pop

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

377 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
378 4a89c54a Iustin Pop

379 4a89c54a Iustin Pop
    @rtype: list
380 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
381 4a89c54a Iustin Pop
        configuration errors
382 4a89c54a Iustin Pop

383 4a89c54a Iustin Pop
    """
384 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
385 4a89c54a Iustin Pop
386 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
387 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
388 a8083063 Iustin Pop

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

391 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
392 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
393 a8083063 Iustin Pop
    node.
394 a8083063 Iustin Pop

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

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

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

430 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
431 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
432 f78ede4e Guido Trotter
    node.
433 f78ede4e Guido Trotter

434 f78ede4e Guido Trotter
    """
435 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
436 f78ede4e Guido Trotter
437 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
438 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
439 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
440 b2fddf63 Iustin Pop

441 b2fddf63 Iustin Pop
    """
442 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
443 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
444 264bb3c5 Michael Hanselmann
445 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
446 264bb3c5 Michael Hanselmann
    self._WriteConfig()
447 264bb3c5 Michael Hanselmann
448 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
449 b2fddf63 Iustin Pop
  def GetPortList(self):
450 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
451 264bb3c5 Michael Hanselmann

452 264bb3c5 Michael Hanselmann
    """
453 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
454 264bb3c5 Michael Hanselmann
455 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
456 a8083063 Iustin Pop
  def AllocatePort(self):
457 a8083063 Iustin Pop
    """Allocate a port.
458 a8083063 Iustin Pop

459 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
460 b2fddf63 Iustin Pop
    default port range (and in this case we increase
461 b2fddf63 Iustin Pop
    highest_used_port).
462 a8083063 Iustin Pop

463 a8083063 Iustin Pop
    """
464 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
465 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
466 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
467 264bb3c5 Michael Hanselmann
    else:
468 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
469 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
470 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
471 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
472 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
473 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
474 a8083063 Iustin Pop
475 a8083063 Iustin Pop
    self._WriteConfig()
476 a8083063 Iustin Pop
    return port
477 a8083063 Iustin Pop
478 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
479 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
480 a81c53c9 Iustin Pop

481 4a89c54a Iustin Pop
    @rtype: (dict, list)
482 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
483 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
484 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
485 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
486 4a89c54a Iustin Pop
        should raise an exception
487 a81c53c9 Iustin Pop

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

521 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
522 6d2e83d5 Iustin Pop

523 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
524 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
525 6d2e83d5 Iustin Pop
        an empty list).
526 6d2e83d5 Iustin Pop

527 6d2e83d5 Iustin Pop
    """
528 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
529 4a89c54a Iustin Pop
    if duplicates:
530 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
531 4a89c54a Iustin Pop
                                      str(duplicates))
532 4a89c54a Iustin Pop
    return d_map
533 6d2e83d5 Iustin Pop
534 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
535 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
536 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
537 a81c53c9 Iustin Pop

538 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
539 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
540 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
541 a81c53c9 Iustin Pop
    order as the passed nodes.
542 a81c53c9 Iustin Pop

543 32388e6d Iustin Pop
    @type instance: string
544 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
545 32388e6d Iustin Pop

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

593 a81c53c9 Iustin Pop
    @type instance: string
594 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
595 a81c53c9 Iustin Pop
                     released
596 a81c53c9 Iustin Pop

597 a81c53c9 Iustin Pop
    """
598 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
599 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
600 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
601 a81c53c9 Iustin Pop
      if name == instance:
602 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
603 a81c53c9 Iustin Pop
604 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
605 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
606 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
607 61cf6b5e Iustin Pop

608 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
609 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
610 61cf6b5e Iustin Pop
    functions.
611 61cf6b5e Iustin Pop

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

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

618 61cf6b5e Iustin Pop
    """
619 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
620 61cf6b5e Iustin Pop
621 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
622 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
623 4a8b186a Michael Hanselmann
    """Get the configuration version.
624 4a8b186a Michael Hanselmann

625 4a8b186a Michael Hanselmann
    @return: Config version
626 4a8b186a Michael Hanselmann

627 4a8b186a Michael Hanselmann
    """
628 4a8b186a Michael Hanselmann
    return self._config_data.version
629 4a8b186a Michael Hanselmann
630 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
631 4a8b186a Michael Hanselmann
  def GetClusterName(self):
632 4a8b186a Michael Hanselmann
    """Get cluster name.
633 4a8b186a Michael Hanselmann

634 4a8b186a Michael Hanselmann
    @return: Cluster name
635 4a8b186a Michael Hanselmann

636 4a8b186a Michael Hanselmann
    """
637 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
638 4a8b186a Michael Hanselmann
639 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
640 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
641 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
642 4a8b186a Michael Hanselmann

643 4a8b186a Michael Hanselmann
    @return: Master hostname
644 4a8b186a Michael Hanselmann

645 4a8b186a Michael Hanselmann
    """
646 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
647 4a8b186a Michael Hanselmann
648 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
649 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
650 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
651 4a8b186a Michael Hanselmann

652 4a8b186a Michael Hanselmann
    @return: Master IP
653 4a8b186a Michael Hanselmann

654 4a8b186a Michael Hanselmann
    """
655 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
656 4a8b186a Michael Hanselmann
657 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
658 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
659 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
660 4a8b186a Michael Hanselmann

661 4a8b186a Michael Hanselmann
    """
662 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
663 4a8b186a Michael Hanselmann
664 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
665 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
666 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
667 4a8b186a Michael Hanselmann

668 4a8b186a Michael Hanselmann
    """
669 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
670 4a8b186a Michael Hanselmann
671 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
672 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
673 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
674 4a8b186a Michael Hanselmann

675 4a8b186a Michael Hanselmann
    """
676 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
677 4a8b186a Michael Hanselmann
678 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
679 a8083063 Iustin Pop
  def GetHostKey(self):
680 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
681 a8083063 Iustin Pop

682 c41eea6e Iustin Pop
    @rtype: string
683 c41eea6e Iustin Pop
    @return: the rsa hostkey
684 a8083063 Iustin Pop

685 a8083063 Iustin Pop
    """
686 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
687 a8083063 Iustin Pop
688 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
689 a8083063 Iustin Pop
  def AddInstance(self, instance):
690 a8083063 Iustin Pop
    """Add an instance to the config.
691 a8083063 Iustin Pop

692 a8083063 Iustin Pop
    This should be used after creating a new instance.
693 a8083063 Iustin Pop

694 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
695 c41eea6e Iustin Pop
    @param instance: the instance object
696 c41eea6e Iustin Pop

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

723 a8083063 Iustin Pop
    """
724 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
725 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
726 a8083063 Iustin Pop
727 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
728 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
729 3ecf6786 Iustin Pop
                                      instance_name)
730 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
731 0d68c45d Iustin Pop
    if instance.admin_up != status:
732 0d68c45d Iustin Pop
      instance.admin_up = status
733 b989e85d Iustin Pop
      instance.serial_no += 1
734 d693c864 Iustin Pop
      instance.mtime = time.time()
735 455a3445 Iustin Pop
      self._WriteConfig()
736 a8083063 Iustin Pop
737 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
738 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
739 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
740 6a408fb2 Iustin Pop

741 6a408fb2 Iustin Pop
    """
742 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
743 6a408fb2 Iustin Pop
744 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
745 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
746 a8083063 Iustin Pop
    """Remove the instance from the configuration.
747 a8083063 Iustin Pop

748 a8083063 Iustin Pop
    """
749 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
750 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
751 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
752 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
753 a8083063 Iustin Pop
    self._WriteConfig()
754 a8083063 Iustin Pop
755 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
756 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
757 fc95f88f Iustin Pop
    """Rename an instance.
758 fc95f88f Iustin Pop

759 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
760 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
761 fc95f88f Iustin Pop
    rename.
762 fc95f88f Iustin Pop

763 fc95f88f Iustin Pop
    """
764 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
765 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
766 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
767 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
768 fc95f88f Iustin Pop
    inst.name = new_name
769 b23c4333 Manuel Franceschini
770 b23c4333 Manuel Franceschini
    for disk in inst.disks:
771 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
772 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
773 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
774 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
775 b23c4333 Manuel Franceschini
                                              os.path.join(file_storage_dir,
776 b23c4333 Manuel Franceschini
                                                           inst.name,
777 b23c4333 Manuel Franceschini
                                                           disk.iv_name))
778 b23c4333 Manuel Franceschini
779 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
780 fc95f88f Iustin Pop
    self._WriteConfig()
781 fc95f88f Iustin Pop
782 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
783 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
784 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
785 a8083063 Iustin Pop

786 a8083063 Iustin Pop
    """
787 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
788 a8083063 Iustin Pop
789 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
790 94bbfece Iustin Pop
    """Get the list of instances.
791 94bbfece Iustin Pop

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

794 94bbfece Iustin Pop
    """
795 94bbfece Iustin Pop
    return self._config_data.instances.keys()
796 94bbfece Iustin Pop
797 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
798 a8083063 Iustin Pop
  def GetInstanceList(self):
799 a8083063 Iustin Pop
    """Get the list of instances.
800 a8083063 Iustin Pop

801 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
802 c41eea6e Iustin Pop
        'instance1.example.com']
803 a8083063 Iustin Pop

804 a8083063 Iustin Pop
    """
805 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
806 a8083063 Iustin Pop
807 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
808 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
809 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
810 a8083063 Iustin Pop

811 a8083063 Iustin Pop
    """
812 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
813 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
814 a8083063 Iustin Pop
815 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
816 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
817 94bbfece Iustin Pop

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

820 94bbfece Iustin Pop
    """
821 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
822 94bbfece Iustin Pop
      return None
823 94bbfece Iustin Pop
824 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
825 94bbfece Iustin Pop
826 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
827 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
828 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
829 a8083063 Iustin Pop

830 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
831 a8083063 Iustin Pop
    an instance are taken from the live systems.
832 a8083063 Iustin Pop

833 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
834 c41eea6e Iustin Pop
        I{instance1.example.com}
835 a8083063 Iustin Pop

836 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
837 c41eea6e Iustin Pop
    @return: the instance object
838 a8083063 Iustin Pop

839 a8083063 Iustin Pop
    """
840 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
841 a8083063 Iustin Pop
842 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
843 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
844 0b2de758 Iustin Pop
    """Get the configuration of all instances.
845 0b2de758 Iustin Pop

846 0b2de758 Iustin Pop
    @rtype: dict
847 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
848 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
849 0b2de758 Iustin Pop

850 0b2de758 Iustin Pop
    """
851 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
852 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
853 0b2de758 Iustin Pop
    return my_dict
854 0b2de758 Iustin Pop
855 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
856 a8083063 Iustin Pop
  def AddNode(self, node):
857 a8083063 Iustin Pop
    """Add a node to the configuration.
858 a8083063 Iustin Pop

859 c41eea6e Iustin Pop
    @type node: L{objects.Node}
860 c41eea6e Iustin Pop
    @param node: a Node instance
861 a8083063 Iustin Pop

862 a8083063 Iustin Pop
    """
863 d8470559 Michael Hanselmann
    logging.info("Adding node %s to configuration" % node.name)
864 d8470559 Michael Hanselmann
865 b989e85d Iustin Pop
    node.serial_no = 1
866 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
867 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
868 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
869 a8083063 Iustin Pop
    self._WriteConfig()
870 a8083063 Iustin Pop
871 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
872 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
873 a8083063 Iustin Pop
    """Remove a node from the configuration.
874 a8083063 Iustin Pop

875 a8083063 Iustin Pop
    """
876 d8470559 Michael Hanselmann
    logging.info("Removing node %s from configuration" % node_name)
877 d8470559 Michael Hanselmann
878 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
879 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
880 a8083063 Iustin Pop
881 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
882 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
883 a8083063 Iustin Pop
    self._WriteConfig()
884 a8083063 Iustin Pop
885 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
886 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
887 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
888 a8083063 Iustin Pop

889 a8083063 Iustin Pop
    """
890 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
891 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
892 a8083063 Iustin Pop
893 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
894 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
895 a8083063 Iustin Pop

896 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
897 c41eea6e Iustin Pop
    held.
898 f78ede4e Guido Trotter

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

901 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
902 c41eea6e Iustin Pop
    @return: the node object
903 a8083063 Iustin Pop

904 a8083063 Iustin Pop
    """
905 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
906 a8083063 Iustin Pop
      return None
907 a8083063 Iustin Pop
908 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
909 a8083063 Iustin Pop
910 f78ede4e Guido Trotter
911 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
912 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
913 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
914 f78ede4e Guido Trotter

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

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

919 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
920 c41eea6e Iustin Pop
    @return: the node object
921 f78ede4e Guido Trotter

922 f78ede4e Guido Trotter
    """
923 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
924 f78ede4e Guido Trotter
925 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
926 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
927 a8083063 Iustin Pop

928 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
929 c41eea6e Iustin Pop
    held.
930 c41eea6e Iustin Pop

931 c41eea6e Iustin Pop
    @rtype: list
932 f78ede4e Guido Trotter

933 a8083063 Iustin Pop
    """
934 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
935 a8083063 Iustin Pop
936 f78ede4e Guido Trotter
937 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
938 f78ede4e Guido Trotter
  def GetNodeList(self):
939 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
940 f78ede4e Guido Trotter

941 f78ede4e Guido Trotter
    """
942 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
943 f78ede4e Guido Trotter
944 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
945 94a02bb5 Iustin Pop
  def GetOnlineNodeList(self):
946 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
947 94a02bb5 Iustin Pop

948 94a02bb5 Iustin Pop
    """
949 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
950 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
951 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
952 94a02bb5 Iustin Pop
953 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
954 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
955 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
956 d65e5776 Iustin Pop

957 d65e5776 Iustin Pop
    @rtype: dict
958 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
959 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
960 d65e5776 Iustin Pop

961 d65e5776 Iustin Pop
    """
962 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
963 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
964 d65e5776 Iustin Pop
    return my_dict
965 d65e5776 Iustin Pop
966 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
967 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
968 ec0292f1 Iustin Pop

969 23f06b2b Iustin Pop
    @type exceptions: list
970 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
971 ec0292f1 Iustin Pop
    @rtype: tuple
972 ec0292f1 Iustin Pop
    @return: tuple of (current, desired and possible)
973 ec0292f1 Iustin Pop

974 ec0292f1 Iustin Pop
    """
975 ec0292f1 Iustin Pop
    mc_now = mc_max = 0
976 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
977 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
978 23f06b2b Iustin Pop
        continue
979 5bf07049 Iustin Pop
      if not (node.offline or node.drained):
980 ec0292f1 Iustin Pop
        mc_max += 1
981 ec0292f1 Iustin Pop
      if node.master_candidate:
982 ec0292f1 Iustin Pop
        mc_now += 1
983 ec0292f1 Iustin Pop
    mc_max = min(mc_max, self._config_data.cluster.candidate_pool_size)
984 ec0292f1 Iustin Pop
    return (mc_now, mc_max)
985 ec0292f1 Iustin Pop
986 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
987 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
988 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
989 ec0292f1 Iustin Pop

990 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
991 ec0292f1 Iustin Pop

992 23f06b2b Iustin Pop
    @type exceptions: list
993 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
994 ec0292f1 Iustin Pop
    @rtype: tuple
995 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
996 ec0292f1 Iustin Pop

997 ec0292f1 Iustin Pop
    """
998 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
999 ec0292f1 Iustin Pop
1000 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1001 ec0292f1 Iustin Pop
  def MaintainCandidatePool(self):
1002 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1003 ec0292f1 Iustin Pop

1004 ec0292f1 Iustin Pop
    @rtype: list
1005 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1006 ec0292f1 Iustin Pop

1007 ec0292f1 Iustin Pop
    """
1008 ec0292f1 Iustin Pop
    mc_now, mc_max = self._UnlockedGetMasterCandidateStats()
1009 ec0292f1 Iustin Pop
    mod_list = []
1010 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1011 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1012 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1013 ec0292f1 Iustin Pop
      for name in node_list:
1014 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1015 ec0292f1 Iustin Pop
          break
1016 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1017 5bf07049 Iustin Pop
        if node.master_candidate or node.offline or node.drained:
1018 ec0292f1 Iustin Pop
          continue
1019 ee513a66 Iustin Pop
        mod_list.append(node)
1020 ec0292f1 Iustin Pop
        node.master_candidate = True
1021 ec0292f1 Iustin Pop
        node.serial_no += 1
1022 ec0292f1 Iustin Pop
        mc_now += 1
1023 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1024 ec0292f1 Iustin Pop
        # this should not happen
1025 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1026 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1027 ec0292f1 Iustin Pop
      if mod_list:
1028 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1029 ec0292f1 Iustin Pop
        self._WriteConfig()
1030 ec0292f1 Iustin Pop
1031 ec0292f1 Iustin Pop
    return mod_list
1032 ec0292f1 Iustin Pop
1033 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1034 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1035 a8083063 Iustin Pop

1036 a8083063 Iustin Pop
    """
1037 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1038 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
1039 a8083063 Iustin Pop
1040 a8083063 Iustin Pop
  def _OpenConfig(self):
1041 a8083063 Iustin Pop
    """Read the config data from disk.
1042 a8083063 Iustin Pop

1043 a8083063 Iustin Pop
    """
1044 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
1045 13998ef2 Michael Hanselmann
1046 a8083063 Iustin Pop
    try:
1047 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
1048 13998ef2 Michael Hanselmann
    except Exception, err:
1049 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
1050 5b263ed7 Michael Hanselmann
1051 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
1052 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
1053 5b263ed7 Michael Hanselmann
1054 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
1055 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
1056 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
1057 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
1058 90d726a8 Iustin Pop
1059 90d726a8 Iustin Pop
    # Upgrade configuration if needed
1060 90d726a8 Iustin Pop
    data.UpgradeConfig()
1061 90d726a8 Iustin Pop
1062 a8083063 Iustin Pop
    self._config_data = data
1063 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
1064 0779e3aa Iustin Pop
    # ssconf update
1065 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
1066 a8083063 Iustin Pop
1067 a8083063 Iustin Pop
  def _DistributeConfig(self):
1068 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
1069 a8083063 Iustin Pop

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

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

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

1142 054596f0 Iustin Pop
    @rtype: dict
1143 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1144 054596f0 Iustin Pop
        associated value
1145 054596f0 Iustin Pop

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

1190 a8083063 Iustin Pop
    """
1191 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1192 a8083063 Iustin Pop
1193 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1194 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1195 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1196 89ff8e15 Manuel Franceschini

1197 89ff8e15 Manuel Franceschini
    """
1198 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1199 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1200 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1201 89ff8e15 Manuel Franceschini
1202 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1203 a8083063 Iustin Pop
  def GetMACPrefix(self):
1204 a8083063 Iustin Pop
    """Return the mac prefix.
1205 a8083063 Iustin Pop

1206 a8083063 Iustin Pop
    """
1207 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1208 62779dd0 Iustin Pop
1209 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1210 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1211 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
1212 62779dd0 Iustin Pop

1213 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1214 c41eea6e Iustin Pop
    @return: the cluster object
1215 62779dd0 Iustin Pop

1216 62779dd0 Iustin Pop
    """
1217 62779dd0 Iustin Pop
    return self._config_data.cluster
1218 e00fb268 Iustin Pop
1219 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1220 e00fb268 Iustin Pop
  def Update(self, target):
1221 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1222 e00fb268 Iustin Pop

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

1229 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1230 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1231 c41eea6e Iustin Pop
        the cluster
1232 c41eea6e Iustin Pop

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