Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 9ff7e35c

History | View | Annotate | Download (20.1 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 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 a8083063 Iustin Pop
38 a8083063 Iustin Pop
from ganeti import errors
39 a8083063 Iustin Pop
from ganeti import logger
40 a8083063 Iustin Pop
from ganeti import utils
41 a8083063 Iustin Pop
from ganeti import constants
42 a8083063 Iustin Pop
from ganeti import rpc
43 a8083063 Iustin Pop
from ganeti import objects
44 a8083063 Iustin Pop
45 319856a9 Michael Hanselmann
46 923b1523 Iustin Pop
def _my_uuidgen():
47 923b1523 Iustin Pop
  """Poor-man's uuidgen using the uuidgen binary.
48 923b1523 Iustin Pop

49 923b1523 Iustin Pop
  """
50 923b1523 Iustin Pop
  result = utils.RunCmd(["uuidgen", "-r"])
51 923b1523 Iustin Pop
  if result.failed:
52 923b1523 Iustin Pop
    return None
53 923b1523 Iustin Pop
  return result.stdout.rstrip('\n')
54 923b1523 Iustin Pop
55 923b1523 Iustin Pop
56 923b1523 Iustin Pop
try:
57 923b1523 Iustin Pop
  import uuid
58 923b1523 Iustin Pop
  _uuidgen = uuid.uuid4
59 923b1523 Iustin Pop
except ImportError:
60 923b1523 Iustin Pop
  _uuidgen = _my_uuidgen
61 923b1523 Iustin Pop
62 a8083063 Iustin Pop
63 a8083063 Iustin Pop
class ConfigWriter:
64 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
65 a8083063 Iustin Pop

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

89 a8083063 Iustin Pop
    """
90 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
91 a8083063 Iustin Pop
92 a8083063 Iustin Pop
  def GenerateMAC(self):
93 a8083063 Iustin Pop
    """Generate a MAC for an instance.
94 a8083063 Iustin Pop

95 a8083063 Iustin Pop
    This should check the current instances for duplicates.
96 a8083063 Iustin Pop

97 a8083063 Iustin Pop
    """
98 a8083063 Iustin Pop
    self._OpenConfig()
99 a8083063 Iustin Pop
    self._ReleaseLock()
100 a8083063 Iustin Pop
    prefix = self._config_data.cluster.mac_prefix
101 a8083063 Iustin Pop
    all_macs = self._AllMACs()
102 a8083063 Iustin Pop
    retries = 64
103 a8083063 Iustin Pop
    while retries > 0:
104 a8083063 Iustin Pop
      byte1 = random.randrange(0, 256)
105 a8083063 Iustin Pop
      byte2 = random.randrange(0, 256)
106 a8083063 Iustin Pop
      byte3 = random.randrange(0, 256)
107 a8083063 Iustin Pop
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
108 a8083063 Iustin Pop
      if mac not in all_macs:
109 a8083063 Iustin Pop
        break
110 a8083063 Iustin Pop
      retries -= 1
111 a8083063 Iustin Pop
    else:
112 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't generate unique MAC")
113 a8083063 Iustin Pop
    return mac
114 a8083063 Iustin Pop
115 923b1523 Iustin Pop
  def _ComputeAllLVs(self):
116 923b1523 Iustin Pop
    """Compute the list of all LVs.
117 923b1523 Iustin Pop

118 923b1523 Iustin Pop
    """
119 923b1523 Iustin Pop
    self._OpenConfig()
120 923b1523 Iustin Pop
    self._ReleaseLock()
121 923b1523 Iustin Pop
    lvnames = set()
122 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
123 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
124 923b1523 Iustin Pop
      for lv_list in node_data.values():
125 923b1523 Iustin Pop
        lvnames.update(lv_list)
126 923b1523 Iustin Pop
    return lvnames
127 923b1523 Iustin Pop
128 923b1523 Iustin Pop
  def GenerateUniqueID(self, exceptions=None):
129 923b1523 Iustin Pop
    """Generate an unique disk name.
130 923b1523 Iustin Pop

131 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
132 923b1523 Iustin Pop
    duplicates.
133 923b1523 Iustin Pop

134 923b1523 Iustin Pop
    Args:
135 923b1523 Iustin Pop
      - exceptions: a list with some other names which should be checked
136 923b1523 Iustin Pop
                    for uniqueness (used for example when you want to get
137 923b1523 Iustin Pop
                    more than one id at one time without adding each one in
138 923b1523 Iustin Pop
                    turn to the config file
139 923b1523 Iustin Pop

140 923b1523 Iustin Pop
    Returns: the unique id as a string
141 923b1523 Iustin Pop

142 923b1523 Iustin Pop
    """
143 923b1523 Iustin Pop
    existing = set()
144 923b1523 Iustin Pop
    existing.update(self._temporary_ids)
145 923b1523 Iustin Pop
    existing.update(self._ComputeAllLVs())
146 923b1523 Iustin Pop
    existing.update(self._config_data.instances.keys())
147 923b1523 Iustin Pop
    existing.update(self._config_data.nodes.keys())
148 923b1523 Iustin Pop
    if exceptions is not None:
149 923b1523 Iustin Pop
      existing.update(exceptions)
150 923b1523 Iustin Pop
    retries = 64
151 923b1523 Iustin Pop
    while retries > 0:
152 923b1523 Iustin Pop
      unique_id = _uuidgen()
153 923b1523 Iustin Pop
      if unique_id not in existing and unique_id is not None:
154 923b1523 Iustin Pop
        break
155 923b1523 Iustin Pop
    else:
156 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Not able generate an unique ID"
157 3ecf6786 Iustin Pop
                                      " (last tried ID: %s" % unique_id)
158 923b1523 Iustin Pop
    self._temporary_ids.add(unique_id)
159 923b1523 Iustin Pop
    return unique_id
160 923b1523 Iustin Pop
161 a8083063 Iustin Pop
  def _AllMACs(self):
162 a8083063 Iustin Pop
    """Return all MACs present in the config.
163 a8083063 Iustin Pop

164 a8083063 Iustin Pop
    """
165 a8083063 Iustin Pop
    self._OpenConfig()
166 a8083063 Iustin Pop
    self._ReleaseLock()
167 a8083063 Iustin Pop
168 a8083063 Iustin Pop
    result = []
169 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
170 a8083063 Iustin Pop
      for nic in instance.nics:
171 a8083063 Iustin Pop
        result.append(nic.mac)
172 a8083063 Iustin Pop
173 a8083063 Iustin Pop
    return result
174 a8083063 Iustin Pop
175 a8083063 Iustin Pop
  def VerifyConfig(self):
176 a8083063 Iustin Pop
    """Stub verify function.
177 a8083063 Iustin Pop
    """
178 a8083063 Iustin Pop
    self._OpenConfig()
179 a8083063 Iustin Pop
    self._ReleaseLock()
180 a8083063 Iustin Pop
181 a8083063 Iustin Pop
    result = []
182 a8083063 Iustin Pop
    seen_macs = []
183 a8083063 Iustin Pop
    data = self._config_data
184 a8083063 Iustin Pop
    for instance_name in data.instances:
185 a8083063 Iustin Pop
      instance = data.instances[instance_name]
186 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
187 a8083063 Iustin Pop
        result.append("Instance '%s' has invalid primary node '%s'" %
188 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
189 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
190 a8083063 Iustin Pop
        if snode not in data.nodes:
191 a8083063 Iustin Pop
          result.append("Instance '%s' has invalid secondary node '%s'" %
192 a8083063 Iustin Pop
                        (instance_name, snode))
193 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
194 a8083063 Iustin Pop
        if nic.mac in seen_macs:
195 a8083063 Iustin Pop
          result.append("Instance '%s' has NIC %d mac %s duplicate" %
196 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
197 a8083063 Iustin Pop
        else:
198 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
199 a8083063 Iustin Pop
    return result
200 a8083063 Iustin Pop
201 a8083063 Iustin Pop
  def SetDiskID(self, disk, node_name):
202 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
203 a8083063 Iustin Pop

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

206 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
207 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
208 a8083063 Iustin Pop
    node.
209 a8083063 Iustin Pop

210 a8083063 Iustin Pop
    """
211 a8083063 Iustin Pop
    if disk.children:
212 a8083063 Iustin Pop
      for child in disk.children:
213 a8083063 Iustin Pop
        self.SetDiskID(child, node_name)
214 a8083063 Iustin Pop
215 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
216 a8083063 Iustin Pop
      return
217 a8083063 Iustin Pop
    if disk.dev_type == "drbd":
218 a8083063 Iustin Pop
      pnode, snode, port = disk.logical_id
219 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
220 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
221 3ecf6786 Iustin Pop
                                        node_name)
222 a8083063 Iustin Pop
      pnode_info = self.GetNodeInfo(pnode)
223 a8083063 Iustin Pop
      snode_info = self.GetNodeInfo(snode)
224 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
225 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
226 a8083063 Iustin Pop
                                        " for %s" % str(disk))
227 a8083063 Iustin Pop
      if pnode == node_name:
228 a8083063 Iustin Pop
        disk.physical_id = (pnode_info.secondary_ip, port,
229 a8083063 Iustin Pop
                            snode_info.secondary_ip, port)
230 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
231 a8083063 Iustin Pop
        disk.physical_id = (snode_info.secondary_ip, port,
232 a8083063 Iustin Pop
                            pnode_info.secondary_ip, port)
233 a8083063 Iustin Pop
    else:
234 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
235 a8083063 Iustin Pop
    return
236 a8083063 Iustin Pop
237 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
238 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
239 b2fddf63 Iustin Pop

240 b2fddf63 Iustin Pop
    """
241 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
242 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
243 264bb3c5 Michael Hanselmann
244 264bb3c5 Michael Hanselmann
    self._OpenConfig()
245 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
246 264bb3c5 Michael Hanselmann
    self._WriteConfig()
247 264bb3c5 Michael Hanselmann
248 b2fddf63 Iustin Pop
  def GetPortList(self):
249 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
250 264bb3c5 Michael Hanselmann

251 264bb3c5 Michael Hanselmann
    """
252 264bb3c5 Michael Hanselmann
    self._OpenConfig()
253 264bb3c5 Michael Hanselmann
    self._ReleaseLock()
254 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
255 264bb3c5 Michael Hanselmann
256 a8083063 Iustin Pop
  def AllocatePort(self):
257 a8083063 Iustin Pop
    """Allocate a port.
258 a8083063 Iustin Pop

259 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
260 b2fddf63 Iustin Pop
    default port range (and in this case we increase
261 b2fddf63 Iustin Pop
    highest_used_port).
262 a8083063 Iustin Pop

263 a8083063 Iustin Pop
    """
264 a8083063 Iustin Pop
    self._OpenConfig()
265 a8083063 Iustin Pop
266 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
267 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
268 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
269 264bb3c5 Michael Hanselmann
    else:
270 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
271 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
272 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
273 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
274 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
275 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
276 a8083063 Iustin Pop
277 a8083063 Iustin Pop
    self._WriteConfig()
278 a8083063 Iustin Pop
    return port
279 a8083063 Iustin Pop
280 a8083063 Iustin Pop
  def GetHostKey(self):
281 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
282 a8083063 Iustin Pop

283 a8083063 Iustin Pop
    Args: None
284 a8083063 Iustin Pop

285 a8083063 Iustin Pop
    Returns: rsa hostkey
286 a8083063 Iustin Pop
    """
287 a8083063 Iustin Pop
    self._OpenConfig()
288 a8083063 Iustin Pop
    self._ReleaseLock()
289 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
290 a8083063 Iustin Pop
291 a8083063 Iustin Pop
  def AddInstance(self, instance):
292 a8083063 Iustin Pop
    """Add an instance to the config.
293 a8083063 Iustin Pop

294 a8083063 Iustin Pop
    This should be used after creating a new instance.
295 a8083063 Iustin Pop

296 a8083063 Iustin Pop
    Args:
297 a8083063 Iustin Pop
      instance: the instance object
298 a8083063 Iustin Pop
    """
299 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
300 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
301 a8083063 Iustin Pop
302 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
303 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
304 e00fb268 Iustin Pop
      logger.Info("Instance '%s' DISK_LAYOUT: %s" % (instance.name, all_lvs))
305 923b1523 Iustin Pop
306 a8083063 Iustin Pop
    self._OpenConfig()
307 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
308 a8083063 Iustin Pop
    self._WriteConfig()
309 a8083063 Iustin Pop
310 a8083063 Iustin Pop
  def MarkInstanceUp(self, instance_name):
311 a8083063 Iustin Pop
    """Mark the instance status to up in the config.
312 a8083063 Iustin Pop

313 a8083063 Iustin Pop
    """
314 a8083063 Iustin Pop
    self._OpenConfig()
315 a8083063 Iustin Pop
316 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
317 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
318 3ecf6786 Iustin Pop
                                      instance_name)
319 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
320 a8083063 Iustin Pop
    instance.status = "up"
321 a8083063 Iustin Pop
    self._WriteConfig()
322 a8083063 Iustin Pop
323 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
324 a8083063 Iustin Pop
    """Remove the instance from the configuration.
325 a8083063 Iustin Pop

326 a8083063 Iustin Pop
    """
327 a8083063 Iustin Pop
    self._OpenConfig()
328 a8083063 Iustin Pop
329 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
330 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
331 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
332 a8083063 Iustin Pop
    self._WriteConfig()
333 a8083063 Iustin Pop
334 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
335 fc95f88f Iustin Pop
    """Rename an instance.
336 fc95f88f Iustin Pop

337 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
338 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
339 fc95f88f Iustin Pop
    rename.
340 fc95f88f Iustin Pop

341 fc95f88f Iustin Pop
    """
342 fc95f88f Iustin Pop
    self._OpenConfig()
343 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
344 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
345 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
346 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
347 fc95f88f Iustin Pop
    inst.name = new_name
348 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
349 fc95f88f Iustin Pop
    self._WriteConfig()
350 fc95f88f Iustin Pop
351 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
352 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
353 a8083063 Iustin Pop

354 a8083063 Iustin Pop
    """
355 a8083063 Iustin Pop
    self._OpenConfig()
356 a8083063 Iustin Pop
357 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
358 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
359 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
360 a8083063 Iustin Pop
    instance.status = "down"
361 a8083063 Iustin Pop
    self._WriteConfig()
362 a8083063 Iustin Pop
363 a8083063 Iustin Pop
  def GetInstanceList(self):
364 a8083063 Iustin Pop
    """Get the list of instances.
365 a8083063 Iustin Pop

366 a8083063 Iustin Pop
    Returns:
367 a8083063 Iustin Pop
      array of instances, ex. ['instance2.example.com','instance1.example.com']
368 a8083063 Iustin Pop
      these contains all the instances, also the ones in Admin_down state
369 a8083063 Iustin Pop

370 a8083063 Iustin Pop
    """
371 a8083063 Iustin Pop
    self._OpenConfig()
372 a8083063 Iustin Pop
    self._ReleaseLock()
373 a8083063 Iustin Pop
374 a8083063 Iustin Pop
    return self._config_data.instances.keys()
375 a8083063 Iustin Pop
376 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
377 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
378 a8083063 Iustin Pop

379 a8083063 Iustin Pop
    """
380 a8083063 Iustin Pop
    self._OpenConfig()
381 a8083063 Iustin Pop
    self._ReleaseLock()
382 a8083063 Iustin Pop
383 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
384 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
385 a8083063 Iustin Pop
386 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
387 a8083063 Iustin Pop
    """Returns informations about an instance.
388 a8083063 Iustin Pop

389 a8083063 Iustin Pop
    It takes the information from the configuration file. Other informations of
390 a8083063 Iustin Pop
    an instance are taken from the live systems.
391 a8083063 Iustin Pop

392 a8083063 Iustin Pop
    Args:
393 a8083063 Iustin Pop
      instance: name of the instance, ex instance1.example.com
394 a8083063 Iustin Pop

395 a8083063 Iustin Pop
    Returns:
396 a8083063 Iustin Pop
      the instance object
397 a8083063 Iustin Pop

398 a8083063 Iustin Pop
    """
399 a8083063 Iustin Pop
    self._OpenConfig()
400 a8083063 Iustin Pop
    self._ReleaseLock()
401 a8083063 Iustin Pop
402 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
403 a8083063 Iustin Pop
      return None
404 a8083063 Iustin Pop
405 a8083063 Iustin Pop
    return self._config_data.instances[instance_name]
406 a8083063 Iustin Pop
407 a8083063 Iustin Pop
  def AddNode(self, node):
408 a8083063 Iustin Pop
    """Add a node to the configuration.
409 a8083063 Iustin Pop

410 a8083063 Iustin Pop
    Args:
411 a8083063 Iustin Pop
      node: an object.Node instance
412 a8083063 Iustin Pop

413 a8083063 Iustin Pop
    """
414 a8083063 Iustin Pop
    self._OpenConfig()
415 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
416 a8083063 Iustin Pop
    self._WriteConfig()
417 a8083063 Iustin Pop
418 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
419 a8083063 Iustin Pop
    """Remove a node from the configuration.
420 a8083063 Iustin Pop

421 a8083063 Iustin Pop
    """
422 a8083063 Iustin Pop
    self._OpenConfig()
423 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
424 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
425 a8083063 Iustin Pop
426 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
427 a8083063 Iustin Pop
    self._WriteConfig()
428 a8083063 Iustin Pop
429 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
430 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
431 a8083063 Iustin Pop

432 a8083063 Iustin Pop
    """
433 a8083063 Iustin Pop
    self._OpenConfig()
434 a8083063 Iustin Pop
    self._ReleaseLock()
435 a8083063 Iustin Pop
436 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
437 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
438 a8083063 Iustin Pop
439 a8083063 Iustin Pop
  def GetNodeInfo(self, node_name):
440 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
441 a8083063 Iustin Pop

442 a8083063 Iustin Pop
    Args: node: nodename (tuple) of the node
443 a8083063 Iustin Pop

444 a8083063 Iustin Pop
    Returns: the node object
445 a8083063 Iustin Pop

446 a8083063 Iustin Pop
    """
447 a8083063 Iustin Pop
    self._OpenConfig()
448 a8083063 Iustin Pop
    self._ReleaseLock()
449 a8083063 Iustin Pop
450 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
451 a8083063 Iustin Pop
      return None
452 a8083063 Iustin Pop
453 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
454 a8083063 Iustin Pop
455 a8083063 Iustin Pop
  def GetNodeList(self):
456 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
457 a8083063 Iustin Pop

458 a8083063 Iustin Pop
    """
459 a8083063 Iustin Pop
    self._OpenConfig()
460 a8083063 Iustin Pop
    self._ReleaseLock()
461 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
462 a8083063 Iustin Pop
463 a8083063 Iustin Pop
  def DumpConfig(self):
464 a8083063 Iustin Pop
    """Return the entire configuration of the cluster.
465 a8083063 Iustin Pop
    """
466 a8083063 Iustin Pop
    self._OpenConfig()
467 a8083063 Iustin Pop
    self._ReleaseLock()
468 a8083063 Iustin Pop
    return self._config_data
469 a8083063 Iustin Pop
470 a8083063 Iustin Pop
  def _BumpSerialNo(self):
471 a8083063 Iustin Pop
    """Bump up the serial number of the config.
472 a8083063 Iustin Pop

473 a8083063 Iustin Pop
    """
474 a8083063 Iustin Pop
    self._config_data.cluster.serial_no += 1
475 a8083063 Iustin Pop
476 a8083063 Iustin Pop
  def _OpenConfig(self):
477 a8083063 Iustin Pop
    """Read the config data from disk.
478 a8083063 Iustin Pop

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

483 a8083063 Iustin Pop
    """
484 a8083063 Iustin Pop
    try:
485 a8083063 Iustin Pop
      st = os.stat(self._cfg_file)
486 a8083063 Iustin Pop
    except OSError, err:
487 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't stat config file: %s" % err)
488 a8083063 Iustin Pop
    if (self._config_data is not None and
489 a8083063 Iustin Pop
        self._config_time is not None and
490 264bb3c5 Michael Hanselmann
        self._config_time == st.st_mtime and
491 264bb3c5 Michael Hanselmann
        self._config_size == st.st_size and
492 264bb3c5 Michael Hanselmann
        self._config_inode == st.st_ino):
493 a8083063 Iustin Pop
      # data is current, so skip loading of config file
494 a8083063 Iustin Pop
      return
495 a8083063 Iustin Pop
    f = open(self._cfg_file, 'r')
496 a8083063 Iustin Pop
    try:
497 a8083063 Iustin Pop
      try:
498 319856a9 Michael Hanselmann
        data = objects.ConfigData.Load(f)
499 a8083063 Iustin Pop
      except Exception, err:
500 319856a9 Michael Hanselmann
        raise
501 3ecf6786 Iustin Pop
        raise errors.ConfigurationError(err)
502 a8083063 Iustin Pop
    finally:
503 a8083063 Iustin Pop
      f.close()
504 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
505 a8083063 Iustin Pop
        not hasattr(data.cluster, 'config_version')):
506 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
507 3ecf6786 Iustin Pop
                                      " (missing cluster.config_version)")
508 a8083063 Iustin Pop
    if data.cluster.config_version != constants.CONFIG_VERSION:
509 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Cluster configuration version"
510 3ecf6786 Iustin Pop
                                      " mismatch, got %s instead of %s" %
511 3ecf6786 Iustin Pop
                                      (data.cluster.config_version,
512 3ecf6786 Iustin Pop
                                       constants.CONFIG_VERSION))
513 a8083063 Iustin Pop
    self._config_data = data
514 a8083063 Iustin Pop
    self._config_time = st.st_mtime
515 264bb3c5 Michael Hanselmann
    self._config_size = st.st_size
516 264bb3c5 Michael Hanselmann
    self._config_inode = st.st_ino
517 a8083063 Iustin Pop
518 a8083063 Iustin Pop
  def _ReleaseLock(self):
519 a8083063 Iustin Pop
    """xxxx
520 a8083063 Iustin Pop
    """
521 a8083063 Iustin Pop
522 a8083063 Iustin Pop
  def _DistributeConfig(self):
523 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
524 a8083063 Iustin Pop

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

528 a8083063 Iustin Pop
    """
529 a8083063 Iustin Pop
    if self._offline:
530 a8083063 Iustin Pop
      return True
531 a8083063 Iustin Pop
    bad = False
532 a8083063 Iustin Pop
    nodelist = self.GetNodeList()
533 89e1fc26 Iustin Pop
    myhostname = self._my_hostname
534 a8083063 Iustin Pop
535 a8083063 Iustin Pop
    tgt_list = []
536 a8083063 Iustin Pop
    for node in nodelist:
537 a8083063 Iustin Pop
      nodeinfo = self.GetNodeInfo(node)
538 a8083063 Iustin Pop
      if nodeinfo.name == myhostname:
539 a8083063 Iustin Pop
        continue
540 a8083063 Iustin Pop
      tgt_list.append(node)
541 a8083063 Iustin Pop
542 a8083063 Iustin Pop
    result = rpc.call_upload_file(tgt_list, self._cfg_file)
543 a8083063 Iustin Pop
    for node in tgt_list:
544 a8083063 Iustin Pop
      if not result[node]:
545 a8083063 Iustin Pop
        logger.Error("copy of file %s to node %s failed" %
546 a8083063 Iustin Pop
                     (self._cfg_file, node))
547 a8083063 Iustin Pop
        bad = True
548 a8083063 Iustin Pop
    return not bad
549 a8083063 Iustin Pop
550 a8083063 Iustin Pop
  def _WriteConfig(self, destination=None):
551 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
552 a8083063 Iustin Pop

553 a8083063 Iustin Pop
    """
554 a8083063 Iustin Pop
    if destination is None:
555 a8083063 Iustin Pop
      destination = self._cfg_file
556 a8083063 Iustin Pop
    self._BumpSerialNo()
557 a8083063 Iustin Pop
    dir_name, file_name = os.path.split(destination)
558 a8083063 Iustin Pop
    fd, name = tempfile.mkstemp('.newconfig', file_name, dir_name)
559 a8083063 Iustin Pop
    f = os.fdopen(fd, 'w')
560 a8083063 Iustin Pop
    try:
561 a8083063 Iustin Pop
      self._config_data.Dump(f)
562 a8083063 Iustin Pop
      os.fsync(f.fileno())
563 a8083063 Iustin Pop
    finally:
564 a8083063 Iustin Pop
      f.close()
565 a8083063 Iustin Pop
    # we don't need to do os.close(fd) as f.close() did it
566 a8083063 Iustin Pop
    os.rename(name, destination)
567 fee9556c Iustin Pop
    # re-set our cache as not to re-read the config file
568 fee9556c Iustin Pop
    try:
569 fee9556c Iustin Pop
      st = os.stat(destination)
570 fee9556c Iustin Pop
    except OSError, err:
571 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't stat config file: %s" % err)
572 fee9556c Iustin Pop
    self._config_time = st.st_mtime
573 fee9556c Iustin Pop
    self._config_size = st.st_size
574 fee9556c Iustin Pop
    self._config_inode = st.st_ino
575 fee9556c Iustin Pop
    # and redistribute the config file
576 a8083063 Iustin Pop
    self._DistributeConfig()
577 a8083063 Iustin Pop
578 a8083063 Iustin Pop
  def InitConfig(self, node, primary_ip, secondary_ip,
579 5fcdc80d Iustin Pop
                 hostkeypub, mac_prefix, vg_name, def_bridge):
580 a8083063 Iustin Pop
    """Create the initial cluster configuration.
581 a8083063 Iustin Pop

582 a8083063 Iustin Pop
    It will contain the current node, which will also be the master
583 a8083063 Iustin Pop
    node, and no instances or operating systmes.
584 a8083063 Iustin Pop

585 a8083063 Iustin Pop
    Args:
586 a8083063 Iustin Pop
      node: the nodename of the initial node
587 a8083063 Iustin Pop
      primary_ip: the IP address of the current host
588 a8083063 Iustin Pop
      secondary_ip: the secondary IP of the current host or None
589 a8083063 Iustin Pop
      hostkeypub: the public hostkey of this host
590 a8083063 Iustin Pop

591 264bb3c5 Michael Hanselmann
    """
592 a8083063 Iustin Pop
    hu_port = constants.FIRST_DRBD_PORT - 1
593 a8083063 Iustin Pop
    globalconfig = objects.Cluster(config_version=constants.CONFIG_VERSION,
594 330cda58 Michael Hanselmann
                                   serial_no=1,
595 a8083063 Iustin Pop
                                   rsahostkeypub=hostkeypub,
596 a8083063 Iustin Pop
                                   highest_used_port=hu_port,
597 a8083063 Iustin Pop
                                   mac_prefix=mac_prefix,
598 a8083063 Iustin Pop
                                   volume_group_name=vg_name,
599 b2fddf63 Iustin Pop
                                   default_bridge=def_bridge,
600 b2fddf63 Iustin Pop
                                   tcpudp_port_pool=set())
601 a8083063 Iustin Pop
    if secondary_ip is None:
602 a8083063 Iustin Pop
      secondary_ip = primary_ip
603 a8083063 Iustin Pop
    nodeconfig = objects.Node(name=node, primary_ip=primary_ip,
604 a8083063 Iustin Pop
                              secondary_ip=secondary_ip)
605 a8083063 Iustin Pop
606 a8083063 Iustin Pop
    self._config_data = objects.ConfigData(nodes={node: nodeconfig},
607 a8083063 Iustin Pop
                                           instances={},
608 b2fddf63 Iustin Pop
                                           cluster=globalconfig)
609 a8083063 Iustin Pop
    self._WriteConfig()
610 a8083063 Iustin Pop
611 a8083063 Iustin Pop
  def GetVGName(self):
612 a8083063 Iustin Pop
    """Return the volume group name.
613 a8083063 Iustin Pop

614 a8083063 Iustin Pop
    """
615 a8083063 Iustin Pop
    self._OpenConfig()
616 a8083063 Iustin Pop
    self._ReleaseLock()
617 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
618 a8083063 Iustin Pop
619 a8083063 Iustin Pop
  def GetDefBridge(self):
620 a8083063 Iustin Pop
    """Return the default bridge.
621 a8083063 Iustin Pop

622 a8083063 Iustin Pop
    """
623 a8083063 Iustin Pop
    self._OpenConfig()
624 a8083063 Iustin Pop
    self._ReleaseLock()
625 a8083063 Iustin Pop
    return self._config_data.cluster.default_bridge
626 a8083063 Iustin Pop
627 a8083063 Iustin Pop
  def GetMACPrefix(self):
628 a8083063 Iustin Pop
    """Return the mac prefix.
629 a8083063 Iustin Pop

630 a8083063 Iustin Pop
    """
631 a8083063 Iustin Pop
    self._OpenConfig()
632 a8083063 Iustin Pop
    self._ReleaseLock()
633 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
634 62779dd0 Iustin Pop
635 62779dd0 Iustin Pop
  def GetClusterInfo(self):
636 62779dd0 Iustin Pop
    """Returns informations about the cluster
637 62779dd0 Iustin Pop

638 62779dd0 Iustin Pop
    Returns:
639 62779dd0 Iustin Pop
      the cluster object
640 62779dd0 Iustin Pop

641 62779dd0 Iustin Pop
    """
642 62779dd0 Iustin Pop
    self._OpenConfig()
643 62779dd0 Iustin Pop
    self._ReleaseLock()
644 62779dd0 Iustin Pop
645 62779dd0 Iustin Pop
    return self._config_data.cluster
646 e00fb268 Iustin Pop
647 e00fb268 Iustin Pop
  def Update(self, target):
648 e00fb268 Iustin Pop
    """Notify function to be called after updates.
649 e00fb268 Iustin Pop

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

656 e00fb268 Iustin Pop
    """
657 e00fb268 Iustin Pop
    if self._config_data is None:
658 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
659 3ecf6786 Iustin Pop
                                   " cannot save.")
660 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
661 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
662 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
663 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
664 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
665 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
666 e00fb268 Iustin Pop
    else:
667 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
668 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
669 e00fb268 Iustin Pop
    if not test:
670 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
671 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
672 e00fb268 Iustin Pop
    self._WriteConfig()