Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 74a48621

History | View | Annotate | Download (27.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
from ganeti import ssconf
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 243cdbcc Michael Hanselmann
def ValidateConfig():
53 243cdbcc Michael Hanselmann
  sstore = ssconf.SimpleStore()
54 243cdbcc Michael Hanselmann
55 243cdbcc Michael Hanselmann
  if sstore.GetConfigVersion() != constants.CONFIG_VERSION:
56 243cdbcc Michael Hanselmann
    raise errors.ConfigurationError("Cluster configuration version"
57 243cdbcc Michael Hanselmann
                                    " mismatch, got %s instead of %s" %
58 243cdbcc Michael Hanselmann
                                    (sstore.GetConfigVersion(),
59 243cdbcc Michael Hanselmann
                                     constants.CONFIG_VERSION))
60 a8083063 Iustin Pop
61 319856a9 Michael Hanselmann
62 a8083063 Iustin Pop
class ConfigWriter:
63 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
64 a8083063 Iustin Pop

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

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

98 a8083063 Iustin Pop
    This should check the current instances for duplicates.
99 a8083063 Iustin Pop

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

121 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
122 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
123 1862d460 Alexander Schreiber

124 1862d460 Alexander Schreiber
    """
125 1862d460 Alexander Schreiber
    self._OpenConfig()
126 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
127 1862d460 Alexander Schreiber
    return mac in all_macs
128 1862d460 Alexander Schreiber
129 923b1523 Iustin Pop
  def _ComputeAllLVs(self):
130 923b1523 Iustin Pop
    """Compute the list of all LVs.
131 923b1523 Iustin Pop

132 923b1523 Iustin Pop
    """
133 923b1523 Iustin Pop
    self._OpenConfig()
134 923b1523 Iustin Pop
    lvnames = set()
135 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
136 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
137 923b1523 Iustin Pop
      for lv_list in node_data.values():
138 923b1523 Iustin Pop
        lvnames.update(lv_list)
139 923b1523 Iustin Pop
    return lvnames
140 923b1523 Iustin Pop
141 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
142 923b1523 Iustin Pop
  def GenerateUniqueID(self, exceptions=None):
143 923b1523 Iustin Pop
    """Generate an unique disk name.
144 923b1523 Iustin Pop

145 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
146 923b1523 Iustin Pop
    duplicates.
147 923b1523 Iustin Pop

148 923b1523 Iustin Pop
    Args:
149 923b1523 Iustin Pop
      - exceptions: a list with some other names which should be checked
150 923b1523 Iustin Pop
                    for uniqueness (used for example when you want to get
151 923b1523 Iustin Pop
                    more than one id at one time without adding each one in
152 923b1523 Iustin Pop
                    turn to the config file
153 923b1523 Iustin Pop

154 923b1523 Iustin Pop
    Returns: the unique id as a string
155 923b1523 Iustin Pop

156 923b1523 Iustin Pop
    """
157 923b1523 Iustin Pop
    existing = set()
158 923b1523 Iustin Pop
    existing.update(self._temporary_ids)
159 923b1523 Iustin Pop
    existing.update(self._ComputeAllLVs())
160 923b1523 Iustin Pop
    existing.update(self._config_data.instances.keys())
161 923b1523 Iustin Pop
    existing.update(self._config_data.nodes.keys())
162 923b1523 Iustin Pop
    if exceptions is not None:
163 923b1523 Iustin Pop
      existing.update(exceptions)
164 923b1523 Iustin Pop
    retries = 64
165 923b1523 Iustin Pop
    while retries > 0:
166 24818e8f Michael Hanselmann
      unique_id = utils.NewUUID()
167 923b1523 Iustin Pop
      if unique_id not in existing and unique_id is not None:
168 923b1523 Iustin Pop
        break
169 923b1523 Iustin Pop
    else:
170 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Not able generate an unique ID"
171 3ecf6786 Iustin Pop
                                      " (last tried ID: %s" % unique_id)
172 923b1523 Iustin Pop
    self._temporary_ids.add(unique_id)
173 923b1523 Iustin Pop
    return unique_id
174 923b1523 Iustin Pop
175 a8083063 Iustin Pop
  def _AllMACs(self):
176 a8083063 Iustin Pop
    """Return all MACs present in the config.
177 a8083063 Iustin Pop

178 a8083063 Iustin Pop
    """
179 a8083063 Iustin Pop
    self._OpenConfig()
180 a8083063 Iustin Pop
181 a8083063 Iustin Pop
    result = []
182 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
183 a8083063 Iustin Pop
      for nic in instance.nics:
184 a8083063 Iustin Pop
        result.append(nic.mac)
185 a8083063 Iustin Pop
186 a8083063 Iustin Pop
    return result
187 a8083063 Iustin Pop
188 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
189 a8083063 Iustin Pop
  def VerifyConfig(self):
190 a8083063 Iustin Pop
    """Stub verify function.
191 a8083063 Iustin Pop
    """
192 a8083063 Iustin Pop
    self._OpenConfig()
193 a8083063 Iustin Pop
194 a8083063 Iustin Pop
    result = []
195 a8083063 Iustin Pop
    seen_macs = []
196 a8083063 Iustin Pop
    data = self._config_data
197 a8083063 Iustin Pop
    for instance_name in data.instances:
198 a8083063 Iustin Pop
      instance = data.instances[instance_name]
199 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
200 8522ceeb Iustin Pop
        result.append("instance '%s' has invalid primary node '%s'" %
201 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
202 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
203 a8083063 Iustin Pop
        if snode not in data.nodes:
204 8522ceeb Iustin Pop
          result.append("instance '%s' has invalid secondary node '%s'" %
205 a8083063 Iustin Pop
                        (instance_name, snode))
206 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
207 a8083063 Iustin Pop
        if nic.mac in seen_macs:
208 8522ceeb Iustin Pop
          result.append("instance '%s' has NIC %d mac %s duplicate" %
209 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
210 a8083063 Iustin Pop
        else:
211 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
212 a8083063 Iustin Pop
    return result
213 a8083063 Iustin Pop
214 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
215 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
216 a8083063 Iustin Pop

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

219 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
220 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
221 a8083063 Iustin Pop
    node.
222 a8083063 Iustin Pop

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

225 a8083063 Iustin Pop
    """
226 a8083063 Iustin Pop
    if disk.children:
227 a8083063 Iustin Pop
      for child in disk.children:
228 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
229 a8083063 Iustin Pop
230 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
231 a8083063 Iustin Pop
      return
232 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
233 ffa1c0dc Iustin Pop
      pnode, snode, port, pminor, sminor = disk.logical_id
234 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
235 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
236 3ecf6786 Iustin Pop
                                        node_name)
237 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
238 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
239 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
240 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
241 a8083063 Iustin Pop
                                        " for %s" % str(disk))
242 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
243 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
244 a8083063 Iustin Pop
      if pnode == node_name:
245 ffa1c0dc Iustin Pop
        disk.physical_id = p_data + s_data + (pminor,)
246 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
247 ffa1c0dc Iustin Pop
        disk.physical_id = s_data + p_data + (sminor,)
248 a8083063 Iustin Pop
    else:
249 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
250 a8083063 Iustin Pop
    return
251 a8083063 Iustin Pop
252 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
253 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
254 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
255 f78ede4e Guido Trotter

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

258 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
259 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
260 f78ede4e Guido Trotter
    node.
261 f78ede4e Guido Trotter

262 f78ede4e Guido Trotter
    """
263 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
264 f78ede4e Guido Trotter
265 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
266 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
267 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
268 b2fddf63 Iustin Pop

269 b2fddf63 Iustin Pop
    """
270 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
271 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
272 264bb3c5 Michael Hanselmann
273 264bb3c5 Michael Hanselmann
    self._OpenConfig()
274 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
275 264bb3c5 Michael Hanselmann
    self._WriteConfig()
276 264bb3c5 Michael Hanselmann
277 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
278 b2fddf63 Iustin Pop
  def GetPortList(self):
279 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
280 264bb3c5 Michael Hanselmann

281 264bb3c5 Michael Hanselmann
    """
282 264bb3c5 Michael Hanselmann
    self._OpenConfig()
283 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
284 264bb3c5 Michael Hanselmann
285 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
286 a8083063 Iustin Pop
  def AllocatePort(self):
287 a8083063 Iustin Pop
    """Allocate a port.
288 a8083063 Iustin Pop

289 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
290 b2fddf63 Iustin Pop
    default port range (and in this case we increase
291 b2fddf63 Iustin Pop
    highest_used_port).
292 a8083063 Iustin Pop

293 a8083063 Iustin Pop
    """
294 a8083063 Iustin Pop
    self._OpenConfig()
295 a8083063 Iustin Pop
296 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
297 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
298 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
299 264bb3c5 Michael Hanselmann
    else:
300 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
301 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
302 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
303 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
304 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
305 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
306 a8083063 Iustin Pop
307 a8083063 Iustin Pop
    self._WriteConfig()
308 a8083063 Iustin Pop
    return port
309 a8083063 Iustin Pop
310 a81c53c9 Iustin Pop
  def _ComputeDRBDMap(self, instance):
311 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
312 a81c53c9 Iustin Pop

313 a81c53c9 Iustin Pop
    Return: dictionary of node_name: dict of minor: instance_name. The
314 a81c53c9 Iustin Pop
    returned dict will have all the nodes in it (even if with an empty
315 a81c53c9 Iustin Pop
    list).
316 a81c53c9 Iustin Pop

317 a81c53c9 Iustin Pop
    """
318 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
319 a81c53c9 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) == 5:
320 a81c53c9 Iustin Pop
        nodeA, nodeB, dummy, minorA, minorB = disk.logical_id
321 a81c53c9 Iustin Pop
        for node, port in ((nodeA, minorA), (nodeB, minorB)):
322 a81c53c9 Iustin Pop
          assert node in used, "Instance node not found in node list"
323 a81c53c9 Iustin Pop
          if port in used[node]:
324 a81c53c9 Iustin Pop
            raise errors.ProgrammerError("DRBD minor already used:"
325 a81c53c9 Iustin Pop
                                         " %s/%s, %s/%s" %
326 a81c53c9 Iustin Pop
                                         (node, port, instance_name,
327 a81c53c9 Iustin Pop
                                          used[node][port]))
328 a81c53c9 Iustin Pop
329 a81c53c9 Iustin Pop
          used[node][port] = instance_name
330 a81c53c9 Iustin Pop
      if disk.children:
331 a81c53c9 Iustin Pop
        for child in disk.children:
332 a81c53c9 Iustin Pop
          _AppendUsedPorts(instance_name, child, used)
333 a81c53c9 Iustin Pop
334 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
335 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
336 a81c53c9 Iustin Pop
      my_dict[node][minor] = instance
337 a81c53c9 Iustin Pop
    for instance in self._config_data.instances.itervalues():
338 a81c53c9 Iustin Pop
      for disk in instance.disks:
339 a81c53c9 Iustin Pop
        _AppendUsedPorts(instance.name, disk, my_dict)
340 a81c53c9 Iustin Pop
    return my_dict
341 a81c53c9 Iustin Pop
342 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
343 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
344 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
345 a81c53c9 Iustin Pop

346 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
347 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
348 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
349 a81c53c9 Iustin Pop
    order as the passed nodes.
350 a81c53c9 Iustin Pop

351 a81c53c9 Iustin Pop
    """
352 a81c53c9 Iustin Pop
    self._OpenConfig()
353 a81c53c9 Iustin Pop
354 a81c53c9 Iustin Pop
    d_map = self._ComputeDRBDMap(instance)
355 a81c53c9 Iustin Pop
    result = []
356 a81c53c9 Iustin Pop
    for nname in nodes:
357 a81c53c9 Iustin Pop
      ndata = d_map[nname]
358 a81c53c9 Iustin Pop
      if not ndata:
359 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
360 a81c53c9 Iustin Pop
        result.append(0)
361 a81c53c9 Iustin Pop
        ndata[0] = instance
362 a81c53c9 Iustin Pop
        continue
363 a81c53c9 Iustin Pop
      keys = ndata.keys()
364 a81c53c9 Iustin Pop
      keys.sort()
365 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
366 a81c53c9 Iustin Pop
      if ffree is None:
367 a81c53c9 Iustin Pop
        # return the next minor
368 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
369 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
370 a81c53c9 Iustin Pop
      else:
371 a81c53c9 Iustin Pop
        minor = ffree
372 a81c53c9 Iustin Pop
      result.append(minor)
373 a81c53c9 Iustin Pop
      ndata[minor] = instance
374 a81c53c9 Iustin Pop
      assert (nname, minor) not in self._temporary_drbds, \
375 a81c53c9 Iustin Pop
             "Attempt to reuse reserved DRBD minor"
376 a81c53c9 Iustin Pop
      self._temporary_drbds[(nname, minor)] = instance
377 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
378 a81c53c9 Iustin Pop
                  nodes, result)
379 a81c53c9 Iustin Pop
    return result
380 a81c53c9 Iustin Pop
381 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
382 a81c53c9 Iustin Pop
  def ReleaseDRBDMinors(self, instance):
383 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
384 a81c53c9 Iustin Pop

385 a81c53c9 Iustin Pop
    This should be called on both the error paths and on the success
386 a81c53c9 Iustin Pop
    paths (after the instance has been added or updated).
387 a81c53c9 Iustin Pop

388 a81c53c9 Iustin Pop
    @type instance: string
389 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
390 a81c53c9 Iustin Pop
                     released
391 a81c53c9 Iustin Pop

392 a81c53c9 Iustin Pop
    """
393 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
394 a81c53c9 Iustin Pop
      if name == instance:
395 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
396 a81c53c9 Iustin Pop
397 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
398 a8083063 Iustin Pop
  def GetHostKey(self):
399 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
400 a8083063 Iustin Pop

401 a8083063 Iustin Pop
    Args: None
402 a8083063 Iustin Pop

403 a8083063 Iustin Pop
    Returns: rsa hostkey
404 a8083063 Iustin Pop
    """
405 a8083063 Iustin Pop
    self._OpenConfig()
406 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
407 a8083063 Iustin Pop
408 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
409 a8083063 Iustin Pop
  def AddInstance(self, instance):
410 a8083063 Iustin Pop
    """Add an instance to the config.
411 a8083063 Iustin Pop

412 a8083063 Iustin Pop
    This should be used after creating a new instance.
413 a8083063 Iustin Pop

414 a8083063 Iustin Pop
    Args:
415 a8083063 Iustin Pop
      instance: the instance object
416 a8083063 Iustin Pop
    """
417 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
418 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
419 a8083063 Iustin Pop
420 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
421 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
422 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
423 923b1523 Iustin Pop
424 a8083063 Iustin Pop
    self._OpenConfig()
425 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
426 a8083063 Iustin Pop
    self._WriteConfig()
427 a8083063 Iustin Pop
428 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
429 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
430 a8083063 Iustin Pop

431 a8083063 Iustin Pop
    """
432 6a408fb2 Iustin Pop
    if status not in ("up", "down"):
433 6a408fb2 Iustin Pop
      raise errors.ProgrammerError("Invalid status '%s' passed to"
434 6a408fb2 Iustin Pop
                                   " ConfigWriter._SetInstanceStatus()" %
435 6a408fb2 Iustin Pop
                                   status)
436 a8083063 Iustin Pop
    self._OpenConfig()
437 a8083063 Iustin Pop
438 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
439 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
440 3ecf6786 Iustin Pop
                                      instance_name)
441 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
442 455a3445 Iustin Pop
    if instance.status != status:
443 455a3445 Iustin Pop
      instance.status = status
444 455a3445 Iustin Pop
      self._WriteConfig()
445 a8083063 Iustin Pop
446 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
447 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
448 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
449 6a408fb2 Iustin Pop

450 6a408fb2 Iustin Pop
    """
451 6a408fb2 Iustin Pop
    self._SetInstanceStatus(instance_name, "up")
452 6a408fb2 Iustin Pop
453 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
454 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
455 a8083063 Iustin Pop
    """Remove the instance from the configuration.
456 a8083063 Iustin Pop

457 a8083063 Iustin Pop
    """
458 a8083063 Iustin Pop
    self._OpenConfig()
459 a8083063 Iustin Pop
460 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
461 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
462 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
463 a8083063 Iustin Pop
    self._WriteConfig()
464 a8083063 Iustin Pop
465 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
466 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
467 fc95f88f Iustin Pop
    """Rename an instance.
468 fc95f88f Iustin Pop

469 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
470 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
471 fc95f88f Iustin Pop
    rename.
472 fc95f88f Iustin Pop

473 fc95f88f Iustin Pop
    """
474 fc95f88f Iustin Pop
    self._OpenConfig()
475 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
476 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
477 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
478 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
479 fc95f88f Iustin Pop
    inst.name = new_name
480 b23c4333 Manuel Franceschini
481 b23c4333 Manuel Franceschini
    for disk in inst.disks:
482 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
483 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
484 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
485 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
486 b23c4333 Manuel Franceschini
                                              os.path.join(file_storage_dir,
487 b23c4333 Manuel Franceschini
                                                           inst.name,
488 b23c4333 Manuel Franceschini
                                                           disk.iv_name))
489 b23c4333 Manuel Franceschini
490 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
491 fc95f88f Iustin Pop
    self._WriteConfig()
492 fc95f88f Iustin Pop
493 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
494 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
495 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
496 a8083063 Iustin Pop

497 a8083063 Iustin Pop
    """
498 6a408fb2 Iustin Pop
    self._SetInstanceStatus(instance_name, "down")
499 a8083063 Iustin Pop
500 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
501 94bbfece Iustin Pop
    """Get the list of instances.
502 94bbfece Iustin Pop

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

505 94bbfece Iustin Pop
    """
506 94bbfece Iustin Pop
    self._OpenConfig()
507 94bbfece Iustin Pop
    return self._config_data.instances.keys()
508 94bbfece Iustin Pop
509 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
510 a8083063 Iustin Pop
  def GetInstanceList(self):
511 a8083063 Iustin Pop
    """Get the list of instances.
512 a8083063 Iustin Pop

513 a8083063 Iustin Pop
    Returns:
514 a8083063 Iustin Pop
      array of instances, ex. ['instance2.example.com','instance1.example.com']
515 a8083063 Iustin Pop
      these contains all the instances, also the ones in Admin_down state
516 a8083063 Iustin Pop

517 a8083063 Iustin Pop
    """
518 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
519 a8083063 Iustin Pop
520 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
521 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
522 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
523 a8083063 Iustin Pop

524 a8083063 Iustin Pop
    """
525 a8083063 Iustin Pop
    self._OpenConfig()
526 a8083063 Iustin Pop
527 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
528 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
529 a8083063 Iustin Pop
530 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
531 94bbfece Iustin Pop
    """Returns informations about an instance.
532 94bbfece Iustin Pop

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

535 94bbfece Iustin Pop
    """
536 94bbfece Iustin Pop
    self._OpenConfig()
537 94bbfece Iustin Pop
538 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
539 94bbfece Iustin Pop
      return None
540 94bbfece Iustin Pop
541 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
542 94bbfece Iustin Pop
543 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
544 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
545 a8083063 Iustin Pop
    """Returns informations about an instance.
546 a8083063 Iustin Pop

547 a8083063 Iustin Pop
    It takes the information from the configuration file. Other informations of
548 a8083063 Iustin Pop
    an instance are taken from the live systems.
549 a8083063 Iustin Pop

550 a8083063 Iustin Pop
    Args:
551 a8083063 Iustin Pop
      instance: name of the instance, ex instance1.example.com
552 a8083063 Iustin Pop

553 a8083063 Iustin Pop
    Returns:
554 a8083063 Iustin Pop
      the instance object
555 a8083063 Iustin Pop

556 a8083063 Iustin Pop
    """
557 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
558 a8083063 Iustin Pop
559 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
560 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
561 0b2de758 Iustin Pop
    """Get the configuration of all instances.
562 0b2de758 Iustin Pop

563 0b2de758 Iustin Pop
    @rtype: dict
564 0b2de758 Iustin Pop
    @returns: dict of (instance, instance_info), where instance_info is what
565 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
566 0b2de758 Iustin Pop

567 0b2de758 Iustin Pop
    """
568 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
569 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
570 0b2de758 Iustin Pop
    return my_dict
571 0b2de758 Iustin Pop
572 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
573 a8083063 Iustin Pop
  def AddNode(self, node):
574 a8083063 Iustin Pop
    """Add a node to the configuration.
575 a8083063 Iustin Pop

576 a8083063 Iustin Pop
    Args:
577 a8083063 Iustin Pop
      node: an object.Node instance
578 a8083063 Iustin Pop

579 a8083063 Iustin Pop
    """
580 d8470559 Michael Hanselmann
    logging.info("Adding node %s to configuration" % node.name)
581 d8470559 Michael Hanselmann
582 a8083063 Iustin Pop
    self._OpenConfig()
583 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
584 a8083063 Iustin Pop
    self._WriteConfig()
585 a8083063 Iustin Pop
586 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
587 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
588 a8083063 Iustin Pop
    """Remove a node from the configuration.
589 a8083063 Iustin Pop

590 a8083063 Iustin Pop
    """
591 d8470559 Michael Hanselmann
    logging.info("Removing node %s from configuration" % node_name)
592 d8470559 Michael Hanselmann
593 a8083063 Iustin Pop
    self._OpenConfig()
594 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
595 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
596 a8083063 Iustin Pop
597 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
598 a8083063 Iustin Pop
    self._WriteConfig()
599 a8083063 Iustin Pop
600 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
601 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
602 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
603 a8083063 Iustin Pop

604 a8083063 Iustin Pop
    """
605 a8083063 Iustin Pop
    self._OpenConfig()
606 a8083063 Iustin Pop
607 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
608 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
609 a8083063 Iustin Pop
610 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
611 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
612 a8083063 Iustin Pop

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

615 a8083063 Iustin Pop
    Args: node: nodename (tuple) of the node
616 a8083063 Iustin Pop

617 a8083063 Iustin Pop
    Returns: the node object
618 a8083063 Iustin Pop

619 a8083063 Iustin Pop
    """
620 a8083063 Iustin Pop
    self._OpenConfig()
621 a8083063 Iustin Pop
622 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
623 a8083063 Iustin Pop
      return None
624 a8083063 Iustin Pop
625 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
626 a8083063 Iustin Pop
627 f78ede4e Guido Trotter
628 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
629 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
630 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
631 f78ede4e Guido Trotter

632 f78ede4e Guido Trotter
    Args: node: nodename (tuple) of the node
633 f78ede4e Guido Trotter

634 f78ede4e Guido Trotter
    Returns: the node object
635 f78ede4e Guido Trotter

636 f78ede4e Guido Trotter
    """
637 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
638 f78ede4e Guido Trotter
639 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
640 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
641 a8083063 Iustin Pop

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

644 a8083063 Iustin Pop
    """
645 a8083063 Iustin Pop
    self._OpenConfig()
646 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
647 a8083063 Iustin Pop
648 f78ede4e Guido Trotter
649 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
650 f78ede4e Guido Trotter
  def GetNodeList(self):
651 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
652 f78ede4e Guido Trotter

653 f78ede4e Guido Trotter
    """
654 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
655 f78ede4e Guido Trotter
656 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
657 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
658 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
659 d65e5776 Iustin Pop

660 d65e5776 Iustin Pop
    @rtype: dict
661 d65e5776 Iustin Pop
    @returns: dict of (node, node_info), where node_info is what
662 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
663 d65e5776 Iustin Pop

664 d65e5776 Iustin Pop
    """
665 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
666 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
667 d65e5776 Iustin Pop
    return my_dict
668 d65e5776 Iustin Pop
669 d65e5776 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
670 a8083063 Iustin Pop
  def DumpConfig(self):
671 a8083063 Iustin Pop
    """Return the entire configuration of the cluster.
672 a8083063 Iustin Pop
    """
673 a8083063 Iustin Pop
    self._OpenConfig()
674 a8083063 Iustin Pop
    return self._config_data
675 a8083063 Iustin Pop
676 a8083063 Iustin Pop
  def _BumpSerialNo(self):
677 a8083063 Iustin Pop
    """Bump up the serial number of the config.
678 a8083063 Iustin Pop

679 a8083063 Iustin Pop
    """
680 a8083063 Iustin Pop
    self._config_data.cluster.serial_no += 1
681 a8083063 Iustin Pop
682 a8083063 Iustin Pop
  def _OpenConfig(self):
683 a8083063 Iustin Pop
    """Read the config data from disk.
684 a8083063 Iustin Pop

685 a8083063 Iustin Pop
    In case we already have configuration data and the config file has
686 a8083063 Iustin Pop
    the same mtime as when we read it, we skip the parsing of the
687 a8083063 Iustin Pop
    file, since de-serialisation could be slow.
688 a8083063 Iustin Pop

689 a8083063 Iustin Pop
    """
690 a8083063 Iustin Pop
    try:
691 a8083063 Iustin Pop
      st = os.stat(self._cfg_file)
692 a8083063 Iustin Pop
    except OSError, err:
693 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't stat config file: %s" % err)
694 a8083063 Iustin Pop
    if (self._config_data is not None and
695 a8083063 Iustin Pop
        self._config_time is not None and
696 264bb3c5 Michael Hanselmann
        self._config_time == st.st_mtime and
697 264bb3c5 Michael Hanselmann
        self._config_size == st.st_size and
698 264bb3c5 Michael Hanselmann
        self._config_inode == st.st_ino):
699 a8083063 Iustin Pop
      # data is current, so skip loading of config file
700 a8083063 Iustin Pop
      return
701 243cdbcc Michael Hanselmann
702 243cdbcc Michael Hanselmann
    # Make sure the configuration has the right version
703 243cdbcc Michael Hanselmann
    ValidateConfig()
704 243cdbcc Michael Hanselmann
705 a8083063 Iustin Pop
    f = open(self._cfg_file, 'r')
706 a8083063 Iustin Pop
    try:
707 a8083063 Iustin Pop
      try:
708 8d14b30d Iustin Pop
        data = objects.ConfigData.FromDict(serializer.Load(f.read()))
709 a8083063 Iustin Pop
      except Exception, err:
710 3ecf6786 Iustin Pop
        raise errors.ConfigurationError(err)
711 a8083063 Iustin Pop
    finally:
712 a8083063 Iustin Pop
      f.close()
713 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
714 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
715 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
716 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
717 a8083063 Iustin Pop
    self._config_data = data
718 a8083063 Iustin Pop
    self._config_time = st.st_mtime
719 264bb3c5 Michael Hanselmann
    self._config_size = st.st_size
720 264bb3c5 Michael Hanselmann
    self._config_inode = st.st_ino
721 a8083063 Iustin Pop
722 a8083063 Iustin Pop
  def _DistributeConfig(self):
723 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
724 a8083063 Iustin Pop

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

728 a8083063 Iustin Pop
    """
729 a8083063 Iustin Pop
    if self._offline:
730 a8083063 Iustin Pop
      return True
731 a8083063 Iustin Pop
    bad = False
732 f78ede4e Guido Trotter
    nodelist = self._UnlockedGetNodeList()
733 89e1fc26 Iustin Pop
    myhostname = self._my_hostname
734 a8083063 Iustin Pop
735 9ff994da Guido Trotter
    try:
736 9ff994da Guido Trotter
      nodelist.remove(myhostname)
737 9ff994da Guido Trotter
    except ValueError:
738 9ff994da Guido Trotter
      pass
739 a8083063 Iustin Pop
740 41362e70 Guido Trotter
    result = rpc.call_upload_file(nodelist, self._cfg_file)
741 41362e70 Guido Trotter
    for node in nodelist:
742 a8083063 Iustin Pop
      if not result[node]:
743 74a48621 Iustin Pop
        logging.error("copy of file %s to node %s failed",
744 74a48621 Iustin Pop
                      self._cfg_file, node)
745 a8083063 Iustin Pop
        bad = True
746 a8083063 Iustin Pop
    return not bad
747 a8083063 Iustin Pop
748 a8083063 Iustin Pop
  def _WriteConfig(self, destination=None):
749 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
750 a8083063 Iustin Pop

751 a8083063 Iustin Pop
    """
752 a8083063 Iustin Pop
    if destination is None:
753 a8083063 Iustin Pop
      destination = self._cfg_file
754 a8083063 Iustin Pop
    self._BumpSerialNo()
755 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
756 a8083063 Iustin Pop
    dir_name, file_name = os.path.split(destination)
757 a8083063 Iustin Pop
    fd, name = tempfile.mkstemp('.newconfig', file_name, dir_name)
758 a8083063 Iustin Pop
    f = os.fdopen(fd, 'w')
759 a8083063 Iustin Pop
    try:
760 8d14b30d Iustin Pop
      f.write(txt)
761 a8083063 Iustin Pop
      os.fsync(f.fileno())
762 a8083063 Iustin Pop
    finally:
763 a8083063 Iustin Pop
      f.close()
764 a8083063 Iustin Pop
    # we don't need to do os.close(fd) as f.close() did it
765 a8083063 Iustin Pop
    os.rename(name, destination)
766 14e15659 Iustin Pop
    self.write_count += 1
767 fee9556c Iustin Pop
    # re-set our cache as not to re-read the config file
768 fee9556c Iustin Pop
    try:
769 fee9556c Iustin Pop
      st = os.stat(destination)
770 fee9556c Iustin Pop
    except OSError, err:
771 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't stat config file: %s" % err)
772 fee9556c Iustin Pop
    self._config_time = st.st_mtime
773 fee9556c Iustin Pop
    self._config_size = st.st_size
774 fee9556c Iustin Pop
    self._config_inode = st.st_ino
775 fee9556c Iustin Pop
    # and redistribute the config file
776 a8083063 Iustin Pop
    self._DistributeConfig()
777 a8083063 Iustin Pop
778 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
779 a8083063 Iustin Pop
  def InitConfig(self, node, primary_ip, secondary_ip,
780 5fcdc80d Iustin Pop
                 hostkeypub, mac_prefix, vg_name, def_bridge):
781 a8083063 Iustin Pop
    """Create the initial cluster configuration.
782 a8083063 Iustin Pop

783 a8083063 Iustin Pop
    It will contain the current node, which will also be the master
784 a8083063 Iustin Pop
    node, and no instances or operating systmes.
785 a8083063 Iustin Pop

786 a8083063 Iustin Pop
    Args:
787 a8083063 Iustin Pop
      node: the nodename of the initial node
788 a8083063 Iustin Pop
      primary_ip: the IP address of the current host
789 a8083063 Iustin Pop
      secondary_ip: the secondary IP of the current host or None
790 a8083063 Iustin Pop
      hostkeypub: the public hostkey of this host
791 a8083063 Iustin Pop

792 264bb3c5 Michael Hanselmann
    """
793 a8083063 Iustin Pop
    hu_port = constants.FIRST_DRBD_PORT - 1
794 243cdbcc Michael Hanselmann
    globalconfig = objects.Cluster(serial_no=1,
795 a8083063 Iustin Pop
                                   rsahostkeypub=hostkeypub,
796 a8083063 Iustin Pop
                                   highest_used_port=hu_port,
797 a8083063 Iustin Pop
                                   mac_prefix=mac_prefix,
798 a8083063 Iustin Pop
                                   volume_group_name=vg_name,
799 b2fddf63 Iustin Pop
                                   default_bridge=def_bridge,
800 b2fddf63 Iustin Pop
                                   tcpudp_port_pool=set())
801 a8083063 Iustin Pop
    if secondary_ip is None:
802 a8083063 Iustin Pop
      secondary_ip = primary_ip
803 a8083063 Iustin Pop
    nodeconfig = objects.Node(name=node, primary_ip=primary_ip,
804 a8083063 Iustin Pop
                              secondary_ip=secondary_ip)
805 a8083063 Iustin Pop
806 a8083063 Iustin Pop
    self._config_data = objects.ConfigData(nodes={node: nodeconfig},
807 a8083063 Iustin Pop
                                           instances={},
808 b2fddf63 Iustin Pop
                                           cluster=globalconfig)
809 a8083063 Iustin Pop
    self._WriteConfig()
810 a8083063 Iustin Pop
811 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
812 a8083063 Iustin Pop
  def GetVGName(self):
813 a8083063 Iustin Pop
    """Return the volume group name.
814 a8083063 Iustin Pop

815 a8083063 Iustin Pop
    """
816 a8083063 Iustin Pop
    self._OpenConfig()
817 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
818 a8083063 Iustin Pop
819 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
820 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
821 89ff8e15 Manuel Franceschini
    """Set the volume group name.
822 89ff8e15 Manuel Franceschini

823 89ff8e15 Manuel Franceschini
    """
824 89ff8e15 Manuel Franceschini
    self._OpenConfig()
825 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
826 89ff8e15 Manuel Franceschini
    self._WriteConfig()
827 89ff8e15 Manuel Franceschini
828 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
829 a8083063 Iustin Pop
  def GetDefBridge(self):
830 a8083063 Iustin Pop
    """Return the default bridge.
831 a8083063 Iustin Pop

832 a8083063 Iustin Pop
    """
833 a8083063 Iustin Pop
    self._OpenConfig()
834 a8083063 Iustin Pop
    return self._config_data.cluster.default_bridge
835 a8083063 Iustin Pop
836 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
837 a8083063 Iustin Pop
  def GetMACPrefix(self):
838 a8083063 Iustin Pop
    """Return the mac prefix.
839 a8083063 Iustin Pop

840 a8083063 Iustin Pop
    """
841 a8083063 Iustin Pop
    self._OpenConfig()
842 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
843 62779dd0 Iustin Pop
844 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
845 62779dd0 Iustin Pop
  def GetClusterInfo(self):
846 62779dd0 Iustin Pop
    """Returns informations about the cluster
847 62779dd0 Iustin Pop

848 62779dd0 Iustin Pop
    Returns:
849 62779dd0 Iustin Pop
      the cluster object
850 62779dd0 Iustin Pop

851 62779dd0 Iustin Pop
    """
852 62779dd0 Iustin Pop
    self._OpenConfig()
853 62779dd0 Iustin Pop
854 62779dd0 Iustin Pop
    return self._config_data.cluster
855 e00fb268 Iustin Pop
856 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
857 e00fb268 Iustin Pop
  def Update(self, target):
858 e00fb268 Iustin Pop
    """Notify function to be called after updates.
859 e00fb268 Iustin Pop

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

866 e00fb268 Iustin Pop
    """
867 e00fb268 Iustin Pop
    if self._config_data is None:
868 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
869 3ecf6786 Iustin Pop
                                   " cannot save.")
870 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
871 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
872 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
873 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
874 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
875 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
876 e00fb268 Iustin Pop
    else:
877 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
878 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
879 e00fb268 Iustin Pop
    if not test:
880 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
881 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
882 e00fb268 Iustin Pop
    self._WriteConfig()