Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 264bb3c5

History | View | Annotate | Download (15.9 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
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 a8083063 Iustin Pop
This module provides the interface to the ganeti cluster configuration.
25 a8083063 Iustin Pop

26 a8083063 Iustin Pop

27 a8083063 Iustin Pop
The configuration data is stored on every node but is updated on the
28 a8083063 Iustin Pop
master only. After each update, the master distributes the data to the
29 a8083063 Iustin Pop
other nodes.
30 a8083063 Iustin Pop

31 a8083063 Iustin Pop
Currently the data storage format is pickle as yaml was initially not
32 a8083063 Iustin Pop
available, then we used it but it was a memory-eating slow beast, so
33 a8083063 Iustin Pop
we reverted to pickle using custom Unpicklers.
34 a8083063 Iustin Pop

35 a8083063 Iustin Pop
"""
36 a8083063 Iustin Pop
37 a8083063 Iustin Pop
import os
38 a8083063 Iustin Pop
import socket
39 a8083063 Iustin Pop
import tempfile
40 a8083063 Iustin Pop
import random
41 a8083063 Iustin Pop
42 a8083063 Iustin Pop
from ganeti import errors
43 a8083063 Iustin Pop
from ganeti import logger
44 a8083063 Iustin Pop
from ganeti import utils
45 a8083063 Iustin Pop
from ganeti import constants
46 a8083063 Iustin Pop
from ganeti import rpc
47 a8083063 Iustin Pop
from ganeti import objects
48 a8083063 Iustin Pop
49 a8083063 Iustin Pop
50 a8083063 Iustin Pop
class ConfigWriter:
51 a8083063 Iustin Pop
  """The interface to the cluster configuration"""
52 a8083063 Iustin Pop
53 a8083063 Iustin Pop
  def __init__(self, cfg_file=None, offline=False):
54 a8083063 Iustin Pop
    self._config_data = None
55 a8083063 Iustin Pop
    self._config_time = None
56 264bb3c5 Michael Hanselmann
    self._config_size = None
57 264bb3c5 Michael Hanselmann
    self._config_inode = None
58 a8083063 Iustin Pop
    self._offline = offline
59 a8083063 Iustin Pop
    if cfg_file is None:
60 a8083063 Iustin Pop
      self._cfg_file = constants.CLUSTER_CONF_FILE
61 a8083063 Iustin Pop
    else:
62 a8083063 Iustin Pop
      self._cfg_file = cfg_file
63 a8083063 Iustin Pop
64 a8083063 Iustin Pop
  # this method needs to be static, so that we can call it on the class
65 a8083063 Iustin Pop
  @staticmethod
66 a8083063 Iustin Pop
  def IsCluster():
67 a8083063 Iustin Pop
    """Check if the cluster is configured.
68 a8083063 Iustin Pop

69 a8083063 Iustin Pop
    """
70 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
71 a8083063 Iustin Pop
72 a8083063 Iustin Pop
  def GenerateMAC(self):
73 a8083063 Iustin Pop
    """Generate a MAC for an instance.
74 a8083063 Iustin Pop

75 a8083063 Iustin Pop
    This should check the current instances for duplicates.
76 a8083063 Iustin Pop

77 a8083063 Iustin Pop
    """
78 a8083063 Iustin Pop
    self._OpenConfig()
79 a8083063 Iustin Pop
    self._ReleaseLock()
80 a8083063 Iustin Pop
    prefix = self._config_data.cluster.mac_prefix
81 a8083063 Iustin Pop
    all_macs = self._AllMACs()
82 a8083063 Iustin Pop
    retries = 64
83 a8083063 Iustin Pop
    while retries > 0:
84 a8083063 Iustin Pop
      byte1 = random.randrange(0, 256)
85 a8083063 Iustin Pop
      byte2 = random.randrange(0, 256)
86 a8083063 Iustin Pop
      byte3 = random.randrange(0, 256)
87 a8083063 Iustin Pop
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
88 a8083063 Iustin Pop
      if mac not in all_macs:
89 a8083063 Iustin Pop
        break
90 a8083063 Iustin Pop
      retries -= 1
91 a8083063 Iustin Pop
    else:
92 a8083063 Iustin Pop
      raise errors.ConfigurationError, ("Can't generate unique MAC")
93 a8083063 Iustin Pop
    return mac
94 a8083063 Iustin Pop
95 a8083063 Iustin Pop
  def _AllMACs(self):
96 a8083063 Iustin Pop
    """Return all MACs present in the config.
97 a8083063 Iustin Pop

98 a8083063 Iustin Pop
    """
99 a8083063 Iustin Pop
    self._OpenConfig()
100 a8083063 Iustin Pop
    self._ReleaseLock()
101 a8083063 Iustin Pop
102 a8083063 Iustin Pop
    result = []
103 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
104 a8083063 Iustin Pop
      for nic in instance.nics:
105 a8083063 Iustin Pop
        result.append(nic.mac)
106 a8083063 Iustin Pop
107 a8083063 Iustin Pop
    return result
108 a8083063 Iustin Pop
109 a8083063 Iustin Pop
  def VerifyConfig(self):
110 a8083063 Iustin Pop
    """Stub verify function.
111 a8083063 Iustin Pop
    """
112 a8083063 Iustin Pop
    self._OpenConfig()
113 a8083063 Iustin Pop
    self._ReleaseLock()
114 a8083063 Iustin Pop
115 a8083063 Iustin Pop
    result = []
116 a8083063 Iustin Pop
    seen_macs = []
117 a8083063 Iustin Pop
    data = self._config_data
118 a8083063 Iustin Pop
    for instance_name in data.instances:
119 a8083063 Iustin Pop
      instance = data.instances[instance_name]
120 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
121 a8083063 Iustin Pop
        result.append("Instance '%s' has invalid primary node '%s'" %
122 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
123 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
124 a8083063 Iustin Pop
        if snode not in data.nodes:
125 a8083063 Iustin Pop
          result.append("Instance '%s' has invalid secondary node '%s'" %
126 a8083063 Iustin Pop
                        (instance_name, snode))
127 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
128 a8083063 Iustin Pop
        if nic.mac in seen_macs:
129 a8083063 Iustin Pop
          result.append("Instance '%s' has NIC %d mac %s duplicate" %
130 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
131 a8083063 Iustin Pop
        else:
132 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
133 a8083063 Iustin Pop
    return result
134 a8083063 Iustin Pop
135 a8083063 Iustin Pop
136 a8083063 Iustin Pop
  def SetDiskID(self, disk, node_name):
137 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
138 a8083063 Iustin Pop

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

141 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
142 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
143 a8083063 Iustin Pop
    node.
144 a8083063 Iustin Pop

145 a8083063 Iustin Pop
    """
146 a8083063 Iustin Pop
    if disk.children:
147 a8083063 Iustin Pop
      for child in disk.children:
148 a8083063 Iustin Pop
        self.SetDiskID(child, node_name)
149 a8083063 Iustin Pop
150 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
151 a8083063 Iustin Pop
      return
152 a8083063 Iustin Pop
    if disk.dev_type == "drbd":
153 a8083063 Iustin Pop
      pnode, snode, port = disk.logical_id
154 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
155 a8083063 Iustin Pop
        raise errors.ConfigurationError, ("DRBD device not knowing node %s" %
156 a8083063 Iustin Pop
                                          node_name)
157 a8083063 Iustin Pop
      pnode_info = self.GetNodeInfo(pnode)
158 a8083063 Iustin Pop
      snode_info = self.GetNodeInfo(snode)
159 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
160 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
161 a8083063 Iustin Pop
                                        " for %s" % str(disk))
162 a8083063 Iustin Pop
      if pnode == node_name:
163 a8083063 Iustin Pop
        disk.physical_id = (pnode_info.secondary_ip, port,
164 a8083063 Iustin Pop
                            snode_info.secondary_ip, port)
165 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
166 a8083063 Iustin Pop
        disk.physical_id = (snode_info.secondary_ip, port,
167 a8083063 Iustin Pop
                            pnode_info.secondary_ip, port)
168 a8083063 Iustin Pop
    else:
169 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
170 a8083063 Iustin Pop
    return
171 a8083063 Iustin Pop
172 264bb3c5 Michael Hanselmann
  def AddTcpIpPort(self, port):
173 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
174 264bb3c5 Michael Hanselmann
      raise errors.ProgrammerError("Invalid type passed for port")
175 264bb3c5 Michael Hanselmann
176 264bb3c5 Michael Hanselmann
    self._OpenConfig()
177 264bb3c5 Michael Hanselmann
    self._config_data.tcpudp_port_pool.add(port)
178 264bb3c5 Michael Hanselmann
    self._WriteConfig()
179 264bb3c5 Michael Hanselmann
180 264bb3c5 Michael Hanselmann
  def GetPortList():
181 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
182 264bb3c5 Michael Hanselmann

183 264bb3c5 Michael Hanselmann
    """
184 264bb3c5 Michael Hanselmann
    self._OpenConfig()
185 264bb3c5 Michael Hanselmann
    self._ReleaseLock()
186 264bb3c5 Michael Hanselmann
    return self._config_data.tcpudp_port_pool.copy()
187 264bb3c5 Michael Hanselmann
188 a8083063 Iustin Pop
  def AllocatePort(self):
189 a8083063 Iustin Pop
    """Allocate a port.
190 a8083063 Iustin Pop

191 a8083063 Iustin Pop
    The port will be recorded in the cluster config.
192 a8083063 Iustin Pop

193 a8083063 Iustin Pop
    """
194 a8083063 Iustin Pop
    self._OpenConfig()
195 a8083063 Iustin Pop
196 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
197 264bb3c5 Michael Hanselmann
    if self._config_data.tcpudp_port_pool:
198 264bb3c5 Michael Hanselmann
      port = self._config_data.tcpudp_port_pool.pop()
199 264bb3c5 Michael Hanselmann
    else:
200 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
201 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
202 264bb3c5 Michael Hanselmann
        raise errors.ConfigurationError, ("The highest used port is greater"
203 264bb3c5 Michael Hanselmann
                                          " than %s. Aborting." %
204 264bb3c5 Michael Hanselmann
                                          constants.LAST_DRBD_PORT)
205 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
206 a8083063 Iustin Pop
207 a8083063 Iustin Pop
    self._WriteConfig()
208 a8083063 Iustin Pop
    return port
209 a8083063 Iustin Pop
210 a8083063 Iustin Pop
  def GetHostKey(self):
211 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
212 a8083063 Iustin Pop

213 a8083063 Iustin Pop
    Args: None
214 a8083063 Iustin Pop

215 a8083063 Iustin Pop
    Returns: rsa hostkey
216 a8083063 Iustin Pop
    """
217 a8083063 Iustin Pop
    self._OpenConfig()
218 a8083063 Iustin Pop
    self._ReleaseLock()
219 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
220 a8083063 Iustin Pop
221 a8083063 Iustin Pop
  def AddInstance(self, instance):
222 a8083063 Iustin Pop
    """Add an instance to the config.
223 a8083063 Iustin Pop

224 a8083063 Iustin Pop
    This should be used after creating a new instance.
225 a8083063 Iustin Pop

226 a8083063 Iustin Pop
    Args:
227 a8083063 Iustin Pop
      instance: the instance object
228 a8083063 Iustin Pop
    """
229 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
230 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
231 a8083063 Iustin Pop
232 a8083063 Iustin Pop
    self._OpenConfig()
233 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
234 a8083063 Iustin Pop
    self._WriteConfig()
235 a8083063 Iustin Pop
236 a8083063 Iustin Pop
  def MarkInstanceUp(self, instance_name):
237 a8083063 Iustin Pop
    """Mark the instance status to up in the config.
238 a8083063 Iustin Pop

239 a8083063 Iustin Pop
    """
240 a8083063 Iustin Pop
    self._OpenConfig()
241 a8083063 Iustin Pop
242 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
243 a8083063 Iustin Pop
      raise errors.ConfigurationError, ("Unknown instance '%s'" %
244 a8083063 Iustin Pop
                                        instance_name)
245 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
246 a8083063 Iustin Pop
    instance.status = "up"
247 a8083063 Iustin Pop
    self._WriteConfig()
248 a8083063 Iustin Pop
249 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
250 a8083063 Iustin Pop
    """Remove the instance from the configuration.
251 a8083063 Iustin Pop

252 a8083063 Iustin Pop
    """
253 a8083063 Iustin Pop
    self._OpenConfig()
254 a8083063 Iustin Pop
255 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
256 a8083063 Iustin Pop
      raise errors.ConfigurationError, ("Unknown instance '%s'" %
257 a8083063 Iustin Pop
                                        instance_name)
258 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
259 a8083063 Iustin Pop
    self._WriteConfig()
260 a8083063 Iustin Pop
261 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
262 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
263 a8083063 Iustin Pop

264 a8083063 Iustin Pop
    """
265 a8083063 Iustin Pop
266 a8083063 Iustin Pop
    self._OpenConfig()
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
269 a8083063 Iustin Pop
      raise errors.ConfigurationError, ("Unknown instance '%s'" %
270 a8083063 Iustin Pop
                                        instance_name)
271 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
272 a8083063 Iustin Pop
    instance.status = "down"
273 a8083063 Iustin Pop
    self._WriteConfig()
274 a8083063 Iustin Pop
275 a8083063 Iustin Pop
  def GetInstanceList(self):
276 a8083063 Iustin Pop
    """Get the list of instances.
277 a8083063 Iustin Pop

278 a8083063 Iustin Pop
    Returns:
279 a8083063 Iustin Pop
      array of instances, ex. ['instance2.example.com','instance1.example.com']
280 a8083063 Iustin Pop
      these contains all the instances, also the ones in Admin_down state
281 a8083063 Iustin Pop

282 a8083063 Iustin Pop
    """
283 a8083063 Iustin Pop
    self._OpenConfig()
284 a8083063 Iustin Pop
    self._ReleaseLock()
285 a8083063 Iustin Pop
286 a8083063 Iustin Pop
    return self._config_data.instances.keys()
287 a8083063 Iustin Pop
288 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
289 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
290 a8083063 Iustin Pop

291 a8083063 Iustin Pop
    """
292 a8083063 Iustin Pop
    self._OpenConfig()
293 a8083063 Iustin Pop
    self._ReleaseLock()
294 a8083063 Iustin Pop
295 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
296 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
297 a8083063 Iustin Pop
298 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
299 a8083063 Iustin Pop
    """Returns informations about an instance.
300 a8083063 Iustin Pop

301 a8083063 Iustin Pop
    It takes the information from the configuration file. Other informations of
302 a8083063 Iustin Pop
    an instance are taken from the live systems.
303 a8083063 Iustin Pop

304 a8083063 Iustin Pop
    Args:
305 a8083063 Iustin Pop
      instance: name of the instance, ex instance1.example.com
306 a8083063 Iustin Pop

307 a8083063 Iustin Pop
    Returns:
308 a8083063 Iustin Pop
      the instance object
309 a8083063 Iustin Pop

310 a8083063 Iustin Pop
    """
311 a8083063 Iustin Pop
    self._OpenConfig()
312 a8083063 Iustin Pop
    self._ReleaseLock()
313 a8083063 Iustin Pop
314 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
315 a8083063 Iustin Pop
      return None
316 a8083063 Iustin Pop
317 a8083063 Iustin Pop
    return self._config_data.instances[instance_name]
318 a8083063 Iustin Pop
319 a8083063 Iustin Pop
  def AddNode(self, node):
320 a8083063 Iustin Pop
    """Add a node to the configuration.
321 a8083063 Iustin Pop

322 a8083063 Iustin Pop
    Args:
323 a8083063 Iustin Pop
      node: an object.Node instance
324 a8083063 Iustin Pop

325 a8083063 Iustin Pop
    """
326 a8083063 Iustin Pop
    self._OpenConfig()
327 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
328 a8083063 Iustin Pop
    self._WriteConfig()
329 a8083063 Iustin Pop
330 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
331 a8083063 Iustin Pop
    """Remove a node from the configuration.
332 a8083063 Iustin Pop

333 a8083063 Iustin Pop
    """
334 a8083063 Iustin Pop
    self._OpenConfig()
335 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
336 a8083063 Iustin Pop
      raise errors.ConfigurationError, ("Unknown node '%s'" % node_name)
337 a8083063 Iustin Pop
338 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
339 a8083063 Iustin Pop
    self._WriteConfig()
340 a8083063 Iustin Pop
341 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
342 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
343 a8083063 Iustin Pop

344 a8083063 Iustin Pop
    """
345 a8083063 Iustin Pop
    self._OpenConfig()
346 a8083063 Iustin Pop
    self._ReleaseLock()
347 a8083063 Iustin Pop
348 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
349 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
350 a8083063 Iustin Pop
351 a8083063 Iustin Pop
  def GetNodeInfo(self, node_name):
352 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
353 a8083063 Iustin Pop

354 a8083063 Iustin Pop
    Args: node: nodename (tuple) of the node
355 a8083063 Iustin Pop

356 a8083063 Iustin Pop
    Returns: the node object
357 a8083063 Iustin Pop

358 a8083063 Iustin Pop
    """
359 a8083063 Iustin Pop
    self._OpenConfig()
360 a8083063 Iustin Pop
    self._ReleaseLock()
361 a8083063 Iustin Pop
362 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
363 a8083063 Iustin Pop
      return None
364 a8083063 Iustin Pop
365 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
366 a8083063 Iustin Pop
367 a8083063 Iustin Pop
  def GetNodeList(self):
368 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
369 a8083063 Iustin Pop

370 a8083063 Iustin Pop
    """
371 a8083063 Iustin Pop
    self._OpenConfig()
372 a8083063 Iustin Pop
    self._ReleaseLock()
373 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
374 a8083063 Iustin Pop
375 a8083063 Iustin Pop
  def DumpConfig(self):
376 a8083063 Iustin Pop
    """Return the entire configuration of the cluster.
377 a8083063 Iustin Pop
    """
378 a8083063 Iustin Pop
    self._OpenConfig()
379 a8083063 Iustin Pop
    self._ReleaseLock()
380 a8083063 Iustin Pop
    return self._config_data
381 a8083063 Iustin Pop
382 a8083063 Iustin Pop
  def _BumpSerialNo(self):
383 a8083063 Iustin Pop
    """Bump up the serial number of the config.
384 a8083063 Iustin Pop

385 a8083063 Iustin Pop
    """
386 a8083063 Iustin Pop
    self._config_data.cluster.serial_no += 1
387 a8083063 Iustin Pop
388 a8083063 Iustin Pop
  def _OpenConfig(self):
389 a8083063 Iustin Pop
    """Read the config data from disk.
390 a8083063 Iustin Pop

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

395 a8083063 Iustin Pop
    """
396 a8083063 Iustin Pop
    try:
397 a8083063 Iustin Pop
      st = os.stat(self._cfg_file)
398 a8083063 Iustin Pop
    except OSError, err:
399 a8083063 Iustin Pop
      raise errors.ConfigurationError, "Can't stat config file: %s" % err
400 a8083063 Iustin Pop
    if (self._config_data is not None and
401 a8083063 Iustin Pop
        self._config_time is not None and
402 264bb3c5 Michael Hanselmann
        self._config_time == st.st_mtime and
403 264bb3c5 Michael Hanselmann
        self._config_size == st.st_size and
404 264bb3c5 Michael Hanselmann
        self._config_inode == st.st_ino):
405 a8083063 Iustin Pop
      # data is current, so skip loading of config file
406 a8083063 Iustin Pop
      return
407 a8083063 Iustin Pop
    f = open(self._cfg_file, 'r')
408 a8083063 Iustin Pop
    try:
409 a8083063 Iustin Pop
      try:
410 a8083063 Iustin Pop
        data = objects.ConfigObject.Load(f)
411 a8083063 Iustin Pop
      except Exception, err:
412 a8083063 Iustin Pop
        raise errors.ConfigurationError, err
413 a8083063 Iustin Pop
    finally:
414 a8083063 Iustin Pop
      f.close()
415 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
416 a8083063 Iustin Pop
        not hasattr(data.cluster, 'config_version')):
417 a8083063 Iustin Pop
      raise errors.ConfigurationError, ("Incomplete configuration"
418 a8083063 Iustin Pop
                                        " (missing cluster.config_version)")
419 a8083063 Iustin Pop
    if data.cluster.config_version != constants.CONFIG_VERSION:
420 a8083063 Iustin Pop
      raise errors.ConfigurationError, ("Cluster configuration version"
421 a8083063 Iustin Pop
                                        " mismatch, got %s instead of %s" %
422 a8083063 Iustin Pop
                                        (data.cluster.config_version,
423 a8083063 Iustin Pop
                                         constants.CONFIG_VERSION))
424 a8083063 Iustin Pop
    self._config_data = data
425 a8083063 Iustin Pop
    self._config_time = st.st_mtime
426 264bb3c5 Michael Hanselmann
    self._config_size = st.st_size
427 264bb3c5 Michael Hanselmann
    self._config_inode = st.st_ino
428 a8083063 Iustin Pop
429 a8083063 Iustin Pop
  def _ReleaseLock(self):
430 a8083063 Iustin Pop
    """xxxx
431 a8083063 Iustin Pop
    """
432 a8083063 Iustin Pop
433 a8083063 Iustin Pop
  def _DistributeConfig(self):
434 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
435 a8083063 Iustin Pop

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

439 a8083063 Iustin Pop
    """
440 a8083063 Iustin Pop
    if self._offline:
441 a8083063 Iustin Pop
      return True
442 a8083063 Iustin Pop
    bad = False
443 a8083063 Iustin Pop
    nodelist = self.GetNodeList()
444 a8083063 Iustin Pop
    myhostname = socket.gethostname()
445 a8083063 Iustin Pop
446 a8083063 Iustin Pop
    tgt_list = []
447 a8083063 Iustin Pop
    for node in nodelist:
448 a8083063 Iustin Pop
      nodeinfo = self.GetNodeInfo(node)
449 a8083063 Iustin Pop
      if nodeinfo.name == myhostname:
450 a8083063 Iustin Pop
        continue
451 a8083063 Iustin Pop
      tgt_list.append(node)
452 a8083063 Iustin Pop
453 a8083063 Iustin Pop
    result = rpc.call_upload_file(tgt_list, self._cfg_file)
454 a8083063 Iustin Pop
    for node in tgt_list:
455 a8083063 Iustin Pop
      if not result[node]:
456 a8083063 Iustin Pop
        logger.Error("copy of file %s to node %s failed" %
457 a8083063 Iustin Pop
                     (self._cfg_file, node))
458 a8083063 Iustin Pop
        bad = True
459 a8083063 Iustin Pop
    return not bad
460 a8083063 Iustin Pop
461 a8083063 Iustin Pop
  def _WriteConfig(self, destination=None):
462 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
463 a8083063 Iustin Pop

464 a8083063 Iustin Pop
    """
465 a8083063 Iustin Pop
    if destination is None:
466 a8083063 Iustin Pop
      destination = self._cfg_file
467 a8083063 Iustin Pop
    self._BumpSerialNo()
468 a8083063 Iustin Pop
    dir_name, file_name = os.path.split(destination)
469 a8083063 Iustin Pop
    fd, name = tempfile.mkstemp('.newconfig', file_name, dir_name)
470 a8083063 Iustin Pop
    f = os.fdopen(fd, 'w')
471 a8083063 Iustin Pop
    try:
472 a8083063 Iustin Pop
      self._config_data.Dump(f)
473 a8083063 Iustin Pop
      os.fsync(f.fileno())
474 a8083063 Iustin Pop
    finally:
475 a8083063 Iustin Pop
      f.close()
476 a8083063 Iustin Pop
    # we don't need to do os.close(fd) as f.close() did it
477 a8083063 Iustin Pop
    os.rename(name, destination)
478 a8083063 Iustin Pop
    self._DistributeConfig()
479 a8083063 Iustin Pop
480 a8083063 Iustin Pop
  def InitConfig(self, node, primary_ip, secondary_ip,
481 a8083063 Iustin Pop
                 clustername, hostkeypub, mac_prefix, vg_name, def_bridge):
482 a8083063 Iustin Pop
    """Create the initial cluster configuration.
483 a8083063 Iustin Pop

484 a8083063 Iustin Pop
    It will contain the current node, which will also be the master
485 a8083063 Iustin Pop
    node, and no instances or operating systmes.
486 a8083063 Iustin Pop

487 a8083063 Iustin Pop
    Args:
488 a8083063 Iustin Pop
      node: the nodename of the initial node
489 a8083063 Iustin Pop
      primary_ip: the IP address of the current host
490 a8083063 Iustin Pop
      secondary_ip: the secondary IP of the current host or None
491 a8083063 Iustin Pop
      clustername: the name of the cluster
492 a8083063 Iustin Pop
      hostkeypub: the public hostkey of this host
493 a8083063 Iustin Pop

494 264bb3c5 Michael Hanselmann
    """
495 a8083063 Iustin Pop
    hu_port = constants.FIRST_DRBD_PORT - 1
496 a8083063 Iustin Pop
    globalconfig = objects.Cluster(config_version=constants.CONFIG_VERSION,
497 a8083063 Iustin Pop
                                   serial_no=1, master_node=node,
498 a8083063 Iustin Pop
                                   name=clustername,
499 a8083063 Iustin Pop
                                   rsahostkeypub=hostkeypub,
500 a8083063 Iustin Pop
                                   highest_used_port=hu_port,
501 a8083063 Iustin Pop
                                   mac_prefix=mac_prefix,
502 a8083063 Iustin Pop
                                   volume_group_name=vg_name,
503 a8083063 Iustin Pop
                                   default_bridge=def_bridge)
504 a8083063 Iustin Pop
    if secondary_ip is None:
505 a8083063 Iustin Pop
      secondary_ip = primary_ip
506 a8083063 Iustin Pop
    nodeconfig = objects.Node(name=node, primary_ip=primary_ip,
507 a8083063 Iustin Pop
                              secondary_ip=secondary_ip)
508 a8083063 Iustin Pop
509 a8083063 Iustin Pop
    self._config_data = objects.ConfigData(nodes={node: nodeconfig},
510 a8083063 Iustin Pop
                                           instances={},
511 264bb3c5 Michael Hanselmann
                                           cluster=globalconfig,
512 264bb3c5 Michael Hanselmann
                                           tcpudp_port_pool=set())
513 a8083063 Iustin Pop
    self._WriteConfig()
514 a8083063 Iustin Pop
515 a8083063 Iustin Pop
  def GetClusterName(self):
516 a8083063 Iustin Pop
    """Return the cluster name.
517 a8083063 Iustin Pop

518 a8083063 Iustin Pop
    """
519 a8083063 Iustin Pop
    self._OpenConfig()
520 a8083063 Iustin Pop
    self._ReleaseLock()
521 a8083063 Iustin Pop
    return self._config_data.cluster.name
522 a8083063 Iustin Pop
523 a8083063 Iustin Pop
  def GetVGName(self):
524 a8083063 Iustin Pop
    """Return the volume group name.
525 a8083063 Iustin Pop

526 a8083063 Iustin Pop
    """
527 a8083063 Iustin Pop
    self._OpenConfig()
528 a8083063 Iustin Pop
    self._ReleaseLock()
529 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
530 a8083063 Iustin Pop
531 a8083063 Iustin Pop
  def GetDefBridge(self):
532 a8083063 Iustin Pop
    """Return the default bridge.
533 a8083063 Iustin Pop

534 a8083063 Iustin Pop
    """
535 a8083063 Iustin Pop
    self._OpenConfig()
536 a8083063 Iustin Pop
    self._ReleaseLock()
537 a8083063 Iustin Pop
    return self._config_data.cluster.default_bridge
538 a8083063 Iustin Pop
539 a8083063 Iustin Pop
  def GetMACPrefix(self):
540 a8083063 Iustin Pop
    """Return the mac prefix.
541 a8083063 Iustin Pop

542 a8083063 Iustin Pop
    """
543 a8083063 Iustin Pop
    self._OpenConfig()
544 a8083063 Iustin Pop
    self._ReleaseLock()
545 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix