Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ e3e66f02

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

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

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

92 a8083063 Iustin Pop
    This should check the current instances for duplicates.
93 a8083063 Iustin Pop

94 a8083063 Iustin Pop
    """
95 a8083063 Iustin Pop
    self._OpenConfig()
96 a8083063 Iustin Pop
    self._ReleaseLock()
97 a8083063 Iustin Pop
    prefix = self._config_data.cluster.mac_prefix
98 a8083063 Iustin Pop
    all_macs = self._AllMACs()
99 a8083063 Iustin Pop
    retries = 64
100 a8083063 Iustin Pop
    while retries > 0:
101 a8083063 Iustin Pop
      byte1 = random.randrange(0, 256)
102 a8083063 Iustin Pop
      byte2 = random.randrange(0, 256)
103 a8083063 Iustin Pop
      byte3 = random.randrange(0, 256)
104 a8083063 Iustin Pop
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
105 a8083063 Iustin Pop
      if mac not in all_macs:
106 a8083063 Iustin Pop
        break
107 a8083063 Iustin Pop
      retries -= 1
108 a8083063 Iustin Pop
    else:
109 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't generate unique MAC")
110 a8083063 Iustin Pop
    return mac
111 a8083063 Iustin Pop
112 1862d460 Alexander Schreiber
  def IsMacInUse(self, mac):
113 1862d460 Alexander Schreiber
    """Predicate: check if the specified MAC is in use in the Ganeti cluster.
114 1862d460 Alexander Schreiber

115 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
116 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
117 1862d460 Alexander Schreiber

118 1862d460 Alexander Schreiber
    """
119 1862d460 Alexander Schreiber
    self._OpenConfig()
120 1862d460 Alexander Schreiber
    self._ReleaseLock()
121 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
122 1862d460 Alexander Schreiber
    return mac in all_macs
123 1862d460 Alexander Schreiber
124 923b1523 Iustin Pop
  def _ComputeAllLVs(self):
125 923b1523 Iustin Pop
    """Compute the list of all LVs.
126 923b1523 Iustin Pop

127 923b1523 Iustin Pop
    """
128 923b1523 Iustin Pop
    self._OpenConfig()
129 923b1523 Iustin Pop
    self._ReleaseLock()
130 923b1523 Iustin Pop
    lvnames = set()
131 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
132 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
133 923b1523 Iustin Pop
      for lv_list in node_data.values():
134 923b1523 Iustin Pop
        lvnames.update(lv_list)
135 923b1523 Iustin Pop
    return lvnames
136 923b1523 Iustin Pop
137 923b1523 Iustin Pop
  def GenerateUniqueID(self, exceptions=None):
138 923b1523 Iustin Pop
    """Generate an unique disk name.
139 923b1523 Iustin Pop

140 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
141 923b1523 Iustin Pop
    duplicates.
142 923b1523 Iustin Pop

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

149 923b1523 Iustin Pop
    Returns: the unique id as a string
150 923b1523 Iustin Pop

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

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

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

215 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
216 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
217 a8083063 Iustin Pop
    node.
218 a8083063 Iustin Pop

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

249 b2fddf63 Iustin Pop
    """
250 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
251 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
252 264bb3c5 Michael Hanselmann
253 264bb3c5 Michael Hanselmann
    self._OpenConfig()
254 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
255 264bb3c5 Michael Hanselmann
    self._WriteConfig()
256 264bb3c5 Michael Hanselmann
257 b2fddf63 Iustin Pop
  def GetPortList(self):
258 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
259 264bb3c5 Michael Hanselmann

260 264bb3c5 Michael Hanselmann
    """
261 264bb3c5 Michael Hanselmann
    self._OpenConfig()
262 264bb3c5 Michael Hanselmann
    self._ReleaseLock()
263 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
264 264bb3c5 Michael Hanselmann
265 a8083063 Iustin Pop
  def AllocatePort(self):
266 a8083063 Iustin Pop
    """Allocate a port.
267 a8083063 Iustin Pop

268 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
269 b2fddf63 Iustin Pop
    default port range (and in this case we increase
270 b2fddf63 Iustin Pop
    highest_used_port).
271 a8083063 Iustin Pop

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

292 a8083063 Iustin Pop
    Args: None
293 a8083063 Iustin Pop

294 a8083063 Iustin Pop
    Returns: rsa hostkey
295 a8083063 Iustin Pop
    """
296 a8083063 Iustin Pop
    self._OpenConfig()
297 a8083063 Iustin Pop
    self._ReleaseLock()
298 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
299 a8083063 Iustin Pop
300 a8083063 Iustin Pop
  def AddInstance(self, instance):
301 a8083063 Iustin Pop
    """Add an instance to the config.
302 a8083063 Iustin Pop

303 a8083063 Iustin Pop
    This should be used after creating a new instance.
304 a8083063 Iustin Pop

305 a8083063 Iustin Pop
    Args:
306 a8083063 Iustin Pop
      instance: the instance object
307 a8083063 Iustin Pop
    """
308 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
309 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
310 a8083063 Iustin Pop
311 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
312 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
313 e00fb268 Iustin Pop
      logger.Info("Instance '%s' DISK_LAYOUT: %s" % (instance.name, all_lvs))
314 923b1523 Iustin Pop
315 a8083063 Iustin Pop
    self._OpenConfig()
316 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
317 a8083063 Iustin Pop
    self._WriteConfig()
318 a8083063 Iustin Pop
319 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
320 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
321 a8083063 Iustin Pop

322 a8083063 Iustin Pop
    """
323 6a408fb2 Iustin Pop
    if status not in ("up", "down"):
324 6a408fb2 Iustin Pop
      raise errors.ProgrammerError("Invalid status '%s' passed to"
325 6a408fb2 Iustin Pop
                                   " ConfigWriter._SetInstanceStatus()" %
326 6a408fb2 Iustin Pop
                                   status)
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'" %
331 3ecf6786 Iustin Pop
                                      instance_name)
332 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
333 455a3445 Iustin Pop
    if instance.status != status:
334 455a3445 Iustin Pop
      instance.status = status
335 455a3445 Iustin Pop
      self._WriteConfig()
336 a8083063 Iustin Pop
337 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
338 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
339 6a408fb2 Iustin Pop

340 6a408fb2 Iustin Pop
    """
341 6a408fb2 Iustin Pop
    self._SetInstanceStatus(instance_name, "up")
342 6a408fb2 Iustin Pop
343 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
344 a8083063 Iustin Pop
    """Remove the instance from the configuration.
345 a8083063 Iustin Pop

346 a8083063 Iustin Pop
    """
347 a8083063 Iustin Pop
    self._OpenConfig()
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
350 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
351 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
352 a8083063 Iustin Pop
    self._WriteConfig()
353 a8083063 Iustin Pop
354 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
355 fc95f88f Iustin Pop
    """Rename an instance.
356 fc95f88f Iustin Pop

357 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
358 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
359 fc95f88f Iustin Pop
    rename.
360 fc95f88f Iustin Pop

361 fc95f88f Iustin Pop
    """
362 fc95f88f Iustin Pop
    self._OpenConfig()
363 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
364 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
365 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
366 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
367 fc95f88f Iustin Pop
    inst.name = new_name
368 b23c4333 Manuel Franceschini
369 b23c4333 Manuel Franceschini
    for disk in inst.disks:
370 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
371 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
372 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
373 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
374 b23c4333 Manuel Franceschini
                                              os.path.join(file_storage_dir,
375 b23c4333 Manuel Franceschini
                                                           inst.name,
376 b23c4333 Manuel Franceschini
                                                           disk.iv_name))
377 b23c4333 Manuel Franceschini
378 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
379 fc95f88f Iustin Pop
    self._WriteConfig()
380 fc95f88f Iustin Pop
381 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
382 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
383 a8083063 Iustin Pop

384 a8083063 Iustin Pop
    """
385 6a408fb2 Iustin Pop
    self._SetInstanceStatus(instance_name, "down")
386 a8083063 Iustin Pop
387 a8083063 Iustin Pop
  def GetInstanceList(self):
388 a8083063 Iustin Pop
    """Get the list of instances.
389 a8083063 Iustin Pop

390 a8083063 Iustin Pop
    Returns:
391 a8083063 Iustin Pop
      array of instances, ex. ['instance2.example.com','instance1.example.com']
392 a8083063 Iustin Pop
      these contains all the instances, also the ones in Admin_down state
393 a8083063 Iustin Pop

394 a8083063 Iustin Pop
    """
395 a8083063 Iustin Pop
    self._OpenConfig()
396 a8083063 Iustin Pop
    self._ReleaseLock()
397 a8083063 Iustin Pop
398 a8083063 Iustin Pop
    return self._config_data.instances.keys()
399 a8083063 Iustin Pop
400 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
401 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
402 a8083063 Iustin Pop

403 a8083063 Iustin Pop
    """
404 a8083063 Iustin Pop
    self._OpenConfig()
405 a8083063 Iustin Pop
    self._ReleaseLock()
406 a8083063 Iustin Pop
407 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
408 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
409 a8083063 Iustin Pop
410 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
411 a8083063 Iustin Pop
    """Returns informations about an instance.
412 a8083063 Iustin Pop

413 a8083063 Iustin Pop
    It takes the information from the configuration file. Other informations of
414 a8083063 Iustin Pop
    an instance are taken from the live systems.
415 a8083063 Iustin Pop

416 a8083063 Iustin Pop
    Args:
417 a8083063 Iustin Pop
      instance: name of the instance, ex instance1.example.com
418 a8083063 Iustin Pop

419 a8083063 Iustin Pop
    Returns:
420 a8083063 Iustin Pop
      the instance object
421 a8083063 Iustin Pop

422 a8083063 Iustin Pop
    """
423 a8083063 Iustin Pop
    self._OpenConfig()
424 a8083063 Iustin Pop
    self._ReleaseLock()
425 a8083063 Iustin Pop
426 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
427 a8083063 Iustin Pop
      return None
428 a8083063 Iustin Pop
429 a8083063 Iustin Pop
    return self._config_data.instances[instance_name]
430 a8083063 Iustin Pop
431 a8083063 Iustin Pop
  def AddNode(self, node):
432 a8083063 Iustin Pop
    """Add a node to the configuration.
433 a8083063 Iustin Pop

434 a8083063 Iustin Pop
    Args:
435 a8083063 Iustin Pop
      node: an object.Node instance
436 a8083063 Iustin Pop

437 a8083063 Iustin Pop
    """
438 a8083063 Iustin Pop
    self._OpenConfig()
439 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
440 a8083063 Iustin Pop
    self._WriteConfig()
441 a8083063 Iustin Pop
442 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
443 a8083063 Iustin Pop
    """Remove a node from the configuration.
444 a8083063 Iustin Pop

445 a8083063 Iustin Pop
    """
446 a8083063 Iustin Pop
    self._OpenConfig()
447 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
448 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
449 a8083063 Iustin Pop
450 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
451 a8083063 Iustin Pop
    self._WriteConfig()
452 a8083063 Iustin Pop
453 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
454 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
455 a8083063 Iustin Pop

456 a8083063 Iustin Pop
    """
457 a8083063 Iustin Pop
    self._OpenConfig()
458 a8083063 Iustin Pop
    self._ReleaseLock()
459 a8083063 Iustin Pop
460 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
461 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
462 a8083063 Iustin Pop
463 a8083063 Iustin Pop
  def GetNodeInfo(self, node_name):
464 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
465 a8083063 Iustin Pop

466 a8083063 Iustin Pop
    Args: node: nodename (tuple) of the node
467 a8083063 Iustin Pop

468 a8083063 Iustin Pop
    Returns: the node object
469 a8083063 Iustin Pop

470 a8083063 Iustin Pop
    """
471 a8083063 Iustin Pop
    self._OpenConfig()
472 a8083063 Iustin Pop
    self._ReleaseLock()
473 a8083063 Iustin Pop
474 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
475 a8083063 Iustin Pop
      return None
476 a8083063 Iustin Pop
477 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
478 a8083063 Iustin Pop
479 a8083063 Iustin Pop
  def GetNodeList(self):
480 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
481 a8083063 Iustin Pop

482 a8083063 Iustin Pop
    """
483 a8083063 Iustin Pop
    self._OpenConfig()
484 a8083063 Iustin Pop
    self._ReleaseLock()
485 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
486 a8083063 Iustin Pop
487 a8083063 Iustin Pop
  def DumpConfig(self):
488 a8083063 Iustin Pop
    """Return the entire configuration of the cluster.
489 a8083063 Iustin Pop
    """
490 a8083063 Iustin Pop
    self._OpenConfig()
491 a8083063 Iustin Pop
    self._ReleaseLock()
492 a8083063 Iustin Pop
    return self._config_data
493 a8083063 Iustin Pop
494 a8083063 Iustin Pop
  def _BumpSerialNo(self):
495 a8083063 Iustin Pop
    """Bump up the serial number of the config.
496 a8083063 Iustin Pop

497 a8083063 Iustin Pop
    """
498 a8083063 Iustin Pop
    self._config_data.cluster.serial_no += 1
499 a8083063 Iustin Pop
500 a8083063 Iustin Pop
  def _OpenConfig(self):
501 a8083063 Iustin Pop
    """Read the config data from disk.
502 a8083063 Iustin Pop

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

507 a8083063 Iustin Pop
    """
508 a8083063 Iustin Pop
    try:
509 a8083063 Iustin Pop
      st = os.stat(self._cfg_file)
510 a8083063 Iustin Pop
    except OSError, err:
511 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't stat config file: %s" % err)
512 a8083063 Iustin Pop
    if (self._config_data is not None and
513 a8083063 Iustin Pop
        self._config_time is not None and
514 264bb3c5 Michael Hanselmann
        self._config_time == st.st_mtime and
515 264bb3c5 Michael Hanselmann
        self._config_size == st.st_size and
516 264bb3c5 Michael Hanselmann
        self._config_inode == st.st_ino):
517 a8083063 Iustin Pop
      # data is current, so skip loading of config file
518 a8083063 Iustin Pop
      return
519 243cdbcc Michael Hanselmann
520 243cdbcc Michael Hanselmann
    # Make sure the configuration has the right version
521 243cdbcc Michael Hanselmann
    ValidateConfig()
522 243cdbcc Michael Hanselmann
523 a8083063 Iustin Pop
    f = open(self._cfg_file, 'r')
524 a8083063 Iustin Pop
    try:
525 a8083063 Iustin Pop
      try:
526 8d14b30d Iustin Pop
        data = objects.ConfigData.FromDict(serializer.Load(f.read()))
527 a8083063 Iustin Pop
      except Exception, err:
528 3ecf6786 Iustin Pop
        raise errors.ConfigurationError(err)
529 a8083063 Iustin Pop
    finally:
530 a8083063 Iustin Pop
      f.close()
531 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
532 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
533 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
534 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
535 a8083063 Iustin Pop
    self._config_data = data
536 a8083063 Iustin Pop
    self._config_time = st.st_mtime
537 264bb3c5 Michael Hanselmann
    self._config_size = st.st_size
538 264bb3c5 Michael Hanselmann
    self._config_inode = st.st_ino
539 a8083063 Iustin Pop
540 a8083063 Iustin Pop
  def _ReleaseLock(self):
541 a8083063 Iustin Pop
    """xxxx
542 a8083063 Iustin Pop
    """
543 a8083063 Iustin Pop
544 a8083063 Iustin Pop
  def _DistributeConfig(self):
545 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
546 a8083063 Iustin Pop

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

550 a8083063 Iustin Pop
    """
551 a8083063 Iustin Pop
    if self._offline:
552 a8083063 Iustin Pop
      return True
553 a8083063 Iustin Pop
    bad = False
554 a8083063 Iustin Pop
    nodelist = self.GetNodeList()
555 89e1fc26 Iustin Pop
    myhostname = self._my_hostname
556 a8083063 Iustin Pop
557 9ff994da Guido Trotter
    try:
558 9ff994da Guido Trotter
      nodelist.remove(myhostname)
559 9ff994da Guido Trotter
    except ValueError:
560 9ff994da Guido Trotter
      pass
561 a8083063 Iustin Pop
562 41362e70 Guido Trotter
    result = rpc.call_upload_file(nodelist, self._cfg_file)
563 41362e70 Guido Trotter
    for node in nodelist:
564 a8083063 Iustin Pop
      if not result[node]:
565 a8083063 Iustin Pop
        logger.Error("copy of file %s to node %s failed" %
566 a8083063 Iustin Pop
                     (self._cfg_file, node))
567 a8083063 Iustin Pop
        bad = True
568 a8083063 Iustin Pop
    return not bad
569 a8083063 Iustin Pop
570 a8083063 Iustin Pop
  def _WriteConfig(self, destination=None):
571 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
572 a8083063 Iustin Pop

573 a8083063 Iustin Pop
    """
574 a8083063 Iustin Pop
    if destination is None:
575 a8083063 Iustin Pop
      destination = self._cfg_file
576 a8083063 Iustin Pop
    self._BumpSerialNo()
577 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
578 a8083063 Iustin Pop
    dir_name, file_name = os.path.split(destination)
579 a8083063 Iustin Pop
    fd, name = tempfile.mkstemp('.newconfig', file_name, dir_name)
580 a8083063 Iustin Pop
    f = os.fdopen(fd, 'w')
581 a8083063 Iustin Pop
    try:
582 8d14b30d Iustin Pop
      f.write(txt)
583 a8083063 Iustin Pop
      os.fsync(f.fileno())
584 a8083063 Iustin Pop
    finally:
585 a8083063 Iustin Pop
      f.close()
586 a8083063 Iustin Pop
    # we don't need to do os.close(fd) as f.close() did it
587 a8083063 Iustin Pop
    os.rename(name, destination)
588 14e15659 Iustin Pop
    self.write_count += 1
589 fee9556c Iustin Pop
    # re-set our cache as not to re-read the config file
590 fee9556c Iustin Pop
    try:
591 fee9556c Iustin Pop
      st = os.stat(destination)
592 fee9556c Iustin Pop
    except OSError, err:
593 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't stat config file: %s" % err)
594 fee9556c Iustin Pop
    self._config_time = st.st_mtime
595 fee9556c Iustin Pop
    self._config_size = st.st_size
596 fee9556c Iustin Pop
    self._config_inode = st.st_ino
597 fee9556c Iustin Pop
    # and redistribute the config file
598 a8083063 Iustin Pop
    self._DistributeConfig()
599 a8083063 Iustin Pop
600 a8083063 Iustin Pop
  def InitConfig(self, node, primary_ip, secondary_ip,
601 5fcdc80d Iustin Pop
                 hostkeypub, mac_prefix, vg_name, def_bridge):
602 a8083063 Iustin Pop
    """Create the initial cluster configuration.
603 a8083063 Iustin Pop

604 a8083063 Iustin Pop
    It will contain the current node, which will also be the master
605 a8083063 Iustin Pop
    node, and no instances or operating systmes.
606 a8083063 Iustin Pop

607 a8083063 Iustin Pop
    Args:
608 a8083063 Iustin Pop
      node: the nodename of the initial node
609 a8083063 Iustin Pop
      primary_ip: the IP address of the current host
610 a8083063 Iustin Pop
      secondary_ip: the secondary IP of the current host or None
611 a8083063 Iustin Pop
      hostkeypub: the public hostkey of this host
612 a8083063 Iustin Pop

613 264bb3c5 Michael Hanselmann
    """
614 a8083063 Iustin Pop
    hu_port = constants.FIRST_DRBD_PORT - 1
615 243cdbcc Michael Hanselmann
    globalconfig = objects.Cluster(serial_no=1,
616 a8083063 Iustin Pop
                                   rsahostkeypub=hostkeypub,
617 a8083063 Iustin Pop
                                   highest_used_port=hu_port,
618 a8083063 Iustin Pop
                                   mac_prefix=mac_prefix,
619 a8083063 Iustin Pop
                                   volume_group_name=vg_name,
620 b2fddf63 Iustin Pop
                                   default_bridge=def_bridge,
621 b2fddf63 Iustin Pop
                                   tcpudp_port_pool=set())
622 a8083063 Iustin Pop
    if secondary_ip is None:
623 a8083063 Iustin Pop
      secondary_ip = primary_ip
624 a8083063 Iustin Pop
    nodeconfig = objects.Node(name=node, primary_ip=primary_ip,
625 a8083063 Iustin Pop
                              secondary_ip=secondary_ip)
626 a8083063 Iustin Pop
627 a8083063 Iustin Pop
    self._config_data = objects.ConfigData(nodes={node: nodeconfig},
628 a8083063 Iustin Pop
                                           instances={},
629 b2fddf63 Iustin Pop
                                           cluster=globalconfig)
630 a8083063 Iustin Pop
    self._WriteConfig()
631 a8083063 Iustin Pop
632 a8083063 Iustin Pop
  def GetVGName(self):
633 a8083063 Iustin Pop
    """Return the volume group name.
634 a8083063 Iustin Pop

635 a8083063 Iustin Pop
    """
636 a8083063 Iustin Pop
    self._OpenConfig()
637 a8083063 Iustin Pop
    self._ReleaseLock()
638 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
639 a8083063 Iustin Pop
640 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
641 89ff8e15 Manuel Franceschini
    """Set the volume group name.
642 89ff8e15 Manuel Franceschini

643 89ff8e15 Manuel Franceschini
    """
644 89ff8e15 Manuel Franceschini
    self._OpenConfig()
645 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
646 89ff8e15 Manuel Franceschini
    self._WriteConfig()
647 89ff8e15 Manuel Franceschini
648 a8083063 Iustin Pop
  def GetDefBridge(self):
649 a8083063 Iustin Pop
    """Return the default bridge.
650 a8083063 Iustin Pop

651 a8083063 Iustin Pop
    """
652 a8083063 Iustin Pop
    self._OpenConfig()
653 a8083063 Iustin Pop
    self._ReleaseLock()
654 a8083063 Iustin Pop
    return self._config_data.cluster.default_bridge
655 a8083063 Iustin Pop
656 a8083063 Iustin Pop
  def GetMACPrefix(self):
657 a8083063 Iustin Pop
    """Return the mac prefix.
658 a8083063 Iustin Pop

659 a8083063 Iustin Pop
    """
660 a8083063 Iustin Pop
    self._OpenConfig()
661 a8083063 Iustin Pop
    self._ReleaseLock()
662 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
663 62779dd0 Iustin Pop
664 62779dd0 Iustin Pop
  def GetClusterInfo(self):
665 62779dd0 Iustin Pop
    """Returns informations about the cluster
666 62779dd0 Iustin Pop

667 62779dd0 Iustin Pop
    Returns:
668 62779dd0 Iustin Pop
      the cluster object
669 62779dd0 Iustin Pop

670 62779dd0 Iustin Pop
    """
671 62779dd0 Iustin Pop
    self._OpenConfig()
672 62779dd0 Iustin Pop
    self._ReleaseLock()
673 62779dd0 Iustin Pop
674 62779dd0 Iustin Pop
    return self._config_data.cluster
675 e00fb268 Iustin Pop
676 e00fb268 Iustin Pop
  def Update(self, target):
677 e00fb268 Iustin Pop
    """Notify function to be called after updates.
678 e00fb268 Iustin Pop

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

685 e00fb268 Iustin Pop
    """
686 e00fb268 Iustin Pop
    if self._config_data is None:
687 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
688 3ecf6786 Iustin Pop
                                   " cannot save.")
689 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
690 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
691 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
692 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
693 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
694 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
695 e00fb268 Iustin Pop
    else:
696 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
697 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
698 e00fb268 Iustin Pop
    if not test:
699 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
700 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
701 e00fb268 Iustin Pop
    self._WriteConfig()