Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 5bf07049

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

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

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

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

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

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

102 a8083063 Iustin Pop
    This should check the current instances for duplicates.
103 a8083063 Iustin Pop

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

124 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
125 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
126 1862d460 Alexander Schreiber

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

135 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
136 f9518d38 Iustin Pop

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

152 923b1523 Iustin Pop
    """
153 923b1523 Iustin Pop
    lvnames = set()
154 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
155 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
156 923b1523 Iustin Pop
      for lv_list in node_data.values():
157 923b1523 Iustin Pop
        lvnames.update(lv_list)
158 923b1523 Iustin Pop
    return lvnames
159 923b1523 Iustin Pop
160 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
161 923b1523 Iustin Pop
  def GenerateUniqueID(self, exceptions=None):
162 923b1523 Iustin Pop
    """Generate an unique disk name.
163 923b1523 Iustin Pop

164 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
165 923b1523 Iustin Pop
    duplicates.
166 923b1523 Iustin Pop

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

172 c41eea6e Iustin Pop
    @rtype: string
173 c41eea6e Iustin Pop
    @return: the unique id
174 923b1523 Iustin Pop

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

197 c41eea6e Iustin Pop
    @rtype: list
198 c41eea6e Iustin Pop
    @return: the list of all MACs
199 c41eea6e Iustin Pop

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

211 c41eea6e Iustin Pop
    @rtype: list
212 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
213 c41eea6e Iustin Pop

214 f9518d38 Iustin Pop
    """
215 f9518d38 Iustin Pop
    def helper(disk, result):
216 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
217 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
218 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
219 f9518d38 Iustin Pop
      if disk.children:
220 f9518d38 Iustin Pop
        for child in disk.children:
221 f9518d38 Iustin Pop
          helper(child, result)
222 f9518d38 Iustin Pop
223 f9518d38 Iustin Pop
    result = []
224 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
225 f9518d38 Iustin Pop
      for disk in instance.disks:
226 f9518d38 Iustin Pop
        helper(disk, result)
227 f9518d38 Iustin Pop
228 f9518d38 Iustin Pop
    return result
229 f9518d38 Iustin Pop
230 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
231 a8efbb40 Iustin Pop
    """Verify function.
232 a8efbb40 Iustin Pop

233 4a89c54a Iustin Pop
    @rtype: list
234 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
235 4a89c54a Iustin Pop
        configuration errors
236 4a89c54a Iustin Pop

237 a8083063 Iustin Pop
    """
238 a8083063 Iustin Pop
    result = []
239 a8083063 Iustin Pop
    seen_macs = []
240 48ce9fd9 Iustin Pop
    ports = {}
241 a8083063 Iustin Pop
    data = self._config_data
242 a8083063 Iustin Pop
    for instance_name in data.instances:
243 a8083063 Iustin Pop
      instance = data.instances[instance_name]
244 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
245 8522ceeb Iustin Pop
        result.append("instance '%s' has invalid primary node '%s'" %
246 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
247 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
248 a8083063 Iustin Pop
        if snode not in data.nodes:
249 8522ceeb Iustin Pop
          result.append("instance '%s' has invalid secondary node '%s'" %
250 a8083063 Iustin Pop
                        (instance_name, snode))
251 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
252 a8083063 Iustin Pop
        if nic.mac in seen_macs:
253 8522ceeb Iustin Pop
          result.append("instance '%s' has NIC %d mac %s duplicate" %
254 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
255 a8083063 Iustin Pop
        else:
256 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
257 48ce9fd9 Iustin Pop
258 48ce9fd9 Iustin Pop
      # gather the drbd ports for duplicate checks
259 48ce9fd9 Iustin Pop
      for dsk in instance.disks:
260 48ce9fd9 Iustin Pop
        if dsk.dev_type in constants.LDS_DRBD:
261 48ce9fd9 Iustin Pop
          tcp_port = dsk.logical_id[2]
262 48ce9fd9 Iustin Pop
          if tcp_port not in ports:
263 48ce9fd9 Iustin Pop
            ports[tcp_port] = []
264 48ce9fd9 Iustin Pop
          ports[tcp_port].append((instance.name, "drbd disk %s" % dsk.iv_name))
265 48ce9fd9 Iustin Pop
      # gather network port reservation
266 48ce9fd9 Iustin Pop
      net_port = getattr(instance, "network_port", None)
267 48ce9fd9 Iustin Pop
      if net_port is not None:
268 48ce9fd9 Iustin Pop
        if net_port not in ports:
269 48ce9fd9 Iustin Pop
          ports[net_port] = []
270 48ce9fd9 Iustin Pop
        ports[net_port].append((instance.name, "network port"))
271 48ce9fd9 Iustin Pop
272 332d0e37 Iustin Pop
      # instance disk verify
273 332d0e37 Iustin Pop
      for idx, disk in enumerate(instance.disks):
274 332d0e37 Iustin Pop
        result.extend(["instance '%s' disk %d error: %s" %
275 332d0e37 Iustin Pop
                       (instance.name, idx, msg) for msg in disk.Verify()])
276 332d0e37 Iustin Pop
277 48ce9fd9 Iustin Pop
    # cluster-wide pool of free ports
278 a8efbb40 Iustin Pop
    for free_port in data.cluster.tcpudp_port_pool:
279 48ce9fd9 Iustin Pop
      if free_port not in ports:
280 48ce9fd9 Iustin Pop
        ports[free_port] = []
281 48ce9fd9 Iustin Pop
      ports[free_port].append(("cluster", "port marked as free"))
282 48ce9fd9 Iustin Pop
283 48ce9fd9 Iustin Pop
    # compute tcp/udp duplicate ports
284 48ce9fd9 Iustin Pop
    keys = ports.keys()
285 48ce9fd9 Iustin Pop
    keys.sort()
286 48ce9fd9 Iustin Pop
    for pnum in keys:
287 48ce9fd9 Iustin Pop
      pdata = ports[pnum]
288 48ce9fd9 Iustin Pop
      if len(pdata) > 1:
289 48ce9fd9 Iustin Pop
        txt = ", ".join(["%s/%s" % val for val in pdata])
290 48ce9fd9 Iustin Pop
        result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
291 48ce9fd9 Iustin Pop
292 48ce9fd9 Iustin Pop
    # highest used tcp port check
293 48ce9fd9 Iustin Pop
    if keys:
294 a8efbb40 Iustin Pop
      if keys[-1] > data.cluster.highest_used_port:
295 48ce9fd9 Iustin Pop
        result.append("Highest used port mismatch, saved %s, computed %s" %
296 a8efbb40 Iustin Pop
                      (data.cluster.highest_used_port, keys[-1]))
297 a8efbb40 Iustin Pop
298 3a26773f Iustin Pop
    if not data.nodes[data.cluster.master_node].master_candidate:
299 3a26773f Iustin Pop
      result.append("Master node is not a master candidate")
300 3a26773f Iustin Pop
301 4a89c54a Iustin Pop
    # master candidate checks
302 ec0292f1 Iustin Pop
    mc_now, mc_max = self._UnlockedGetMasterCandidateStats()
303 ec0292f1 Iustin Pop
    if mc_now < mc_max:
304 ec0292f1 Iustin Pop
      result.append("Not enough master candidates: actual %d, target %d" %
305 ec0292f1 Iustin Pop
                    (mc_now, mc_max))
306 48ce9fd9 Iustin Pop
307 5bf07049 Iustin Pop
    # node checks
308 5bf07049 Iustin Pop
    for node in data.nodes.values():
309 5bf07049 Iustin Pop
      if [node.master_candidate, node.drained, node.offline].count(True) > 1:
310 5bf07049 Iustin Pop
        result.append("Node %s state is invalid: master_candidate=%s,"
311 5bf07049 Iustin Pop
                      " drain=%s, offline=%s" %
312 5bf07049 Iustin Pop
                      (node.name, node.master_candidate, node.drain,
313 5bf07049 Iustin Pop
                       node.offline))
314 5bf07049 Iustin Pop
315 4a89c54a Iustin Pop
    # drbd minors check
316 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
317 4a89c54a Iustin Pop
    for node, minor, instance_a, instance_b in duplicates:
318 4a89c54a Iustin Pop
      result.append("DRBD minor %d on node %s is assigned twice to instances"
319 4a89c54a Iustin Pop
                    " %s and %s" % (minor, node, instance_a, instance_b))
320 4a89c54a Iustin Pop
321 a8083063 Iustin Pop
    return result
322 a8083063 Iustin Pop
323 4a89c54a Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
324 4a89c54a Iustin Pop
  def VerifyConfig(self):
325 4a89c54a Iustin Pop
    """Verify function.
326 4a89c54a Iustin Pop

327 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
328 4a89c54a Iustin Pop

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

333 4a89c54a Iustin Pop
    """
334 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
335 4a89c54a Iustin Pop
336 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
337 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
338 a8083063 Iustin Pop

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

341 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
342 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
343 a8083063 Iustin Pop
    node.
344 a8083063 Iustin Pop

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

347 a8083063 Iustin Pop
    """
348 a8083063 Iustin Pop
    if disk.children:
349 a8083063 Iustin Pop
      for child in disk.children:
350 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
351 a8083063 Iustin Pop
352 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
353 a8083063 Iustin Pop
      return
354 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
355 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
356 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
357 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
358 3ecf6786 Iustin Pop
                                        node_name)
359 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
360 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
361 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
362 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
363 a8083063 Iustin Pop
                                        " for %s" % str(disk))
364 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
365 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
366 a8083063 Iustin Pop
      if pnode == node_name:
367 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
368 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
369 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
370 a8083063 Iustin Pop
    else:
371 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
372 a8083063 Iustin Pop
    return
373 a8083063 Iustin Pop
374 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
375 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
376 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
377 f78ede4e Guido Trotter

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

380 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
381 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
382 f78ede4e Guido Trotter
    node.
383 f78ede4e Guido Trotter

384 f78ede4e Guido Trotter
    """
385 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
386 f78ede4e Guido Trotter
387 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
388 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
389 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
390 b2fddf63 Iustin Pop

391 b2fddf63 Iustin Pop
    """
392 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
393 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
394 264bb3c5 Michael Hanselmann
395 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
396 264bb3c5 Michael Hanselmann
    self._WriteConfig()
397 264bb3c5 Michael Hanselmann
398 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
399 b2fddf63 Iustin Pop
  def GetPortList(self):
400 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
401 264bb3c5 Michael Hanselmann

402 264bb3c5 Michael Hanselmann
    """
403 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
404 264bb3c5 Michael Hanselmann
405 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
406 a8083063 Iustin Pop
  def AllocatePort(self):
407 a8083063 Iustin Pop
    """Allocate a port.
408 a8083063 Iustin Pop

409 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
410 b2fddf63 Iustin Pop
    default port range (and in this case we increase
411 b2fddf63 Iustin Pop
    highest_used_port).
412 a8083063 Iustin Pop

413 a8083063 Iustin Pop
    """
414 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
415 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
416 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
417 264bb3c5 Michael Hanselmann
    else:
418 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
419 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
420 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
421 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
422 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
423 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
424 a8083063 Iustin Pop
425 a8083063 Iustin Pop
    self._WriteConfig()
426 a8083063 Iustin Pop
    return port
427 a8083063 Iustin Pop
428 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
429 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
430 a81c53c9 Iustin Pop

431 4a89c54a Iustin Pop
    @rtype: (dict, list)
432 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
433 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
434 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
435 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
436 4a89c54a Iustin Pop
        should raise an exception
437 a81c53c9 Iustin Pop

438 a81c53c9 Iustin Pop
    """
439 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
440 4a89c54a Iustin Pop
      duplicates = []
441 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
442 f9518d38 Iustin Pop
        nodeA, nodeB, dummy, minorA, minorB = disk.logical_id[:5]
443 a81c53c9 Iustin Pop
        for node, port in ((nodeA, minorA), (nodeB, minorB)):
444 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
445 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
446 a81c53c9 Iustin Pop
          if port in used[node]:
447 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
448 4a89c54a Iustin Pop
          else:
449 4a89c54a Iustin Pop
            used[node][port] = instance_name
450 a81c53c9 Iustin Pop
      if disk.children:
451 a81c53c9 Iustin Pop
        for child in disk.children:
452 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
453 4a89c54a Iustin Pop
      return duplicates
454 a81c53c9 Iustin Pop
455 4a89c54a Iustin Pop
    duplicates = []
456 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
457 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
458 79b26a7a Iustin Pop
      for disk in instance.disks:
459 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
460 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
461 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
462 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
463 4a89c54a Iustin Pop
      else:
464 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
465 4a89c54a Iustin Pop
    return my_dict, duplicates
466 a81c53c9 Iustin Pop
467 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
468 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
469 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
470 6d2e83d5 Iustin Pop

471 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
472 6d2e83d5 Iustin Pop

473 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
474 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
475 6d2e83d5 Iustin Pop
        an empty list).
476 6d2e83d5 Iustin Pop

477 6d2e83d5 Iustin Pop
    """
478 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
479 4a89c54a Iustin Pop
    if duplicates:
480 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
481 4a89c54a Iustin Pop
                                      str(duplicates))
482 4a89c54a Iustin Pop
    return d_map
483 6d2e83d5 Iustin Pop
484 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
485 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
486 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
487 a81c53c9 Iustin Pop

488 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
489 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
490 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
491 a81c53c9 Iustin Pop
    order as the passed nodes.
492 a81c53c9 Iustin Pop

493 32388e6d Iustin Pop
    @type instance: string
494 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
495 32388e6d Iustin Pop

496 a81c53c9 Iustin Pop
    """
497 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
498 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
499 32388e6d Iustin Pop
500 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
501 4a89c54a Iustin Pop
    if duplicates:
502 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
503 4a89c54a Iustin Pop
                                      str(duplicates))
504 a81c53c9 Iustin Pop
    result = []
505 a81c53c9 Iustin Pop
    for nname in nodes:
506 a81c53c9 Iustin Pop
      ndata = d_map[nname]
507 a81c53c9 Iustin Pop
      if not ndata:
508 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
509 a81c53c9 Iustin Pop
        result.append(0)
510 a81c53c9 Iustin Pop
        ndata[0] = instance
511 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
512 a81c53c9 Iustin Pop
        continue
513 a81c53c9 Iustin Pop
      keys = ndata.keys()
514 a81c53c9 Iustin Pop
      keys.sort()
515 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
516 a81c53c9 Iustin Pop
      if ffree is None:
517 a81c53c9 Iustin Pop
        # return the next minor
518 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
519 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
520 a81c53c9 Iustin Pop
      else:
521 a81c53c9 Iustin Pop
        minor = ffree
522 4a89c54a Iustin Pop
      # double-check minor against current instances
523 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
524 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
525 4a89c54a Iustin Pop
              " already allocated to instance %s" %
526 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
527 a81c53c9 Iustin Pop
      ndata[minor] = instance
528 4a89c54a Iustin Pop
      # double-check minor against reservation
529 4a89c54a Iustin Pop
      r_key = (nname, minor)
530 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
531 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
532 4a89c54a Iustin Pop
              " reserved for instance %s" %
533 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
534 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
535 4a89c54a Iustin Pop
      result.append(minor)
536 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
537 a81c53c9 Iustin Pop
                  nodes, result)
538 a81c53c9 Iustin Pop
    return result
539 a81c53c9 Iustin Pop
540 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
541 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
542 a81c53c9 Iustin Pop

543 a81c53c9 Iustin Pop
    @type instance: string
544 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
545 a81c53c9 Iustin Pop
                     released
546 a81c53c9 Iustin Pop

547 a81c53c9 Iustin Pop
    """
548 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
549 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
550 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
551 a81c53c9 Iustin Pop
      if name == instance:
552 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
553 a81c53c9 Iustin Pop
554 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
555 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
556 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
557 61cf6b5e Iustin Pop

558 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
559 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
560 61cf6b5e Iustin Pop
    functions.
561 61cf6b5e Iustin Pop

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

564 61cf6b5e Iustin Pop
    @type instance: string
565 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
566 61cf6b5e Iustin Pop
                     released
567 61cf6b5e Iustin Pop

568 61cf6b5e Iustin Pop
    """
569 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
570 61cf6b5e Iustin Pop
571 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
572 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
573 4a8b186a Michael Hanselmann
    """Get the configuration version.
574 4a8b186a Michael Hanselmann

575 4a8b186a Michael Hanselmann
    @return: Config version
576 4a8b186a Michael Hanselmann

577 4a8b186a Michael Hanselmann
    """
578 4a8b186a Michael Hanselmann
    return self._config_data.version
579 4a8b186a Michael Hanselmann
580 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
581 4a8b186a Michael Hanselmann
  def GetClusterName(self):
582 4a8b186a Michael Hanselmann
    """Get cluster name.
583 4a8b186a Michael Hanselmann

584 4a8b186a Michael Hanselmann
    @return: Cluster name
585 4a8b186a Michael Hanselmann

586 4a8b186a Michael Hanselmann
    """
587 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
588 4a8b186a Michael Hanselmann
589 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
590 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
591 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
592 4a8b186a Michael Hanselmann

593 4a8b186a Michael Hanselmann
    @return: Master hostname
594 4a8b186a Michael Hanselmann

595 4a8b186a Michael Hanselmann
    """
596 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
597 4a8b186a Michael Hanselmann
598 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
599 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
600 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
601 4a8b186a Michael Hanselmann

602 4a8b186a Michael Hanselmann
    @return: Master IP
603 4a8b186a Michael Hanselmann

604 4a8b186a Michael Hanselmann
    """
605 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
606 4a8b186a Michael Hanselmann
607 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
608 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
609 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
610 4a8b186a Michael Hanselmann

611 4a8b186a Michael Hanselmann
    """
612 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
613 4a8b186a Michael Hanselmann
614 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
615 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
616 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
617 4a8b186a Michael Hanselmann

618 4a8b186a Michael Hanselmann
    """
619 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
620 4a8b186a Michael Hanselmann
621 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
622 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
623 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
624 4a8b186a Michael Hanselmann

625 4a8b186a Michael Hanselmann
    """
626 64272529 Iustin Pop
    return self._config_data.cluster.default_hypervisor
627 4a8b186a Michael Hanselmann
628 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
629 a8083063 Iustin Pop
  def GetHostKey(self):
630 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
631 a8083063 Iustin Pop

632 c41eea6e Iustin Pop
    @rtype: string
633 c41eea6e Iustin Pop
    @return: the rsa hostkey
634 a8083063 Iustin Pop

635 a8083063 Iustin Pop
    """
636 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
637 a8083063 Iustin Pop
638 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
639 a8083063 Iustin Pop
  def AddInstance(self, instance):
640 a8083063 Iustin Pop
    """Add an instance to the config.
641 a8083063 Iustin Pop

642 a8083063 Iustin Pop
    This should be used after creating a new instance.
643 a8083063 Iustin Pop

644 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
645 c41eea6e Iustin Pop
    @param instance: the instance object
646 c41eea6e Iustin Pop

647 a8083063 Iustin Pop
    """
648 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
649 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
650 a8083063 Iustin Pop
651 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
652 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
653 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
654 923b1523 Iustin Pop
655 b989e85d Iustin Pop
    instance.serial_no = 1
656 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
657 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
658 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
659 a8083063 Iustin Pop
    self._WriteConfig()
660 a8083063 Iustin Pop
661 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
662 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
663 a8083063 Iustin Pop

664 a8083063 Iustin Pop
    """
665 0d68c45d Iustin Pop
    assert isinstance(status, bool), \
666 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
667 a8083063 Iustin Pop
668 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
669 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
670 3ecf6786 Iustin Pop
                                      instance_name)
671 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
672 0d68c45d Iustin Pop
    if instance.admin_up != status:
673 0d68c45d Iustin Pop
      instance.admin_up = status
674 b989e85d Iustin Pop
      instance.serial_no += 1
675 455a3445 Iustin Pop
      self._WriteConfig()
676 a8083063 Iustin Pop
677 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
678 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
679 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
680 6a408fb2 Iustin Pop

681 6a408fb2 Iustin Pop
    """
682 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, True)
683 6a408fb2 Iustin Pop
684 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
685 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
686 a8083063 Iustin Pop
    """Remove the instance from the configuration.
687 a8083063 Iustin Pop

688 a8083063 Iustin Pop
    """
689 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
690 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
691 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
692 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
693 a8083063 Iustin Pop
    self._WriteConfig()
694 a8083063 Iustin Pop
695 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
696 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
697 fc95f88f Iustin Pop
    """Rename an instance.
698 fc95f88f Iustin Pop

699 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
700 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
701 fc95f88f Iustin Pop
    rename.
702 fc95f88f Iustin Pop

703 fc95f88f Iustin Pop
    """
704 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
705 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
706 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
707 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
708 fc95f88f Iustin Pop
    inst.name = new_name
709 b23c4333 Manuel Franceschini
710 b23c4333 Manuel Franceschini
    for disk in inst.disks:
711 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
712 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
713 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
714 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
715 b23c4333 Manuel Franceschini
                                              os.path.join(file_storage_dir,
716 b23c4333 Manuel Franceschini
                                                           inst.name,
717 b23c4333 Manuel Franceschini
                                                           disk.iv_name))
718 b23c4333 Manuel Franceschini
719 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
720 fc95f88f Iustin Pop
    self._WriteConfig()
721 fc95f88f Iustin Pop
722 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
723 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
724 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
725 a8083063 Iustin Pop

726 a8083063 Iustin Pop
    """
727 0d68c45d Iustin Pop
    self._SetInstanceStatus(instance_name, False)
728 a8083063 Iustin Pop
729 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
730 94bbfece Iustin Pop
    """Get the list of instances.
731 94bbfece Iustin Pop

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

734 94bbfece Iustin Pop
    """
735 94bbfece Iustin Pop
    return self._config_data.instances.keys()
736 94bbfece Iustin Pop
737 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
738 a8083063 Iustin Pop
  def GetInstanceList(self):
739 a8083063 Iustin Pop
    """Get the list of instances.
740 a8083063 Iustin Pop

741 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
742 c41eea6e Iustin Pop
        'instance1.example.com']
743 a8083063 Iustin Pop

744 a8083063 Iustin Pop
    """
745 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
746 a8083063 Iustin Pop
747 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
748 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
749 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
750 a8083063 Iustin Pop

751 a8083063 Iustin Pop
    """
752 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
753 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
754 a8083063 Iustin Pop
755 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
756 94bbfece Iustin Pop
    """Returns informations about an instance.
757 94bbfece Iustin Pop

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

760 94bbfece Iustin Pop
    """
761 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
762 94bbfece Iustin Pop
      return None
763 94bbfece Iustin Pop
764 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
765 94bbfece Iustin Pop
766 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
767 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
768 a8083063 Iustin Pop
    """Returns informations about an instance.
769 a8083063 Iustin Pop

770 a8083063 Iustin Pop
    It takes the information from the configuration file. Other informations of
771 a8083063 Iustin Pop
    an instance are taken from the live systems.
772 a8083063 Iustin Pop

773 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
774 c41eea6e Iustin Pop
        I{instance1.example.com}
775 a8083063 Iustin Pop

776 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
777 c41eea6e Iustin Pop
    @return: the instance object
778 a8083063 Iustin Pop

779 a8083063 Iustin Pop
    """
780 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
781 a8083063 Iustin Pop
782 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
783 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
784 0b2de758 Iustin Pop
    """Get the configuration of all instances.
785 0b2de758 Iustin Pop

786 0b2de758 Iustin Pop
    @rtype: dict
787 0b2de758 Iustin Pop
    @returns: dict of (instance, instance_info), where instance_info is what
788 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
789 0b2de758 Iustin Pop

790 0b2de758 Iustin Pop
    """
791 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
792 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
793 0b2de758 Iustin Pop
    return my_dict
794 0b2de758 Iustin Pop
795 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
796 a8083063 Iustin Pop
  def AddNode(self, node):
797 a8083063 Iustin Pop
    """Add a node to the configuration.
798 a8083063 Iustin Pop

799 c41eea6e Iustin Pop
    @type node: L{objects.Node}
800 c41eea6e Iustin Pop
    @param node: a Node instance
801 a8083063 Iustin Pop

802 a8083063 Iustin Pop
    """
803 d8470559 Michael Hanselmann
    logging.info("Adding node %s to configuration" % node.name)
804 d8470559 Michael Hanselmann
805 b989e85d Iustin Pop
    node.serial_no = 1
806 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
807 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
808 a8083063 Iustin Pop
    self._WriteConfig()
809 a8083063 Iustin Pop
810 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
811 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
812 a8083063 Iustin Pop
    """Remove a node from the configuration.
813 a8083063 Iustin Pop

814 a8083063 Iustin Pop
    """
815 d8470559 Michael Hanselmann
    logging.info("Removing node %s from configuration" % node_name)
816 d8470559 Michael Hanselmann
817 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
818 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
819 a8083063 Iustin Pop
820 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
821 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
822 a8083063 Iustin Pop
    self._WriteConfig()
823 a8083063 Iustin Pop
824 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
825 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
826 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
827 a8083063 Iustin Pop

828 a8083063 Iustin Pop
    """
829 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
830 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
831 a8083063 Iustin Pop
832 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
833 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
834 a8083063 Iustin Pop

835 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
836 c41eea6e Iustin Pop
    held.
837 f78ede4e Guido Trotter

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

840 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
841 c41eea6e Iustin Pop
    @return: the node object
842 a8083063 Iustin Pop

843 a8083063 Iustin Pop
    """
844 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
845 a8083063 Iustin Pop
      return None
846 a8083063 Iustin Pop
847 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
848 a8083063 Iustin Pop
849 f78ede4e Guido Trotter
850 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
851 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
852 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
853 f78ede4e Guido Trotter

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

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

858 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
859 c41eea6e Iustin Pop
    @return: the node object
860 f78ede4e Guido Trotter

861 f78ede4e Guido Trotter
    """
862 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
863 f78ede4e Guido Trotter
864 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
865 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
866 a8083063 Iustin Pop

867 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
868 c41eea6e Iustin Pop
    held.
869 c41eea6e Iustin Pop

870 c41eea6e Iustin Pop
    @rtype: list
871 f78ede4e Guido Trotter

872 a8083063 Iustin Pop
    """
873 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
874 a8083063 Iustin Pop
875 f78ede4e Guido Trotter
876 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
877 f78ede4e Guido Trotter
  def GetNodeList(self):
878 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
879 f78ede4e Guido Trotter

880 f78ede4e Guido Trotter
    """
881 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
882 f78ede4e Guido Trotter
883 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
884 94a02bb5 Iustin Pop
  def GetOnlineNodeList(self):
885 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
886 94a02bb5 Iustin Pop

887 94a02bb5 Iustin Pop
    """
888 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
889 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
890 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
891 94a02bb5 Iustin Pop
892 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
893 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
894 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
895 d65e5776 Iustin Pop

896 d65e5776 Iustin Pop
    @rtype: dict
897 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
898 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
899 d65e5776 Iustin Pop

900 d65e5776 Iustin Pop
    """
901 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
902 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
903 d65e5776 Iustin Pop
    return my_dict
904 d65e5776 Iustin Pop
905 ec0292f1 Iustin Pop
  def _UnlockedGetMasterCandidateStats(self):
906 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
907 ec0292f1 Iustin Pop

908 ec0292f1 Iustin Pop
    @rtype: tuple
909 ec0292f1 Iustin Pop
    @return: tuple of (current, desired and possible)
910 ec0292f1 Iustin Pop

911 ec0292f1 Iustin Pop
    """
912 ec0292f1 Iustin Pop
    mc_now = mc_max = 0
913 ec0292f1 Iustin Pop
    for node in self._config_data.nodes.itervalues():
914 5bf07049 Iustin Pop
      if not (node.offline or node.drained):
915 ec0292f1 Iustin Pop
        mc_max += 1
916 ec0292f1 Iustin Pop
      if node.master_candidate:
917 ec0292f1 Iustin Pop
        mc_now += 1
918 ec0292f1 Iustin Pop
    mc_max = min(mc_max, self._config_data.cluster.candidate_pool_size)
919 ec0292f1 Iustin Pop
    return (mc_now, mc_max)
920 ec0292f1 Iustin Pop
921 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
922 ec0292f1 Iustin Pop
  def GetMasterCandidateStats(self):
923 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
924 ec0292f1 Iustin Pop

925 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
926 ec0292f1 Iustin Pop

927 ec0292f1 Iustin Pop
    @rtype: tuple
928 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
929 ec0292f1 Iustin Pop

930 ec0292f1 Iustin Pop
    """
931 ec0292f1 Iustin Pop
    return self._UnlockedGetMasterCandidateStats()
932 ec0292f1 Iustin Pop
933 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
934 ec0292f1 Iustin Pop
  def MaintainCandidatePool(self):
935 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
936 ec0292f1 Iustin Pop

937 ec0292f1 Iustin Pop
    @rtype: list
938 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
939 ec0292f1 Iustin Pop

940 ec0292f1 Iustin Pop
    """
941 ec0292f1 Iustin Pop
    mc_now, mc_max = self._UnlockedGetMasterCandidateStats()
942 ec0292f1 Iustin Pop
    mod_list = []
943 ec0292f1 Iustin Pop
    if mc_now < mc_max:
944 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
945 ec0292f1 Iustin Pop
      random.shuffle(node_list)
946 ec0292f1 Iustin Pop
      for name in node_list:
947 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
948 ec0292f1 Iustin Pop
          break
949 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
950 5bf07049 Iustin Pop
        if node.master_candidate or node.offline or node.drained:
951 ec0292f1 Iustin Pop
          continue
952 ee513a66 Iustin Pop
        mod_list.append(node)
953 ec0292f1 Iustin Pop
        node.master_candidate = True
954 ec0292f1 Iustin Pop
        node.serial_no += 1
955 ec0292f1 Iustin Pop
        mc_now += 1
956 ec0292f1 Iustin Pop
      if mc_now != mc_max:
957 ec0292f1 Iustin Pop
        # this should not happen
958 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
959 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
960 ec0292f1 Iustin Pop
      if mod_list:
961 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
962 ec0292f1 Iustin Pop
        self._WriteConfig()
963 ec0292f1 Iustin Pop
964 ec0292f1 Iustin Pop
    return mod_list
965 ec0292f1 Iustin Pop
966 a8083063 Iustin Pop
  def _BumpSerialNo(self):
967 a8083063 Iustin Pop
    """Bump up the serial number of the config.
968 a8083063 Iustin Pop

969 a8083063 Iustin Pop
    """
970 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
971 a8083063 Iustin Pop
972 a8083063 Iustin Pop
  def _OpenConfig(self):
973 a8083063 Iustin Pop
    """Read the config data from disk.
974 a8083063 Iustin Pop

975 a8083063 Iustin Pop
    """
976 a8083063 Iustin Pop
    f = open(self._cfg_file, 'r')
977 a8083063 Iustin Pop
    try:
978 a8083063 Iustin Pop
      try:
979 8d14b30d Iustin Pop
        data = objects.ConfigData.FromDict(serializer.Load(f.read()))
980 a8083063 Iustin Pop
      except Exception, err:
981 3ecf6786 Iustin Pop
        raise errors.ConfigurationError(err)
982 a8083063 Iustin Pop
    finally:
983 a8083063 Iustin Pop
      f.close()
984 5b263ed7 Michael Hanselmann
985 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
986 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
987 5b263ed7 Michael Hanselmann
988 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
989 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
990 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
991 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
992 a8083063 Iustin Pop
    self._config_data = data
993 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
994 0779e3aa Iustin Pop
    # ssconf update
995 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
996 a8083063 Iustin Pop
997 a8083063 Iustin Pop
  def _DistributeConfig(self):
998 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
999 a8083063 Iustin Pop

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

1003 a8083063 Iustin Pop
    """
1004 a8083063 Iustin Pop
    if self._offline:
1005 a8083063 Iustin Pop
      return True
1006 a8083063 Iustin Pop
    bad = False
1007 a8083063 Iustin Pop
1008 6a5b8b4b Iustin Pop
    node_list = []
1009 6a5b8b4b Iustin Pop
    addr_list = []
1010 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
1011 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
1012 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
1013 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
1014 6b294c53 Iustin Pop
    # in between
1015 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
1016 6a5b8b4b Iustin Pop
      if node_name == myhostname:
1017 6a5b8b4b Iustin Pop
        continue
1018 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
1019 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
1020 6a5b8b4b Iustin Pop
        continue
1021 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
1022 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
1023 6b294c53 Iustin Pop
1024 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
1025 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
1026 6a5b8b4b Iustin Pop
    for node in node_list:
1027 a8083063 Iustin Pop
      if not result[node]:
1028 74a48621 Iustin Pop
        logging.error("copy of file %s to node %s failed",
1029 74a48621 Iustin Pop
                      self._cfg_file, node)
1030 a8083063 Iustin Pop
        bad = True
1031 a8083063 Iustin Pop
    return not bad
1032 a8083063 Iustin Pop
1033 a8083063 Iustin Pop
  def _WriteConfig(self, destination=None):
1034 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
1035 a8083063 Iustin Pop

1036 a8083063 Iustin Pop
    """
1037 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
1038 4a89c54a Iustin Pop
    if config_errors:
1039 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Configuration data is not"
1040 4a89c54a Iustin Pop
                                      " consistent: %s" %
1041 4a89c54a Iustin Pop
                                      (", ".join(config_errors)))
1042 a8083063 Iustin Pop
    if destination is None:
1043 a8083063 Iustin Pop
      destination = self._cfg_file
1044 a8083063 Iustin Pop
    self._BumpSerialNo()
1045 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
1046 a8083063 Iustin Pop
    dir_name, file_name = os.path.split(destination)
1047 a8083063 Iustin Pop
    fd, name = tempfile.mkstemp('.newconfig', file_name, dir_name)
1048 a8083063 Iustin Pop
    f = os.fdopen(fd, 'w')
1049 a8083063 Iustin Pop
    try:
1050 8d14b30d Iustin Pop
      f.write(txt)
1051 a8083063 Iustin Pop
      os.fsync(f.fileno())
1052 a8083063 Iustin Pop
    finally:
1053 a8083063 Iustin Pop
      f.close()
1054 a8083063 Iustin Pop
    # we don't need to do os.close(fd) as f.close() did it
1055 a8083063 Iustin Pop
    os.rename(name, destination)
1056 14e15659 Iustin Pop
    self.write_count += 1
1057 3d3a04bc Iustin Pop
1058 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
1059 a8083063 Iustin Pop
    self._DistributeConfig()
1060 a8083063 Iustin Pop
1061 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
1062 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
1063 d9a855f1 Michael Hanselmann
      if not self._offline:
1064 03d1dba2 Michael Hanselmann
        rpc.RpcRunner.call_write_ssconf_files(self._UnlockedGetNodeList(),
1065 03d1dba2 Michael Hanselmann
                                              self._UnlockedGetSsconfValues())
1066 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
1067 54d1a06e Michael Hanselmann
1068 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
1069 054596f0 Iustin Pop
    """Return the values needed by ssconf.
1070 054596f0 Iustin Pop

1071 054596f0 Iustin Pop
    @rtype: dict
1072 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
1073 054596f0 Iustin Pop
        associated value
1074 054596f0 Iustin Pop

1075 054596f0 Iustin Pop
    """
1076 a3316e4a Iustin Pop
    fn = "\n".join
1077 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
1078 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
1079 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
1080 a3316e4a Iustin Pop
1081 81a49123 Iustin Pop
    instance_data = fn(instance_names)
1082 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
1083 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
1084 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
1085 a3316e4a Iustin Pop
    node_data = fn(node_names)
1086 f56618e0 Iustin Pop
1087 054596f0 Iustin Pop
    cluster = self._config_data.cluster
1088 03d1dba2 Michael Hanselmann
    return {
1089 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
1090 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
1091 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
1092 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
1093 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
1094 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
1095 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
1096 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
1097 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
1098 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
1099 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
1100 03d1dba2 Michael Hanselmann
      }
1101 03d1dba2 Michael Hanselmann
1102 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1103 f6bd6e98 Michael Hanselmann
  def InitConfig(self, version, cluster_config, master_node_config):
1104 a8083063 Iustin Pop
    """Create the initial cluster configuration.
1105 a8083063 Iustin Pop

1106 a8083063 Iustin Pop
    It will contain the current node, which will also be the master
1107 b9eeeb02 Michael Hanselmann
    node, and no instances.
1108 a8083063 Iustin Pop

1109 f6bd6e98 Michael Hanselmann
    @type version: int
1110 f6bd6e98 Michael Hanselmann
    @param version: Configuration version
1111 b9eeeb02 Michael Hanselmann
    @type cluster_config: objects.Cluster
1112 b9eeeb02 Michael Hanselmann
    @param cluster_config: Cluster configuration
1113 b9eeeb02 Michael Hanselmann
    @type master_node_config: objects.Node
1114 b9eeeb02 Michael Hanselmann
    @param master_node_config: Master node configuration
1115 b9eeeb02 Michael Hanselmann

1116 b9eeeb02 Michael Hanselmann
    """
1117 b9eeeb02 Michael Hanselmann
    nodes = {
1118 b9eeeb02 Michael Hanselmann
      master_node_config.name: master_node_config,
1119 b9eeeb02 Michael Hanselmann
      }
1120 b9eeeb02 Michael Hanselmann
1121 f6bd6e98 Michael Hanselmann
    self._config_data = objects.ConfigData(version=version,
1122 f6bd6e98 Michael Hanselmann
                                           cluster=cluster_config,
1123 b9eeeb02 Michael Hanselmann
                                           nodes=nodes,
1124 a8083063 Iustin Pop
                                           instances={},
1125 9d38c6e1 Iustin Pop
                                           serial_no=1)
1126 a8083063 Iustin Pop
    self._WriteConfig()
1127 a8083063 Iustin Pop
1128 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1129 a8083063 Iustin Pop
  def GetVGName(self):
1130 a8083063 Iustin Pop
    """Return the volume group name.
1131 a8083063 Iustin Pop

1132 a8083063 Iustin Pop
    """
1133 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
1134 a8083063 Iustin Pop
1135 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1136 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
1137 89ff8e15 Manuel Franceschini
    """Set the volume group name.
1138 89ff8e15 Manuel Franceschini

1139 89ff8e15 Manuel Franceschini
    """
1140 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
1141 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1142 89ff8e15 Manuel Franceschini
    self._WriteConfig()
1143 89ff8e15 Manuel Franceschini
1144 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1145 a8083063 Iustin Pop
  def GetDefBridge(self):
1146 a8083063 Iustin Pop
    """Return the default bridge.
1147 a8083063 Iustin Pop

1148 a8083063 Iustin Pop
    """
1149 a8083063 Iustin Pop
    return self._config_data.cluster.default_bridge
1150 a8083063 Iustin Pop
1151 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1152 a8083063 Iustin Pop
  def GetMACPrefix(self):
1153 a8083063 Iustin Pop
    """Return the mac prefix.
1154 a8083063 Iustin Pop

1155 a8083063 Iustin Pop
    """
1156 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
1157 62779dd0 Iustin Pop
1158 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1159 62779dd0 Iustin Pop
  def GetClusterInfo(self):
1160 62779dd0 Iustin Pop
    """Returns informations about the cluster
1161 62779dd0 Iustin Pop

1162 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
1163 c41eea6e Iustin Pop
    @return: the cluster object
1164 62779dd0 Iustin Pop

1165 62779dd0 Iustin Pop
    """
1166 62779dd0 Iustin Pop
    return self._config_data.cluster
1167 e00fb268 Iustin Pop
1168 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1169 e00fb268 Iustin Pop
  def Update(self, target):
1170 e00fb268 Iustin Pop
    """Notify function to be called after updates.
1171 e00fb268 Iustin Pop

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

1178 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
1179 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
1180 c41eea6e Iustin Pop
        the cluster
1181 c41eea6e Iustin Pop

1182 e00fb268 Iustin Pop
    """
1183 e00fb268 Iustin Pop
    if self._config_data is None:
1184 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
1185 3ecf6786 Iustin Pop
                                   " cannot save.")
1186 f34901f8 Iustin Pop
    update_serial = False
1187 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
1188 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
1189 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
1190 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
1191 f34901f8 Iustin Pop
      update_serial = True
1192 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
1193 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
1194 e00fb268 Iustin Pop
    else:
1195 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
1196 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
1197 e00fb268 Iustin Pop
    if not test:
1198 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
1199 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1200 f34901f8 Iustin Pop
    target.serial_no += 1
1201 f34901f8 Iustin Pop
1202 cff4c037 Iustin Pop
    if update_serial:
1203 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1204 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1205 b989e85d Iustin Pop
1206 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
1207 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
1208 61cf6b5e Iustin Pop
1209 e00fb268 Iustin Pop
    self._WriteConfig()