Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 14e15659

History | View | Annotate | Download (19.8 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 923b1523 Iustin Pop
  def _ComputeAllLVs(self):
100 923b1523 Iustin Pop
    """Compute the list of all LVs.
101 923b1523 Iustin Pop

102 923b1523 Iustin Pop
    """
103 923b1523 Iustin Pop
    self._OpenConfig()
104 923b1523 Iustin Pop
    self._ReleaseLock()
105 923b1523 Iustin Pop
    lvnames = set()
106 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
107 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
108 923b1523 Iustin Pop
      for lv_list in node_data.values():
109 923b1523 Iustin Pop
        lvnames.update(lv_list)
110 923b1523 Iustin Pop
    return lvnames
111 923b1523 Iustin Pop
112 923b1523 Iustin Pop
  def GenerateUniqueID(self, exceptions=None):
113 923b1523 Iustin Pop
    """Generate an unique disk name.
114 923b1523 Iustin Pop

115 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
116 923b1523 Iustin Pop
    duplicates.
117 923b1523 Iustin Pop

118 923b1523 Iustin Pop
    Args:
119 923b1523 Iustin Pop
      - exceptions: a list with some other names which should be checked
120 923b1523 Iustin Pop
                    for uniqueness (used for example when you want to get
121 923b1523 Iustin Pop
                    more than one id at one time without adding each one in
122 923b1523 Iustin Pop
                    turn to the config file
123 923b1523 Iustin Pop

124 923b1523 Iustin Pop
    Returns: the unique id as a string
125 923b1523 Iustin Pop

126 923b1523 Iustin Pop
    """
127 923b1523 Iustin Pop
    existing = set()
128 923b1523 Iustin Pop
    existing.update(self._temporary_ids)
129 923b1523 Iustin Pop
    existing.update(self._ComputeAllLVs())
130 923b1523 Iustin Pop
    existing.update(self._config_data.instances.keys())
131 923b1523 Iustin Pop
    existing.update(self._config_data.nodes.keys())
132 923b1523 Iustin Pop
    if exceptions is not None:
133 923b1523 Iustin Pop
      existing.update(exceptions)
134 923b1523 Iustin Pop
    retries = 64
135 923b1523 Iustin Pop
    while retries > 0:
136 59072e7e Michael Hanselmann
      unique_id = utils.GetUUID()
137 923b1523 Iustin Pop
      if unique_id not in existing and unique_id is not None:
138 923b1523 Iustin Pop
        break
139 923b1523 Iustin Pop
    else:
140 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Not able generate an unique ID"
141 3ecf6786 Iustin Pop
                                      " (last tried ID: %s" % unique_id)
142 923b1523 Iustin Pop
    self._temporary_ids.add(unique_id)
143 923b1523 Iustin Pop
    return unique_id
144 923b1523 Iustin Pop
145 a8083063 Iustin Pop
  def _AllMACs(self):
146 a8083063 Iustin Pop
    """Return all MACs present in the config.
147 a8083063 Iustin Pop

148 a8083063 Iustin Pop
    """
149 a8083063 Iustin Pop
    self._OpenConfig()
150 a8083063 Iustin Pop
    self._ReleaseLock()
151 a8083063 Iustin Pop
152 a8083063 Iustin Pop
    result = []
153 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
154 a8083063 Iustin Pop
      for nic in instance.nics:
155 a8083063 Iustin Pop
        result.append(nic.mac)
156 a8083063 Iustin Pop
157 a8083063 Iustin Pop
    return result
158 a8083063 Iustin Pop
159 a8083063 Iustin Pop
  def VerifyConfig(self):
160 a8083063 Iustin Pop
    """Stub verify function.
161 a8083063 Iustin Pop
    """
162 a8083063 Iustin Pop
    self._OpenConfig()
163 a8083063 Iustin Pop
    self._ReleaseLock()
164 a8083063 Iustin Pop
165 a8083063 Iustin Pop
    result = []
166 a8083063 Iustin Pop
    seen_macs = []
167 a8083063 Iustin Pop
    data = self._config_data
168 a8083063 Iustin Pop
    for instance_name in data.instances:
169 a8083063 Iustin Pop
      instance = data.instances[instance_name]
170 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
171 a8083063 Iustin Pop
        result.append("Instance '%s' has invalid primary node '%s'" %
172 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
173 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
174 a8083063 Iustin Pop
        if snode not in data.nodes:
175 a8083063 Iustin Pop
          result.append("Instance '%s' has invalid secondary node '%s'" %
176 a8083063 Iustin Pop
                        (instance_name, snode))
177 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
178 a8083063 Iustin Pop
        if nic.mac in seen_macs:
179 a8083063 Iustin Pop
          result.append("Instance '%s' has NIC %d mac %s duplicate" %
180 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
181 a8083063 Iustin Pop
        else:
182 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
183 a8083063 Iustin Pop
    return result
184 a8083063 Iustin Pop
185 a8083063 Iustin Pop
  def SetDiskID(self, disk, node_name):
186 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
187 a8083063 Iustin Pop

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

190 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
191 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
192 a8083063 Iustin Pop
    node.
193 a8083063 Iustin Pop

194 a8083063 Iustin Pop
    """
195 a8083063 Iustin Pop
    if disk.children:
196 a8083063 Iustin Pop
      for child in disk.children:
197 a8083063 Iustin Pop
        self.SetDiskID(child, node_name)
198 a8083063 Iustin Pop
199 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
200 a8083063 Iustin Pop
      return
201 a8083063 Iustin Pop
    if disk.dev_type == "drbd":
202 a8083063 Iustin Pop
      pnode, snode, port = disk.logical_id
203 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
204 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
205 3ecf6786 Iustin Pop
                                        node_name)
206 a8083063 Iustin Pop
      pnode_info = self.GetNodeInfo(pnode)
207 a8083063 Iustin Pop
      snode_info = self.GetNodeInfo(snode)
208 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
209 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
210 a8083063 Iustin Pop
                                        " for %s" % str(disk))
211 a8083063 Iustin Pop
      if pnode == node_name:
212 a8083063 Iustin Pop
        disk.physical_id = (pnode_info.secondary_ip, port,
213 a8083063 Iustin Pop
                            snode_info.secondary_ip, port)
214 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
215 a8083063 Iustin Pop
        disk.physical_id = (snode_info.secondary_ip, port,
216 a8083063 Iustin Pop
                            pnode_info.secondary_ip, port)
217 a8083063 Iustin Pop
    else:
218 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
219 a8083063 Iustin Pop
    return
220 a8083063 Iustin Pop
221 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
222 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
223 b2fddf63 Iustin Pop

224 b2fddf63 Iustin Pop
    """
225 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
226 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
227 264bb3c5 Michael Hanselmann
228 264bb3c5 Michael Hanselmann
    self._OpenConfig()
229 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
230 264bb3c5 Michael Hanselmann
    self._WriteConfig()
231 264bb3c5 Michael Hanselmann
232 b2fddf63 Iustin Pop
  def GetPortList(self):
233 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
234 264bb3c5 Michael Hanselmann

235 264bb3c5 Michael Hanselmann
    """
236 264bb3c5 Michael Hanselmann
    self._OpenConfig()
237 264bb3c5 Michael Hanselmann
    self._ReleaseLock()
238 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
239 264bb3c5 Michael Hanselmann
240 a8083063 Iustin Pop
  def AllocatePort(self):
241 a8083063 Iustin Pop
    """Allocate a port.
242 a8083063 Iustin Pop

243 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
244 b2fddf63 Iustin Pop
    default port range (and in this case we increase
245 b2fddf63 Iustin Pop
    highest_used_port).
246 a8083063 Iustin Pop

247 a8083063 Iustin Pop
    """
248 a8083063 Iustin Pop
    self._OpenConfig()
249 a8083063 Iustin Pop
250 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
251 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
252 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
253 264bb3c5 Michael Hanselmann
    else:
254 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
255 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
256 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
257 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
258 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
259 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
260 a8083063 Iustin Pop
261 a8083063 Iustin Pop
    self._WriteConfig()
262 a8083063 Iustin Pop
    return port
263 a8083063 Iustin Pop
264 a8083063 Iustin Pop
  def GetHostKey(self):
265 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
266 a8083063 Iustin Pop

267 a8083063 Iustin Pop
    Args: None
268 a8083063 Iustin Pop

269 a8083063 Iustin Pop
    Returns: rsa hostkey
270 a8083063 Iustin Pop
    """
271 a8083063 Iustin Pop
    self._OpenConfig()
272 a8083063 Iustin Pop
    self._ReleaseLock()
273 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
274 a8083063 Iustin Pop
275 a8083063 Iustin Pop
  def AddInstance(self, instance):
276 a8083063 Iustin Pop
    """Add an instance to the config.
277 a8083063 Iustin Pop

278 a8083063 Iustin Pop
    This should be used after creating a new instance.
279 a8083063 Iustin Pop

280 a8083063 Iustin Pop
    Args:
281 a8083063 Iustin Pop
      instance: the instance object
282 a8083063 Iustin Pop
    """
283 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
284 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
285 a8083063 Iustin Pop
286 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
287 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
288 e00fb268 Iustin Pop
      logger.Info("Instance '%s' DISK_LAYOUT: %s" % (instance.name, all_lvs))
289 923b1523 Iustin Pop
290 a8083063 Iustin Pop
    self._OpenConfig()
291 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
292 a8083063 Iustin Pop
    self._WriteConfig()
293 a8083063 Iustin Pop
294 a8083063 Iustin Pop
  def MarkInstanceUp(self, instance_name):
295 a8083063 Iustin Pop
    """Mark the instance status to up in the config.
296 a8083063 Iustin Pop

297 a8083063 Iustin Pop
    """
298 a8083063 Iustin Pop
    self._OpenConfig()
299 a8083063 Iustin Pop
300 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
301 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
302 3ecf6786 Iustin Pop
                                      instance_name)
303 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
304 a8083063 Iustin Pop
    instance.status = "up"
305 a8083063 Iustin Pop
    self._WriteConfig()
306 a8083063 Iustin Pop
307 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
308 a8083063 Iustin Pop
    """Remove the instance from the configuration.
309 a8083063 Iustin Pop

310 a8083063 Iustin Pop
    """
311 a8083063 Iustin Pop
    self._OpenConfig()
312 a8083063 Iustin Pop
313 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
314 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
315 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
316 a8083063 Iustin Pop
    self._WriteConfig()
317 a8083063 Iustin Pop
318 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
319 fc95f88f Iustin Pop
    """Rename an instance.
320 fc95f88f Iustin Pop

321 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
322 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
323 fc95f88f Iustin Pop
    rename.
324 fc95f88f Iustin Pop

325 fc95f88f Iustin Pop
    """
326 fc95f88f Iustin Pop
    self._OpenConfig()
327 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
328 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
329 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
330 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
331 fc95f88f Iustin Pop
    inst.name = new_name
332 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
333 fc95f88f Iustin Pop
    self._WriteConfig()
334 fc95f88f Iustin Pop
335 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
336 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
337 a8083063 Iustin Pop

338 a8083063 Iustin Pop
    """
339 a8083063 Iustin Pop
    self._OpenConfig()
340 a8083063 Iustin Pop
341 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
342 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
343 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
344 a8083063 Iustin Pop
    instance.status = "down"
345 a8083063 Iustin Pop
    self._WriteConfig()
346 a8083063 Iustin Pop
347 a8083063 Iustin Pop
  def GetInstanceList(self):
348 a8083063 Iustin Pop
    """Get the list of instances.
349 a8083063 Iustin Pop

350 a8083063 Iustin Pop
    Returns:
351 a8083063 Iustin Pop
      array of instances, ex. ['instance2.example.com','instance1.example.com']
352 a8083063 Iustin Pop
      these contains all the instances, also the ones in Admin_down state
353 a8083063 Iustin Pop

354 a8083063 Iustin Pop
    """
355 a8083063 Iustin Pop
    self._OpenConfig()
356 a8083063 Iustin Pop
    self._ReleaseLock()
357 a8083063 Iustin Pop
358 a8083063 Iustin Pop
    return self._config_data.instances.keys()
359 a8083063 Iustin Pop
360 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
361 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
362 a8083063 Iustin Pop

363 a8083063 Iustin Pop
    """
364 a8083063 Iustin Pop
    self._OpenConfig()
365 a8083063 Iustin Pop
    self._ReleaseLock()
366 a8083063 Iustin Pop
367 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
368 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
369 a8083063 Iustin Pop
370 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
371 a8083063 Iustin Pop
    """Returns informations about an instance.
372 a8083063 Iustin Pop

373 a8083063 Iustin Pop
    It takes the information from the configuration file. Other informations of
374 a8083063 Iustin Pop
    an instance are taken from the live systems.
375 a8083063 Iustin Pop

376 a8083063 Iustin Pop
    Args:
377 a8083063 Iustin Pop
      instance: name of the instance, ex instance1.example.com
378 a8083063 Iustin Pop

379 a8083063 Iustin Pop
    Returns:
380 a8083063 Iustin Pop
      the instance object
381 a8083063 Iustin Pop

382 a8083063 Iustin Pop
    """
383 a8083063 Iustin Pop
    self._OpenConfig()
384 a8083063 Iustin Pop
    self._ReleaseLock()
385 a8083063 Iustin Pop
386 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
387 a8083063 Iustin Pop
      return None
388 a8083063 Iustin Pop
389 a8083063 Iustin Pop
    return self._config_data.instances[instance_name]
390 a8083063 Iustin Pop
391 a8083063 Iustin Pop
  def AddNode(self, node):
392 a8083063 Iustin Pop
    """Add a node to the configuration.
393 a8083063 Iustin Pop

394 a8083063 Iustin Pop
    Args:
395 a8083063 Iustin Pop
      node: an object.Node instance
396 a8083063 Iustin Pop

397 a8083063 Iustin Pop
    """
398 a8083063 Iustin Pop
    self._OpenConfig()
399 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
400 a8083063 Iustin Pop
    self._WriteConfig()
401 a8083063 Iustin Pop
402 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
403 a8083063 Iustin Pop
    """Remove a node from the configuration.
404 a8083063 Iustin Pop

405 a8083063 Iustin Pop
    """
406 a8083063 Iustin Pop
    self._OpenConfig()
407 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
408 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
409 a8083063 Iustin Pop
410 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
411 a8083063 Iustin Pop
    self._WriteConfig()
412 a8083063 Iustin Pop
413 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
414 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
415 a8083063 Iustin Pop

416 a8083063 Iustin Pop
    """
417 a8083063 Iustin Pop
    self._OpenConfig()
418 a8083063 Iustin Pop
    self._ReleaseLock()
419 a8083063 Iustin Pop
420 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
421 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
422 a8083063 Iustin Pop
423 a8083063 Iustin Pop
  def GetNodeInfo(self, node_name):
424 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
425 a8083063 Iustin Pop

426 a8083063 Iustin Pop
    Args: node: nodename (tuple) of the node
427 a8083063 Iustin Pop

428 a8083063 Iustin Pop
    Returns: the node object
429 a8083063 Iustin Pop

430 a8083063 Iustin Pop
    """
431 a8083063 Iustin Pop
    self._OpenConfig()
432 a8083063 Iustin Pop
    self._ReleaseLock()
433 a8083063 Iustin Pop
434 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
435 a8083063 Iustin Pop
      return None
436 a8083063 Iustin Pop
437 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
438 a8083063 Iustin Pop
439 a8083063 Iustin Pop
  def GetNodeList(self):
440 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
441 a8083063 Iustin Pop

442 a8083063 Iustin Pop
    """
443 a8083063 Iustin Pop
    self._OpenConfig()
444 a8083063 Iustin Pop
    self._ReleaseLock()
445 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
446 a8083063 Iustin Pop
447 a8083063 Iustin Pop
  def DumpConfig(self):
448 a8083063 Iustin Pop
    """Return the entire configuration of the cluster.
449 a8083063 Iustin Pop
    """
450 a8083063 Iustin Pop
    self._OpenConfig()
451 a8083063 Iustin Pop
    self._ReleaseLock()
452 a8083063 Iustin Pop
    return self._config_data
453 a8083063 Iustin Pop
454 a8083063 Iustin Pop
  def _BumpSerialNo(self):
455 a8083063 Iustin Pop
    """Bump up the serial number of the config.
456 a8083063 Iustin Pop

457 a8083063 Iustin Pop
    """
458 a8083063 Iustin Pop
    self._config_data.cluster.serial_no += 1
459 a8083063 Iustin Pop
460 a8083063 Iustin Pop
  def _OpenConfig(self):
461 a8083063 Iustin Pop
    """Read the config data from disk.
462 a8083063 Iustin Pop

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

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

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

511 a8083063 Iustin Pop
    """
512 a8083063 Iustin Pop
    if self._offline:
513 a8083063 Iustin Pop
      return True
514 a8083063 Iustin Pop
    bad = False
515 a8083063 Iustin Pop
    nodelist = self.GetNodeList()
516 89e1fc26 Iustin Pop
    myhostname = self._my_hostname
517 a8083063 Iustin Pop
518 a8083063 Iustin Pop
    tgt_list = []
519 a8083063 Iustin Pop
    for node in nodelist:
520 a8083063 Iustin Pop
      nodeinfo = self.GetNodeInfo(node)
521 a8083063 Iustin Pop
      if nodeinfo.name == myhostname:
522 a8083063 Iustin Pop
        continue
523 a8083063 Iustin Pop
      tgt_list.append(node)
524 a8083063 Iustin Pop
525 a8083063 Iustin Pop
    result = rpc.call_upload_file(tgt_list, self._cfg_file)
526 a8083063 Iustin Pop
    for node in tgt_list:
527 a8083063 Iustin Pop
      if not result[node]:
528 a8083063 Iustin Pop
        logger.Error("copy of file %s to node %s failed" %
529 a8083063 Iustin Pop
                     (self._cfg_file, node))
530 a8083063 Iustin Pop
        bad = True
531 a8083063 Iustin Pop
    return not bad
532 a8083063 Iustin Pop
533 a8083063 Iustin Pop
  def _WriteConfig(self, destination=None):
534 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
535 a8083063 Iustin Pop

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

566 a8083063 Iustin Pop
    It will contain the current node, which will also be the master
567 a8083063 Iustin Pop
    node, and no instances or operating systmes.
568 a8083063 Iustin Pop

569 a8083063 Iustin Pop
    Args:
570 a8083063 Iustin Pop
      node: the nodename of the initial node
571 a8083063 Iustin Pop
      primary_ip: the IP address of the current host
572 a8083063 Iustin Pop
      secondary_ip: the secondary IP of the current host or None
573 a8083063 Iustin Pop
      hostkeypub: the public hostkey of this host
574 a8083063 Iustin Pop

575 264bb3c5 Michael Hanselmann
    """
576 a8083063 Iustin Pop
    hu_port = constants.FIRST_DRBD_PORT - 1
577 a8083063 Iustin Pop
    globalconfig = objects.Cluster(config_version=constants.CONFIG_VERSION,
578 330cda58 Michael Hanselmann
                                   serial_no=1,
579 a8083063 Iustin Pop
                                   rsahostkeypub=hostkeypub,
580 a8083063 Iustin Pop
                                   highest_used_port=hu_port,
581 a8083063 Iustin Pop
                                   mac_prefix=mac_prefix,
582 a8083063 Iustin Pop
                                   volume_group_name=vg_name,
583 b2fddf63 Iustin Pop
                                   default_bridge=def_bridge,
584 b2fddf63 Iustin Pop
                                   tcpudp_port_pool=set())
585 a8083063 Iustin Pop
    if secondary_ip is None:
586 a8083063 Iustin Pop
      secondary_ip = primary_ip
587 a8083063 Iustin Pop
    nodeconfig = objects.Node(name=node, primary_ip=primary_ip,
588 a8083063 Iustin Pop
                              secondary_ip=secondary_ip)
589 a8083063 Iustin Pop
590 a8083063 Iustin Pop
    self._config_data = objects.ConfigData(nodes={node: nodeconfig},
591 a8083063 Iustin Pop
                                           instances={},
592 b2fddf63 Iustin Pop
                                           cluster=globalconfig)
593 a8083063 Iustin Pop
    self._WriteConfig()
594 a8083063 Iustin Pop
595 a8083063 Iustin Pop
  def GetVGName(self):
596 a8083063 Iustin Pop
    """Return the volume group name.
597 a8083063 Iustin Pop

598 a8083063 Iustin Pop
    """
599 a8083063 Iustin Pop
    self._OpenConfig()
600 a8083063 Iustin Pop
    self._ReleaseLock()
601 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
602 a8083063 Iustin Pop
603 a8083063 Iustin Pop
  def GetDefBridge(self):
604 a8083063 Iustin Pop
    """Return the default bridge.
605 a8083063 Iustin Pop

606 a8083063 Iustin Pop
    """
607 a8083063 Iustin Pop
    self._OpenConfig()
608 a8083063 Iustin Pop
    self._ReleaseLock()
609 a8083063 Iustin Pop
    return self._config_data.cluster.default_bridge
610 a8083063 Iustin Pop
611 a8083063 Iustin Pop
  def GetMACPrefix(self):
612 a8083063 Iustin Pop
    """Return the mac prefix.
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.mac_prefix
618 62779dd0 Iustin Pop
619 62779dd0 Iustin Pop
  def GetClusterInfo(self):
620 62779dd0 Iustin Pop
    """Returns informations about the cluster
621 62779dd0 Iustin Pop

622 62779dd0 Iustin Pop
    Returns:
623 62779dd0 Iustin Pop
      the cluster object
624 62779dd0 Iustin Pop

625 62779dd0 Iustin Pop
    """
626 62779dd0 Iustin Pop
    self._OpenConfig()
627 62779dd0 Iustin Pop
    self._ReleaseLock()
628 62779dd0 Iustin Pop
629 62779dd0 Iustin Pop
    return self._config_data.cluster
630 e00fb268 Iustin Pop
631 e00fb268 Iustin Pop
  def Update(self, target):
632 e00fb268 Iustin Pop
    """Notify function to be called after updates.
633 e00fb268 Iustin Pop

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

640 e00fb268 Iustin Pop
    """
641 e00fb268 Iustin Pop
    if self._config_data is None:
642 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
643 3ecf6786 Iustin Pop
                                   " cannot save.")
644 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
645 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
646 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
647 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
648 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
649 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
650 e00fb268 Iustin Pop
    else:
651 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
652 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
653 e00fb268 Iustin Pop
    if not test:
654 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
655 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
656 e00fb268 Iustin Pop
    self._WriteConfig()