Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ d65e5776

History | View | Annotate | Download (23.6 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 d8470559 Michael Hanselmann
import logging
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
from ganeti import errors
40 f78ede4e Guido Trotter
from ganeti import locking
41 a8083063 Iustin Pop
from ganeti import logger
42 a8083063 Iustin Pop
from ganeti import utils
43 a8083063 Iustin Pop
from ganeti import constants
44 a8083063 Iustin Pop
from ganeti import rpc
45 a8083063 Iustin Pop
from ganeti import objects
46 8d14b30d Iustin Pop
from ganeti import serializer
47 243cdbcc Michael Hanselmann
from ganeti import ssconf
48 243cdbcc Michael Hanselmann
49 243cdbcc Michael Hanselmann
50 f78ede4e Guido Trotter
_config_lock = locking.SharedLock()
51 f78ede4e Guido Trotter
52 f78ede4e Guido Trotter
53 243cdbcc Michael Hanselmann
def ValidateConfig():
54 243cdbcc Michael Hanselmann
  sstore = ssconf.SimpleStore()
55 243cdbcc Michael Hanselmann
56 243cdbcc Michael Hanselmann
  if sstore.GetConfigVersion() != constants.CONFIG_VERSION:
57 243cdbcc Michael Hanselmann
    raise errors.ConfigurationError("Cluster configuration version"
58 243cdbcc Michael Hanselmann
                                    " mismatch, got %s instead of %s" %
59 243cdbcc Michael Hanselmann
                                    (sstore.GetConfigVersion(),
60 243cdbcc Michael Hanselmann
                                     constants.CONFIG_VERSION))
61 a8083063 Iustin Pop
62 319856a9 Michael Hanselmann
63 a8083063 Iustin Pop
class ConfigWriter:
64 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
65 a8083063 Iustin Pop

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

91 a8083063 Iustin Pop
    """
92 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
93 a8083063 Iustin Pop
94 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
95 a8083063 Iustin Pop
  def GenerateMAC(self):
96 a8083063 Iustin Pop
    """Generate a MAC for an instance.
97 a8083063 Iustin Pop

98 a8083063 Iustin Pop
    This should check the current instances for duplicates.
99 a8083063 Iustin Pop

100 a8083063 Iustin Pop
    """
101 a8083063 Iustin Pop
    self._OpenConfig()
102 a8083063 Iustin Pop
    prefix = self._config_data.cluster.mac_prefix
103 a8083063 Iustin Pop
    all_macs = self._AllMACs()
104 a8083063 Iustin Pop
    retries = 64
105 a8083063 Iustin Pop
    while retries > 0:
106 a8083063 Iustin Pop
      byte1 = random.randrange(0, 256)
107 a8083063 Iustin Pop
      byte2 = random.randrange(0, 256)
108 a8083063 Iustin Pop
      byte3 = random.randrange(0, 256)
109 a8083063 Iustin Pop
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
110 a8083063 Iustin Pop
      if mac not in all_macs:
111 a8083063 Iustin Pop
        break
112 a8083063 Iustin Pop
      retries -= 1
113 a8083063 Iustin Pop
    else:
114 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't generate unique MAC")
115 a8083063 Iustin Pop
    return mac
116 a8083063 Iustin Pop
117 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
118 1862d460 Alexander Schreiber
  def IsMacInUse(self, mac):
119 1862d460 Alexander Schreiber
    """Predicate: check if the specified MAC is in use in the Ganeti cluster.
120 1862d460 Alexander Schreiber

121 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
122 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
123 1862d460 Alexander Schreiber

124 1862d460 Alexander Schreiber
    """
125 1862d460 Alexander Schreiber
    self._OpenConfig()
126 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
127 1862d460 Alexander Schreiber
    return mac in all_macs
128 1862d460 Alexander Schreiber
129 923b1523 Iustin Pop
  def _ComputeAllLVs(self):
130 923b1523 Iustin Pop
    """Compute the list of all LVs.
131 923b1523 Iustin Pop

132 923b1523 Iustin Pop
    """
133 923b1523 Iustin Pop
    self._OpenConfig()
134 923b1523 Iustin Pop
    lvnames = set()
135 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
136 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
137 923b1523 Iustin Pop
      for lv_list in node_data.values():
138 923b1523 Iustin Pop
        lvnames.update(lv_list)
139 923b1523 Iustin Pop
    return lvnames
140 923b1523 Iustin Pop
141 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
142 923b1523 Iustin Pop
  def GenerateUniqueID(self, exceptions=None):
143 923b1523 Iustin Pop
    """Generate an unique disk name.
144 923b1523 Iustin Pop

145 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
146 923b1523 Iustin Pop
    duplicates.
147 923b1523 Iustin Pop

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

154 923b1523 Iustin Pop
    Returns: the unique id as a string
155 923b1523 Iustin Pop

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

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

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

219 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
220 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
221 a8083063 Iustin Pop
    node.
222 a8083063 Iustin Pop

223 f78ede4e Guido Trotter
    This function is for internal use, when the config lock is already held.
224 f78ede4e Guido Trotter

225 a8083063 Iustin Pop
    """
226 a8083063 Iustin Pop
    if disk.children:
227 a8083063 Iustin Pop
      for child in disk.children:
228 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
229 a8083063 Iustin Pop
230 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
231 a8083063 Iustin Pop
      return
232 a1f445d3 Iustin Pop
    if disk.dev_type in constants.LDS_DRBD:
233 a8083063 Iustin Pop
      pnode, snode, port = disk.logical_id
234 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
235 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
236 3ecf6786 Iustin Pop
                                        node_name)
237 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
238 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
239 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
240 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
241 a8083063 Iustin Pop
                                        " for %s" % str(disk))
242 a8083063 Iustin Pop
      if pnode == node_name:
243 a8083063 Iustin Pop
        disk.physical_id = (pnode_info.secondary_ip, port,
244 a8083063 Iustin Pop
                            snode_info.secondary_ip, port)
245 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
246 a8083063 Iustin Pop
        disk.physical_id = (snode_info.secondary_ip, port,
247 a8083063 Iustin Pop
                            pnode_info.secondary_ip, port)
248 a8083063 Iustin Pop
    else:
249 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
250 a8083063 Iustin Pop
    return
251 a8083063 Iustin Pop
252 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
253 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
254 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
255 f78ede4e Guido Trotter

256 f78ede4e Guido Trotter
    This is used only for drbd, which needs ip/port configuration.
257 f78ede4e Guido Trotter

258 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
259 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
260 f78ede4e Guido Trotter
    node.
261 f78ede4e Guido Trotter

262 f78ede4e Guido Trotter
    """
263 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
264 f78ede4e Guido Trotter
265 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
266 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
267 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
268 b2fddf63 Iustin Pop

269 b2fddf63 Iustin Pop
    """
270 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
271 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
272 264bb3c5 Michael Hanselmann
273 264bb3c5 Michael Hanselmann
    self._OpenConfig()
274 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
275 264bb3c5 Michael Hanselmann
    self._WriteConfig()
276 264bb3c5 Michael Hanselmann
277 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
278 b2fddf63 Iustin Pop
  def GetPortList(self):
279 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
280 264bb3c5 Michael Hanselmann

281 264bb3c5 Michael Hanselmann
    """
282 264bb3c5 Michael Hanselmann
    self._OpenConfig()
283 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
284 264bb3c5 Michael Hanselmann
285 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
286 a8083063 Iustin Pop
  def AllocatePort(self):
287 a8083063 Iustin Pop
    """Allocate a port.
288 a8083063 Iustin Pop

289 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
290 b2fddf63 Iustin Pop
    default port range (and in this case we increase
291 b2fddf63 Iustin Pop
    highest_used_port).
292 a8083063 Iustin Pop

293 a8083063 Iustin Pop
    """
294 a8083063 Iustin Pop
    self._OpenConfig()
295 a8083063 Iustin Pop
296 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
297 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
298 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
299 264bb3c5 Michael Hanselmann
    else:
300 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
301 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
302 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
303 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
304 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
305 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
306 a8083063 Iustin Pop
307 a8083063 Iustin Pop
    self._WriteConfig()
308 a8083063 Iustin Pop
    return port
309 a8083063 Iustin Pop
310 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
311 a8083063 Iustin Pop
  def GetHostKey(self):
312 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
313 a8083063 Iustin Pop

314 a8083063 Iustin Pop
    Args: None
315 a8083063 Iustin Pop

316 a8083063 Iustin Pop
    Returns: rsa hostkey
317 a8083063 Iustin Pop
    """
318 a8083063 Iustin Pop
    self._OpenConfig()
319 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
320 a8083063 Iustin Pop
321 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
322 a8083063 Iustin Pop
  def AddInstance(self, instance):
323 a8083063 Iustin Pop
    """Add an instance to the config.
324 a8083063 Iustin Pop

325 a8083063 Iustin Pop
    This should be used after creating a new instance.
326 a8083063 Iustin Pop

327 a8083063 Iustin Pop
    Args:
328 a8083063 Iustin Pop
      instance: the instance object
329 a8083063 Iustin Pop
    """
330 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
331 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
332 a8083063 Iustin Pop
333 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
334 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
335 e00fb268 Iustin Pop
      logger.Info("Instance '%s' DISK_LAYOUT: %s" % (instance.name, all_lvs))
336 923b1523 Iustin Pop
337 a8083063 Iustin Pop
    self._OpenConfig()
338 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
339 a8083063 Iustin Pop
    self._WriteConfig()
340 a8083063 Iustin Pop
341 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
342 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
343 a8083063 Iustin Pop

344 a8083063 Iustin Pop
    """
345 6a408fb2 Iustin Pop
    if status not in ("up", "down"):
346 6a408fb2 Iustin Pop
      raise errors.ProgrammerError("Invalid status '%s' passed to"
347 6a408fb2 Iustin Pop
                                   " ConfigWriter._SetInstanceStatus()" %
348 6a408fb2 Iustin Pop
                                   status)
349 a8083063 Iustin Pop
    self._OpenConfig()
350 a8083063 Iustin Pop
351 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
352 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
353 3ecf6786 Iustin Pop
                                      instance_name)
354 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
355 455a3445 Iustin Pop
    if instance.status != status:
356 455a3445 Iustin Pop
      instance.status = status
357 455a3445 Iustin Pop
      self._WriteConfig()
358 a8083063 Iustin Pop
359 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
360 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
361 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
362 6a408fb2 Iustin Pop

363 6a408fb2 Iustin Pop
    """
364 6a408fb2 Iustin Pop
    self._SetInstanceStatus(instance_name, "up")
365 6a408fb2 Iustin Pop
366 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
367 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
368 a8083063 Iustin Pop
    """Remove the instance from the configuration.
369 a8083063 Iustin Pop

370 a8083063 Iustin Pop
    """
371 a8083063 Iustin Pop
    self._OpenConfig()
372 a8083063 Iustin Pop
373 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
374 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
375 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
376 a8083063 Iustin Pop
    self._WriteConfig()
377 a8083063 Iustin Pop
378 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
379 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
380 fc95f88f Iustin Pop
    """Rename an instance.
381 fc95f88f Iustin Pop

382 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
383 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
384 fc95f88f Iustin Pop
    rename.
385 fc95f88f Iustin Pop

386 fc95f88f Iustin Pop
    """
387 fc95f88f Iustin Pop
    self._OpenConfig()
388 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
389 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
390 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
391 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
392 fc95f88f Iustin Pop
    inst.name = new_name
393 b23c4333 Manuel Franceschini
394 b23c4333 Manuel Franceschini
    for disk in inst.disks:
395 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
396 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
397 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
398 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
399 b23c4333 Manuel Franceschini
                                              os.path.join(file_storage_dir,
400 b23c4333 Manuel Franceschini
                                                           inst.name,
401 b23c4333 Manuel Franceschini
                                                           disk.iv_name))
402 b23c4333 Manuel Franceschini
403 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
404 fc95f88f Iustin Pop
    self._WriteConfig()
405 fc95f88f Iustin Pop
406 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
407 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
408 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
409 a8083063 Iustin Pop

410 a8083063 Iustin Pop
    """
411 6a408fb2 Iustin Pop
    self._SetInstanceStatus(instance_name, "down")
412 a8083063 Iustin Pop
413 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
414 a8083063 Iustin Pop
  def GetInstanceList(self):
415 a8083063 Iustin Pop
    """Get the list of instances.
416 a8083063 Iustin Pop

417 a8083063 Iustin Pop
    Returns:
418 a8083063 Iustin Pop
      array of instances, ex. ['instance2.example.com','instance1.example.com']
419 a8083063 Iustin Pop
      these contains all the instances, also the ones in Admin_down state
420 a8083063 Iustin Pop

421 a8083063 Iustin Pop
    """
422 a8083063 Iustin Pop
    self._OpenConfig()
423 a8083063 Iustin Pop
424 a8083063 Iustin Pop
    return self._config_data.instances.keys()
425 a8083063 Iustin Pop
426 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
427 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
428 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
429 a8083063 Iustin Pop

430 a8083063 Iustin Pop
    """
431 a8083063 Iustin Pop
    self._OpenConfig()
432 a8083063 Iustin Pop
433 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
434 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
435 a8083063 Iustin Pop
436 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
437 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
438 a8083063 Iustin Pop
    """Returns informations about an instance.
439 a8083063 Iustin Pop

440 a8083063 Iustin Pop
    It takes the information from the configuration file. Other informations of
441 a8083063 Iustin Pop
    an instance are taken from the live systems.
442 a8083063 Iustin Pop

443 a8083063 Iustin Pop
    Args:
444 a8083063 Iustin Pop
      instance: name of the instance, ex instance1.example.com
445 a8083063 Iustin Pop

446 a8083063 Iustin Pop
    Returns:
447 a8083063 Iustin Pop
      the instance object
448 a8083063 Iustin Pop

449 a8083063 Iustin Pop
    """
450 a8083063 Iustin Pop
    self._OpenConfig()
451 a8083063 Iustin Pop
452 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
453 a8083063 Iustin Pop
      return None
454 a8083063 Iustin Pop
455 a8083063 Iustin Pop
    return self._config_data.instances[instance_name]
456 a8083063 Iustin Pop
457 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
458 a8083063 Iustin Pop
  def AddNode(self, node):
459 a8083063 Iustin Pop
    """Add a node to the configuration.
460 a8083063 Iustin Pop

461 a8083063 Iustin Pop
    Args:
462 a8083063 Iustin Pop
      node: an object.Node instance
463 a8083063 Iustin Pop

464 a8083063 Iustin Pop
    """
465 d8470559 Michael Hanselmann
    logging.info("Adding node %s to configuration" % node.name)
466 d8470559 Michael Hanselmann
467 a8083063 Iustin Pop
    self._OpenConfig()
468 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
469 a8083063 Iustin Pop
    self._WriteConfig()
470 a8083063 Iustin Pop
471 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
472 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
473 a8083063 Iustin Pop
    """Remove a node from the configuration.
474 a8083063 Iustin Pop

475 a8083063 Iustin Pop
    """
476 d8470559 Michael Hanselmann
    logging.info("Removing node %s from configuration" % node_name)
477 d8470559 Michael Hanselmann
478 a8083063 Iustin Pop
    self._OpenConfig()
479 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
480 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
481 a8083063 Iustin Pop
482 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
483 a8083063 Iustin Pop
    self._WriteConfig()
484 a8083063 Iustin Pop
485 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
486 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
487 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
488 a8083063 Iustin Pop

489 a8083063 Iustin Pop
    """
490 a8083063 Iustin Pop
    self._OpenConfig()
491 a8083063 Iustin Pop
492 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
493 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
494 a8083063 Iustin Pop
495 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
496 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
497 a8083063 Iustin Pop

498 f78ede4e Guido Trotter
    This function is for internal use, when the config lock is already held.
499 f78ede4e Guido Trotter

500 a8083063 Iustin Pop
    Args: node: nodename (tuple) of the node
501 a8083063 Iustin Pop

502 a8083063 Iustin Pop
    Returns: the node object
503 a8083063 Iustin Pop

504 a8083063 Iustin Pop
    """
505 a8083063 Iustin Pop
    self._OpenConfig()
506 a8083063 Iustin Pop
507 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
508 a8083063 Iustin Pop
      return None
509 a8083063 Iustin Pop
510 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
511 a8083063 Iustin Pop
512 f78ede4e Guido Trotter
513 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
514 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
515 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
516 f78ede4e Guido Trotter

517 f78ede4e Guido Trotter
    Args: node: nodename (tuple) of the node
518 f78ede4e Guido Trotter

519 f78ede4e Guido Trotter
    Returns: the node object
520 f78ede4e Guido Trotter

521 f78ede4e Guido Trotter
    """
522 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
523 f78ede4e Guido Trotter
524 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
525 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
526 a8083063 Iustin Pop

527 f78ede4e Guido Trotter
    This function is for internal use, when the config lock is already held.
528 f78ede4e Guido Trotter

529 a8083063 Iustin Pop
    """
530 a8083063 Iustin Pop
    self._OpenConfig()
531 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
532 a8083063 Iustin Pop
533 f78ede4e Guido Trotter
534 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
535 f78ede4e Guido Trotter
  def GetNodeList(self):
536 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
537 f78ede4e Guido Trotter

538 f78ede4e Guido Trotter
    """
539 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
540 f78ede4e Guido Trotter
541 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
542 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
543 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
544 d65e5776 Iustin Pop

545 d65e5776 Iustin Pop
    @rtype: dict
546 d65e5776 Iustin Pop
    @returns: dict of (node, node_info), where node_info is what
547 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
548 d65e5776 Iustin Pop

549 d65e5776 Iustin Pop
    """
550 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
551 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
552 d65e5776 Iustin Pop
    return my_dict
553 d65e5776 Iustin Pop
554 d65e5776 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
555 a8083063 Iustin Pop
  def DumpConfig(self):
556 a8083063 Iustin Pop
    """Return the entire configuration of the cluster.
557 a8083063 Iustin Pop
    """
558 a8083063 Iustin Pop
    self._OpenConfig()
559 a8083063 Iustin Pop
    return self._config_data
560 a8083063 Iustin Pop
561 a8083063 Iustin Pop
  def _BumpSerialNo(self):
562 a8083063 Iustin Pop
    """Bump up the serial number of the config.
563 a8083063 Iustin Pop

564 a8083063 Iustin Pop
    """
565 a8083063 Iustin Pop
    self._config_data.cluster.serial_no += 1
566 a8083063 Iustin Pop
567 a8083063 Iustin Pop
  def _OpenConfig(self):
568 a8083063 Iustin Pop
    """Read the config data from disk.
569 a8083063 Iustin Pop

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

574 a8083063 Iustin Pop
    """
575 a8083063 Iustin Pop
    try:
576 a8083063 Iustin Pop
      st = os.stat(self._cfg_file)
577 a8083063 Iustin Pop
    except OSError, err:
578 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't stat config file: %s" % err)
579 a8083063 Iustin Pop
    if (self._config_data is not None and
580 a8083063 Iustin Pop
        self._config_time is not None and
581 264bb3c5 Michael Hanselmann
        self._config_time == st.st_mtime and
582 264bb3c5 Michael Hanselmann
        self._config_size == st.st_size and
583 264bb3c5 Michael Hanselmann
        self._config_inode == st.st_ino):
584 a8083063 Iustin Pop
      # data is current, so skip loading of config file
585 a8083063 Iustin Pop
      return
586 243cdbcc Michael Hanselmann
587 243cdbcc Michael Hanselmann
    # Make sure the configuration has the right version
588 243cdbcc Michael Hanselmann
    ValidateConfig()
589 243cdbcc Michael Hanselmann
590 a8083063 Iustin Pop
    f = open(self._cfg_file, 'r')
591 a8083063 Iustin Pop
    try:
592 a8083063 Iustin Pop
      try:
593 8d14b30d Iustin Pop
        data = objects.ConfigData.FromDict(serializer.Load(f.read()))
594 a8083063 Iustin Pop
      except Exception, err:
595 3ecf6786 Iustin Pop
        raise errors.ConfigurationError(err)
596 a8083063 Iustin Pop
    finally:
597 a8083063 Iustin Pop
      f.close()
598 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
599 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
600 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
601 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
602 a8083063 Iustin Pop
    self._config_data = data
603 a8083063 Iustin Pop
    self._config_time = st.st_mtime
604 264bb3c5 Michael Hanselmann
    self._config_size = st.st_size
605 264bb3c5 Michael Hanselmann
    self._config_inode = st.st_ino
606 a8083063 Iustin Pop
607 a8083063 Iustin Pop
  def _DistributeConfig(self):
608 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
609 a8083063 Iustin Pop

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

613 a8083063 Iustin Pop
    """
614 a8083063 Iustin Pop
    if self._offline:
615 a8083063 Iustin Pop
      return True
616 a8083063 Iustin Pop
    bad = False
617 f78ede4e Guido Trotter
    nodelist = self._UnlockedGetNodeList()
618 89e1fc26 Iustin Pop
    myhostname = self._my_hostname
619 a8083063 Iustin Pop
620 9ff994da Guido Trotter
    try:
621 9ff994da Guido Trotter
      nodelist.remove(myhostname)
622 9ff994da Guido Trotter
    except ValueError:
623 9ff994da Guido Trotter
      pass
624 a8083063 Iustin Pop
625 41362e70 Guido Trotter
    result = rpc.call_upload_file(nodelist, self._cfg_file)
626 41362e70 Guido Trotter
    for node in nodelist:
627 a8083063 Iustin Pop
      if not result[node]:
628 a8083063 Iustin Pop
        logger.Error("copy of file %s to node %s failed" %
629 a8083063 Iustin Pop
                     (self._cfg_file, node))
630 a8083063 Iustin Pop
        bad = True
631 a8083063 Iustin Pop
    return not bad
632 a8083063 Iustin Pop
633 a8083063 Iustin Pop
  def _WriteConfig(self, destination=None):
634 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
635 a8083063 Iustin Pop

636 a8083063 Iustin Pop
    """
637 a8083063 Iustin Pop
    if destination is None:
638 a8083063 Iustin Pop
      destination = self._cfg_file
639 a8083063 Iustin Pop
    self._BumpSerialNo()
640 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
641 a8083063 Iustin Pop
    dir_name, file_name = os.path.split(destination)
642 a8083063 Iustin Pop
    fd, name = tempfile.mkstemp('.newconfig', file_name, dir_name)
643 a8083063 Iustin Pop
    f = os.fdopen(fd, 'w')
644 a8083063 Iustin Pop
    try:
645 8d14b30d Iustin Pop
      f.write(txt)
646 a8083063 Iustin Pop
      os.fsync(f.fileno())
647 a8083063 Iustin Pop
    finally:
648 a8083063 Iustin Pop
      f.close()
649 a8083063 Iustin Pop
    # we don't need to do os.close(fd) as f.close() did it
650 a8083063 Iustin Pop
    os.rename(name, destination)
651 14e15659 Iustin Pop
    self.write_count += 1
652 fee9556c Iustin Pop
    # re-set our cache as not to re-read the config file
653 fee9556c Iustin Pop
    try:
654 fee9556c Iustin Pop
      st = os.stat(destination)
655 fee9556c Iustin Pop
    except OSError, err:
656 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Can't stat config file: %s" % err)
657 fee9556c Iustin Pop
    self._config_time = st.st_mtime
658 fee9556c Iustin Pop
    self._config_size = st.st_size
659 fee9556c Iustin Pop
    self._config_inode = st.st_ino
660 fee9556c Iustin Pop
    # and redistribute the config file
661 a8083063 Iustin Pop
    self._DistributeConfig()
662 a8083063 Iustin Pop
663 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
664 a8083063 Iustin Pop
  def InitConfig(self, node, primary_ip, secondary_ip,
665 5fcdc80d Iustin Pop
                 hostkeypub, mac_prefix, vg_name, def_bridge):
666 a8083063 Iustin Pop
    """Create the initial cluster configuration.
667 a8083063 Iustin Pop

668 a8083063 Iustin Pop
    It will contain the current node, which will also be the master
669 a8083063 Iustin Pop
    node, and no instances or operating systmes.
670 a8083063 Iustin Pop

671 a8083063 Iustin Pop
    Args:
672 a8083063 Iustin Pop
      node: the nodename of the initial node
673 a8083063 Iustin Pop
      primary_ip: the IP address of the current host
674 a8083063 Iustin Pop
      secondary_ip: the secondary IP of the current host or None
675 a8083063 Iustin Pop
      hostkeypub: the public hostkey of this host
676 a8083063 Iustin Pop

677 264bb3c5 Michael Hanselmann
    """
678 a8083063 Iustin Pop
    hu_port = constants.FIRST_DRBD_PORT - 1
679 243cdbcc Michael Hanselmann
    globalconfig = objects.Cluster(serial_no=1,
680 a8083063 Iustin Pop
                                   rsahostkeypub=hostkeypub,
681 a8083063 Iustin Pop
                                   highest_used_port=hu_port,
682 a8083063 Iustin Pop
                                   mac_prefix=mac_prefix,
683 a8083063 Iustin Pop
                                   volume_group_name=vg_name,
684 b2fddf63 Iustin Pop
                                   default_bridge=def_bridge,
685 b2fddf63 Iustin Pop
                                   tcpudp_port_pool=set())
686 a8083063 Iustin Pop
    if secondary_ip is None:
687 a8083063 Iustin Pop
      secondary_ip = primary_ip
688 a8083063 Iustin Pop
    nodeconfig = objects.Node(name=node, primary_ip=primary_ip,
689 a8083063 Iustin Pop
                              secondary_ip=secondary_ip)
690 a8083063 Iustin Pop
691 a8083063 Iustin Pop
    self._config_data = objects.ConfigData(nodes={node: nodeconfig},
692 a8083063 Iustin Pop
                                           instances={},
693 b2fddf63 Iustin Pop
                                           cluster=globalconfig)
694 a8083063 Iustin Pop
    self._WriteConfig()
695 a8083063 Iustin Pop
696 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
697 a8083063 Iustin Pop
  def GetVGName(self):
698 a8083063 Iustin Pop
    """Return the volume group name.
699 a8083063 Iustin Pop

700 a8083063 Iustin Pop
    """
701 a8083063 Iustin Pop
    self._OpenConfig()
702 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
703 a8083063 Iustin Pop
704 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
705 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
706 89ff8e15 Manuel Franceschini
    """Set the volume group name.
707 89ff8e15 Manuel Franceschini

708 89ff8e15 Manuel Franceschini
    """
709 89ff8e15 Manuel Franceschini
    self._OpenConfig()
710 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
711 89ff8e15 Manuel Franceschini
    self._WriteConfig()
712 89ff8e15 Manuel Franceschini
713 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
714 a8083063 Iustin Pop
  def GetDefBridge(self):
715 a8083063 Iustin Pop
    """Return the default bridge.
716 a8083063 Iustin Pop

717 a8083063 Iustin Pop
    """
718 a8083063 Iustin Pop
    self._OpenConfig()
719 a8083063 Iustin Pop
    return self._config_data.cluster.default_bridge
720 a8083063 Iustin Pop
721 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
722 a8083063 Iustin Pop
  def GetMACPrefix(self):
723 a8083063 Iustin Pop
    """Return the mac prefix.
724 a8083063 Iustin Pop

725 a8083063 Iustin Pop
    """
726 a8083063 Iustin Pop
    self._OpenConfig()
727 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
728 62779dd0 Iustin Pop
729 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
730 62779dd0 Iustin Pop
  def GetClusterInfo(self):
731 62779dd0 Iustin Pop
    """Returns informations about the cluster
732 62779dd0 Iustin Pop

733 62779dd0 Iustin Pop
    Returns:
734 62779dd0 Iustin Pop
      the cluster object
735 62779dd0 Iustin Pop

736 62779dd0 Iustin Pop
    """
737 62779dd0 Iustin Pop
    self._OpenConfig()
738 62779dd0 Iustin Pop
739 62779dd0 Iustin Pop
    return self._config_data.cluster
740 e00fb268 Iustin Pop
741 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
742 e00fb268 Iustin Pop
  def Update(self, target):
743 e00fb268 Iustin Pop
    """Notify function to be called after updates.
744 e00fb268 Iustin Pop

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

751 e00fb268 Iustin Pop
    """
752 e00fb268 Iustin Pop
    if self._config_data is None:
753 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
754 3ecf6786 Iustin Pop
                                   " cannot save.")
755 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
756 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
757 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
758 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
759 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
760 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
761 e00fb268 Iustin Pop
    else:
762 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
763 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
764 e00fb268 Iustin Pop
    if not test:
765 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
766 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
767 e00fb268 Iustin Pop
    self._WriteConfig()