Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ cdb08f44

History | View | Annotate | Download (20.2 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 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 a8083063 Iustin Pop
class ConfigWriter:
47 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
48 a8083063 Iustin Pop

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

73 a8083063 Iustin Pop
    """
74 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
75 a8083063 Iustin Pop
76 a8083063 Iustin Pop
  def GenerateMAC(self):
77 a8083063 Iustin Pop
    """Generate a MAC for an instance.
78 a8083063 Iustin Pop

79 a8083063 Iustin Pop
    This should check the current instances for duplicates.
80 a8083063 Iustin Pop

81 a8083063 Iustin Pop
    """
82 a8083063 Iustin Pop
    self._OpenConfig()
83 a8083063 Iustin Pop
    self._ReleaseLock()
84 a8083063 Iustin Pop
    prefix = self._config_data.cluster.mac_prefix
85 a8083063 Iustin Pop
    all_macs = self._AllMACs()
86 a8083063 Iustin Pop
    retries = 64
87 a8083063 Iustin Pop
    while retries > 0:
88 a8083063 Iustin Pop
      byte1 = random.randrange(0, 256)
89 a8083063 Iustin Pop
      byte2 = random.randrange(0, 256)
90 a8083063 Iustin Pop
      byte3 = random.randrange(0, 256)
91 a8083063 Iustin Pop
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
92 a8083063 Iustin Pop
      if mac not in all_macs:
93 a8083063 Iustin Pop
        break
94 a8083063 Iustin Pop
      retries -= 1
95 a8083063 Iustin Pop
    else:
96 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't generate unique MAC")
97 a8083063 Iustin Pop
    return mac
98 a8083063 Iustin Pop
99 1862d460 Alexander Schreiber
  def IsMacInUse(self, mac):
100 1862d460 Alexander Schreiber
    """Predicate: check if the specified MAC is in use in the Ganeti cluster.
101 1862d460 Alexander Schreiber

102 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
103 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
104 1862d460 Alexander Schreiber

105 1862d460 Alexander Schreiber
    """
106 1862d460 Alexander Schreiber
    self._OpenConfig()
107 1862d460 Alexander Schreiber
    self._ReleaseLock()
108 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
109 1862d460 Alexander Schreiber
    return mac in all_macs
110 1862d460 Alexander Schreiber
111 923b1523 Iustin Pop
  def _ComputeAllLVs(self):
112 923b1523 Iustin Pop
    """Compute the list of all LVs.
113 923b1523 Iustin Pop

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

127 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
128 923b1523 Iustin Pop
    duplicates.
129 923b1523 Iustin Pop

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

136 923b1523 Iustin Pop
    Returns: the unique id as a string
137 923b1523 Iustin Pop

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

160 a8083063 Iustin Pop
    """
161 a8083063 Iustin Pop
    self._OpenConfig()
162 a8083063 Iustin Pop
    self._ReleaseLock()
163 a8083063 Iustin Pop
164 a8083063 Iustin Pop
    result = []
165 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
166 a8083063 Iustin Pop
      for nic in instance.nics:
167 a8083063 Iustin Pop
        result.append(nic.mac)
168 a8083063 Iustin Pop
169 a8083063 Iustin Pop
    return result
170 a8083063 Iustin Pop
171 a8083063 Iustin Pop
  def VerifyConfig(self):
172 a8083063 Iustin Pop
    """Stub verify function.
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
    seen_macs = []
179 a8083063 Iustin Pop
    data = self._config_data
180 a8083063 Iustin Pop
    for instance_name in data.instances:
181 a8083063 Iustin Pop
      instance = data.instances[instance_name]
182 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
183 8522ceeb Iustin Pop
        result.append("instance '%s' has invalid primary node '%s'" %
184 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
185 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
186 a8083063 Iustin Pop
        if snode not in data.nodes:
187 8522ceeb Iustin Pop
          result.append("instance '%s' has invalid secondary node '%s'" %
188 a8083063 Iustin Pop
                        (instance_name, snode))
189 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
190 a8083063 Iustin Pop
        if nic.mac in seen_macs:
191 8522ceeb Iustin Pop
          result.append("instance '%s' has NIC %d mac %s duplicate" %
192 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
193 a8083063 Iustin Pop
        else:
194 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
195 a8083063 Iustin Pop
    return result
196 a8083063 Iustin Pop
197 a8083063 Iustin Pop
  def SetDiskID(self, disk, node_name):
198 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
199 a8083063 Iustin Pop

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

202 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
203 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
204 a8083063 Iustin Pop
    node.
205 a8083063 Iustin Pop

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

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

247 264bb3c5 Michael Hanselmann
    """
248 264bb3c5 Michael Hanselmann
    self._OpenConfig()
249 264bb3c5 Michael Hanselmann
    self._ReleaseLock()
250 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
251 264bb3c5 Michael Hanselmann
252 a8083063 Iustin Pop
  def AllocatePort(self):
253 a8083063 Iustin Pop
    """Allocate a port.
254 a8083063 Iustin Pop

255 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
256 b2fddf63 Iustin Pop
    default port range (and in this case we increase
257 b2fddf63 Iustin Pop
    highest_used_port).
258 a8083063 Iustin Pop

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

279 a8083063 Iustin Pop
    Args: None
280 a8083063 Iustin Pop

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

290 a8083063 Iustin Pop
    This should be used after creating a new instance.
291 a8083063 Iustin Pop

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

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

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

333 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
334 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
335 fc95f88f Iustin Pop
    rename.
336 fc95f88f Iustin Pop

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

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

362 a8083063 Iustin Pop
    Returns:
363 a8083063 Iustin Pop
      array of instances, ex. ['instance2.example.com','instance1.example.com']
364 a8083063 Iustin Pop
      these contains all the instances, also the ones in Admin_down state
365 a8083063 Iustin Pop

366 a8083063 Iustin Pop
    """
367 a8083063 Iustin Pop
    self._OpenConfig()
368 a8083063 Iustin Pop
    self._ReleaseLock()
369 a8083063 Iustin Pop
370 a8083063 Iustin Pop
    return self._config_data.instances.keys()
371 a8083063 Iustin Pop
372 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
373 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
374 a8083063 Iustin Pop

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

385 a8083063 Iustin Pop
    It takes the information from the configuration file. Other informations of
386 a8083063 Iustin Pop
    an instance are taken from the live systems.
387 a8083063 Iustin Pop

388 a8083063 Iustin Pop
    Args:
389 a8083063 Iustin Pop
      instance: name of the instance, ex instance1.example.com
390 a8083063 Iustin Pop

391 a8083063 Iustin Pop
    Returns:
392 a8083063 Iustin Pop
      the instance object
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
    if instance_name not in self._config_data.instances:
399 a8083063 Iustin Pop
      return None
400 a8083063 Iustin Pop
401 a8083063 Iustin Pop
    return self._config_data.instances[instance_name]
402 a8083063 Iustin Pop
403 a8083063 Iustin Pop
  def AddNode(self, node):
404 a8083063 Iustin Pop
    """Add a node to the configuration.
405 a8083063 Iustin Pop

406 a8083063 Iustin Pop
    Args:
407 a8083063 Iustin Pop
      node: an object.Node instance
408 a8083063 Iustin Pop

409 a8083063 Iustin Pop
    """
410 a8083063 Iustin Pop
    self._OpenConfig()
411 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
412 a8083063 Iustin Pop
    self._WriteConfig()
413 a8083063 Iustin Pop
414 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
415 a8083063 Iustin Pop
    """Remove a node from the configuration.
416 a8083063 Iustin Pop

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

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

438 a8083063 Iustin Pop
    Args: node: nodename (tuple) of the node
439 a8083063 Iustin Pop

440 a8083063 Iustin Pop
    Returns: the node object
441 a8083063 Iustin Pop

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

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

469 a8083063 Iustin Pop
    """
470 a8083063 Iustin Pop
    self._config_data.cluster.serial_no += 1
471 a8083063 Iustin Pop
472 a8083063 Iustin Pop
  def _OpenConfig(self):
473 a8083063 Iustin Pop
    """Read the config data from disk.
474 a8083063 Iustin Pop

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

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

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

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

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

578 a8083063 Iustin Pop
    It will contain the current node, which will also be the master
579 a8083063 Iustin Pop
    node, and no instances or operating systmes.
580 a8083063 Iustin Pop

581 a8083063 Iustin Pop
    Args:
582 a8083063 Iustin Pop
      node: the nodename of the initial node
583 a8083063 Iustin Pop
      primary_ip: the IP address of the current host
584 a8083063 Iustin Pop
      secondary_ip: the secondary IP of the current host or None
585 a8083063 Iustin Pop
      hostkeypub: the public hostkey of this host
586 a8083063 Iustin Pop

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

610 a8083063 Iustin Pop
    """
611 a8083063 Iustin Pop
    self._OpenConfig()
612 a8083063 Iustin Pop
    self._ReleaseLock()
613 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
614 a8083063 Iustin Pop
615 a8083063 Iustin Pop
  def GetDefBridge(self):
616 a8083063 Iustin Pop
    """Return the default bridge.
617 a8083063 Iustin Pop

618 a8083063 Iustin Pop
    """
619 a8083063 Iustin Pop
    self._OpenConfig()
620 a8083063 Iustin Pop
    self._ReleaseLock()
621 a8083063 Iustin Pop
    return self._config_data.cluster.default_bridge
622 a8083063 Iustin Pop
623 a8083063 Iustin Pop
  def GetMACPrefix(self):
624 a8083063 Iustin Pop
    """Return the mac prefix.
625 a8083063 Iustin Pop

626 a8083063 Iustin Pop
    """
627 a8083063 Iustin Pop
    self._OpenConfig()
628 a8083063 Iustin Pop
    self._ReleaseLock()
629 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
630 62779dd0 Iustin Pop
631 62779dd0 Iustin Pop
  def GetClusterInfo(self):
632 62779dd0 Iustin Pop
    """Returns informations about the cluster
633 62779dd0 Iustin Pop

634 62779dd0 Iustin Pop
    Returns:
635 62779dd0 Iustin Pop
      the cluster object
636 62779dd0 Iustin Pop

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

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

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