Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ a8efbb40

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

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

86 a8083063 Iustin Pop
    """
87 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
88 a8083063 Iustin Pop
89 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
90 a8083063 Iustin Pop
  def GenerateMAC(self):
91 a8083063 Iustin Pop
    """Generate a MAC for an instance.
92 a8083063 Iustin Pop

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

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

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

118 1862d460 Alexander Schreiber
    """
119 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
120 1862d460 Alexander Schreiber
    return mac in all_macs
121 1862d460 Alexander Schreiber
122 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
123 f9518d38 Iustin Pop
  def GenerateDRBDSecret(self):
124 f9518d38 Iustin Pop
    """Generate a DRBD secret.
125 f9518d38 Iustin Pop

126 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
127 f9518d38 Iustin Pop

128 f9518d38 Iustin Pop
    """
129 f9518d38 Iustin Pop
    all_secrets = self._AllDRBDSecrets()
130 f9518d38 Iustin Pop
    retries = 64
131 f9518d38 Iustin Pop
    while retries > 0:
132 f9518d38 Iustin Pop
      secret = utils.GenerateSecret()
133 f9518d38 Iustin Pop
      if secret not in all_secrets:
134 f9518d38 Iustin Pop
        break
135 f9518d38 Iustin Pop
      retries -= 1
136 f9518d38 Iustin Pop
    else:
137 f9518d38 Iustin Pop
      raise errors.ConfigurationError("Can't generate unique DRBD secret")
138 f9518d38 Iustin Pop
    return secret
139 f9518d38 Iustin Pop
140 923b1523 Iustin Pop
  def _ComputeAllLVs(self):
141 923b1523 Iustin Pop
    """Compute the list of all LVs.
142 923b1523 Iustin Pop

143 923b1523 Iustin Pop
    """
144 923b1523 Iustin Pop
    lvnames = set()
145 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
146 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
147 923b1523 Iustin Pop
      for lv_list in node_data.values():
148 923b1523 Iustin Pop
        lvnames.update(lv_list)
149 923b1523 Iustin Pop
    return lvnames
150 923b1523 Iustin Pop
151 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
152 923b1523 Iustin Pop
  def GenerateUniqueID(self, exceptions=None):
153 923b1523 Iustin Pop
    """Generate an unique disk name.
154 923b1523 Iustin Pop

155 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
156 923b1523 Iustin Pop
    duplicates.
157 923b1523 Iustin Pop

158 923b1523 Iustin Pop
    Args:
159 923b1523 Iustin Pop
      - exceptions: a list with some other names which should be checked
160 923b1523 Iustin Pop
                    for uniqueness (used for example when you want to get
161 923b1523 Iustin Pop
                    more than one id at one time without adding each one in
162 923b1523 Iustin Pop
                    turn to the config file
163 923b1523 Iustin Pop

164 923b1523 Iustin Pop
    Returns: the unique id as a string
165 923b1523 Iustin Pop

166 923b1523 Iustin Pop
    """
167 923b1523 Iustin Pop
    existing = set()
168 923b1523 Iustin Pop
    existing.update(self._temporary_ids)
169 923b1523 Iustin Pop
    existing.update(self._ComputeAllLVs())
170 923b1523 Iustin Pop
    existing.update(self._config_data.instances.keys())
171 923b1523 Iustin Pop
    existing.update(self._config_data.nodes.keys())
172 923b1523 Iustin Pop
    if exceptions is not None:
173 923b1523 Iustin Pop
      existing.update(exceptions)
174 923b1523 Iustin Pop
    retries = 64
175 923b1523 Iustin Pop
    while retries > 0:
176 24818e8f Michael Hanselmann
      unique_id = utils.NewUUID()
177 923b1523 Iustin Pop
      if unique_id not in existing and unique_id is not None:
178 923b1523 Iustin Pop
        break
179 923b1523 Iustin Pop
    else:
180 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Not able generate an unique ID"
181 3ecf6786 Iustin Pop
                                      " (last tried ID: %s" % unique_id)
182 923b1523 Iustin Pop
    self._temporary_ids.add(unique_id)
183 923b1523 Iustin Pop
    return unique_id
184 923b1523 Iustin Pop
185 a8083063 Iustin Pop
  def _AllMACs(self):
186 a8083063 Iustin Pop
    """Return all MACs present in the config.
187 a8083063 Iustin Pop

188 a8083063 Iustin Pop
    """
189 a8083063 Iustin Pop
    result = []
190 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
191 a8083063 Iustin Pop
      for nic in instance.nics:
192 a8083063 Iustin Pop
        result.append(nic.mac)
193 a8083063 Iustin Pop
194 a8083063 Iustin Pop
    return result
195 a8083063 Iustin Pop
196 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
197 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
198 f9518d38 Iustin Pop

199 f9518d38 Iustin Pop
    """
200 f9518d38 Iustin Pop
    def helper(disk, result):
201 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
202 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
203 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
204 f9518d38 Iustin Pop
      if disk.children:
205 f9518d38 Iustin Pop
        for child in disk.children:
206 f9518d38 Iustin Pop
          helper(child, result)
207 f9518d38 Iustin Pop
208 f9518d38 Iustin Pop
    result = []
209 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
210 f9518d38 Iustin Pop
      for disk in instance.disks:
211 f9518d38 Iustin Pop
        helper(disk, result)
212 f9518d38 Iustin Pop
213 f9518d38 Iustin Pop
    return result
214 f9518d38 Iustin Pop
215 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
216 a8083063 Iustin Pop
  def VerifyConfig(self):
217 a8efbb40 Iustin Pop
    """Verify function.
218 a8efbb40 Iustin Pop

219 a8083063 Iustin Pop
    """
220 a8083063 Iustin Pop
    result = []
221 a8083063 Iustin Pop
    seen_macs = []
222 48ce9fd9 Iustin Pop
    ports = {}
223 a8083063 Iustin Pop
    data = self._config_data
224 a8083063 Iustin Pop
    for instance_name in data.instances:
225 a8083063 Iustin Pop
      instance = data.instances[instance_name]
226 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
227 8522ceeb Iustin Pop
        result.append("instance '%s' has invalid primary node '%s'" %
228 a8083063 Iustin Pop
                      (instance_name, instance.primary_node))
229 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
230 a8083063 Iustin Pop
        if snode not in data.nodes:
231 8522ceeb Iustin Pop
          result.append("instance '%s' has invalid secondary node '%s'" %
232 a8083063 Iustin Pop
                        (instance_name, snode))
233 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
234 a8083063 Iustin Pop
        if nic.mac in seen_macs:
235 8522ceeb Iustin Pop
          result.append("instance '%s' has NIC %d mac %s duplicate" %
236 a8083063 Iustin Pop
                        (instance_name, idx, nic.mac))
237 a8083063 Iustin Pop
        else:
238 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
239 48ce9fd9 Iustin Pop
240 48ce9fd9 Iustin Pop
      # gather the drbd ports for duplicate checks
241 48ce9fd9 Iustin Pop
      for dsk in instance.disks:
242 48ce9fd9 Iustin Pop
        if dsk.dev_type in constants.LDS_DRBD:
243 48ce9fd9 Iustin Pop
          tcp_port = dsk.logical_id[2]
244 48ce9fd9 Iustin Pop
          if tcp_port not in ports:
245 48ce9fd9 Iustin Pop
            ports[tcp_port] = []
246 48ce9fd9 Iustin Pop
          ports[tcp_port].append((instance.name, "drbd disk %s" % dsk.iv_name))
247 48ce9fd9 Iustin Pop
      # gather network port reservation
248 48ce9fd9 Iustin Pop
      net_port = getattr(instance, "network_port", None)
249 48ce9fd9 Iustin Pop
      if net_port is not None:
250 48ce9fd9 Iustin Pop
        if net_port not in ports:
251 48ce9fd9 Iustin Pop
          ports[net_port] = []
252 48ce9fd9 Iustin Pop
        ports[net_port].append((instance.name, "network port"))
253 48ce9fd9 Iustin Pop
254 48ce9fd9 Iustin Pop
    # cluster-wide pool of free ports
255 a8efbb40 Iustin Pop
    for free_port in data.cluster.tcpudp_port_pool:
256 48ce9fd9 Iustin Pop
      if free_port not in ports:
257 48ce9fd9 Iustin Pop
        ports[free_port] = []
258 48ce9fd9 Iustin Pop
      ports[free_port].append(("cluster", "port marked as free"))
259 48ce9fd9 Iustin Pop
260 48ce9fd9 Iustin Pop
    # compute tcp/udp duplicate ports
261 48ce9fd9 Iustin Pop
    keys = ports.keys()
262 48ce9fd9 Iustin Pop
    keys.sort()
263 48ce9fd9 Iustin Pop
    for pnum in keys:
264 48ce9fd9 Iustin Pop
      pdata = ports[pnum]
265 48ce9fd9 Iustin Pop
      if len(pdata) > 1:
266 48ce9fd9 Iustin Pop
        txt = ", ".join(["%s/%s" % val for val in pdata])
267 48ce9fd9 Iustin Pop
        result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
268 48ce9fd9 Iustin Pop
269 48ce9fd9 Iustin Pop
    # highest used tcp port check
270 48ce9fd9 Iustin Pop
    if keys:
271 a8efbb40 Iustin Pop
      if keys[-1] > data.cluster.highest_used_port:
272 48ce9fd9 Iustin Pop
        result.append("Highest used port mismatch, saved %s, computed %s" %
273 a8efbb40 Iustin Pop
                      (data.cluster.highest_used_port, keys[-1]))
274 a8efbb40 Iustin Pop
275 a8efbb40 Iustin Pop
    cp_size = data.cluster.candidate_pool_size
276 a8efbb40 Iustin Pop
    num_c = 0
277 a8efbb40 Iustin Pop
    for node in data.nodes.values():
278 a8efbb40 Iustin Pop
      if node.master_candidate:
279 a8efbb40 Iustin Pop
        num_c += 1
280 a8efbb40 Iustin Pop
    if cp_size > num_c:
281 a8efbb40 Iustin Pop
      result.append("Not enough master candidates: actual %d, desired %d" %
282 a8efbb40 Iustin Pop
                    (num_c, cp_size))
283 48ce9fd9 Iustin Pop
284 a8083063 Iustin Pop
    return result
285 a8083063 Iustin Pop
286 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
287 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
288 a8083063 Iustin Pop

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

291 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
292 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
293 a8083063 Iustin Pop
    node.
294 a8083063 Iustin Pop

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

297 a8083063 Iustin Pop
    """
298 a8083063 Iustin Pop
    if disk.children:
299 a8083063 Iustin Pop
      for child in disk.children:
300 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
301 a8083063 Iustin Pop
302 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
303 a8083063 Iustin Pop
      return
304 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
305 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
306 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
307 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
308 3ecf6786 Iustin Pop
                                        node_name)
309 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
310 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
311 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
312 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
313 a8083063 Iustin Pop
                                        " for %s" % str(disk))
314 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
315 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
316 a8083063 Iustin Pop
      if pnode == node_name:
317 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
318 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
319 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
320 a8083063 Iustin Pop
    else:
321 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
322 a8083063 Iustin Pop
    return
323 a8083063 Iustin Pop
324 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
325 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
326 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
327 f78ede4e Guido Trotter

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

330 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
331 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
332 f78ede4e Guido Trotter
    node.
333 f78ede4e Guido Trotter

334 f78ede4e Guido Trotter
    """
335 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
336 f78ede4e Guido Trotter
337 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
338 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
339 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
340 b2fddf63 Iustin Pop

341 b2fddf63 Iustin Pop
    """
342 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
343 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
344 264bb3c5 Michael Hanselmann
345 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
346 264bb3c5 Michael Hanselmann
    self._WriteConfig()
347 264bb3c5 Michael Hanselmann
348 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
349 b2fddf63 Iustin Pop
  def GetPortList(self):
350 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
351 264bb3c5 Michael Hanselmann

352 264bb3c5 Michael Hanselmann
    """
353 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
354 264bb3c5 Michael Hanselmann
355 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
356 a8083063 Iustin Pop
  def AllocatePort(self):
357 a8083063 Iustin Pop
    """Allocate a port.
358 a8083063 Iustin Pop

359 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
360 b2fddf63 Iustin Pop
    default port range (and in this case we increase
361 b2fddf63 Iustin Pop
    highest_used_port).
362 a8083063 Iustin Pop

363 a8083063 Iustin Pop
    """
364 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
365 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
366 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
367 264bb3c5 Michael Hanselmann
    else:
368 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
369 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
370 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
371 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
372 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
373 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
374 a8083063 Iustin Pop
375 a8083063 Iustin Pop
    self._WriteConfig()
376 a8083063 Iustin Pop
    return port
377 a8083063 Iustin Pop
378 a81c53c9 Iustin Pop
  def _ComputeDRBDMap(self, instance):
379 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
380 a81c53c9 Iustin Pop

381 a81c53c9 Iustin Pop
    Return: dictionary of node_name: dict of minor: instance_name. The
382 a81c53c9 Iustin Pop
    returned dict will have all the nodes in it (even if with an empty
383 a81c53c9 Iustin Pop
    list).
384 a81c53c9 Iustin Pop

385 a81c53c9 Iustin Pop
    """
386 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
387 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
388 f9518d38 Iustin Pop
        nodeA, nodeB, dummy, minorA, minorB = disk.logical_id[:5]
389 a81c53c9 Iustin Pop
        for node, port in ((nodeA, minorA), (nodeB, minorB)):
390 a81c53c9 Iustin Pop
          assert node in used, "Instance node not found in node list"
391 a81c53c9 Iustin Pop
          if port in used[node]:
392 a81c53c9 Iustin Pop
            raise errors.ProgrammerError("DRBD minor already used:"
393 a81c53c9 Iustin Pop
                                         " %s/%s, %s/%s" %
394 a81c53c9 Iustin Pop
                                         (node, port, instance_name,
395 a81c53c9 Iustin Pop
                                          used[node][port]))
396 a81c53c9 Iustin Pop
397 a81c53c9 Iustin Pop
          used[node][port] = instance_name
398 a81c53c9 Iustin Pop
      if disk.children:
399 a81c53c9 Iustin Pop
        for child in disk.children:
400 a81c53c9 Iustin Pop
          _AppendUsedPorts(instance_name, child, used)
401 a81c53c9 Iustin Pop
402 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
403 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
404 a81c53c9 Iustin Pop
      my_dict[node][minor] = instance
405 a81c53c9 Iustin Pop
    for instance in self._config_data.instances.itervalues():
406 a81c53c9 Iustin Pop
      for disk in instance.disks:
407 a81c53c9 Iustin Pop
        _AppendUsedPorts(instance.name, disk, my_dict)
408 a81c53c9 Iustin Pop
    return my_dict
409 a81c53c9 Iustin Pop
410 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
411 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
412 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
413 a81c53c9 Iustin Pop

414 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
415 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
416 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
417 a81c53c9 Iustin Pop
    order as the passed nodes.
418 a81c53c9 Iustin Pop

419 a81c53c9 Iustin Pop
    """
420 a81c53c9 Iustin Pop
    d_map = self._ComputeDRBDMap(instance)
421 a81c53c9 Iustin Pop
    result = []
422 a81c53c9 Iustin Pop
    for nname in nodes:
423 a81c53c9 Iustin Pop
      ndata = d_map[nname]
424 a81c53c9 Iustin Pop
      if not ndata:
425 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
426 a81c53c9 Iustin Pop
        result.append(0)
427 a81c53c9 Iustin Pop
        ndata[0] = instance
428 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
429 a81c53c9 Iustin Pop
        continue
430 a81c53c9 Iustin Pop
      keys = ndata.keys()
431 a81c53c9 Iustin Pop
      keys.sort()
432 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
433 a81c53c9 Iustin Pop
      if ffree is None:
434 a81c53c9 Iustin Pop
        # return the next minor
435 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
436 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
437 a81c53c9 Iustin Pop
      else:
438 a81c53c9 Iustin Pop
        minor = ffree
439 a81c53c9 Iustin Pop
      result.append(minor)
440 a81c53c9 Iustin Pop
      ndata[minor] = instance
441 a81c53c9 Iustin Pop
      assert (nname, minor) not in self._temporary_drbds, \
442 a81c53c9 Iustin Pop
             "Attempt to reuse reserved DRBD minor"
443 a81c53c9 Iustin Pop
      self._temporary_drbds[(nname, minor)] = instance
444 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
445 a81c53c9 Iustin Pop
                  nodes, result)
446 a81c53c9 Iustin Pop
    return result
447 a81c53c9 Iustin Pop
448 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
449 a81c53c9 Iustin Pop
  def ReleaseDRBDMinors(self, instance):
450 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
451 a81c53c9 Iustin Pop

452 a81c53c9 Iustin Pop
    This should be called on both the error paths and on the success
453 a81c53c9 Iustin Pop
    paths (after the instance has been added or updated).
454 a81c53c9 Iustin Pop

455 a81c53c9 Iustin Pop
    @type instance: string
456 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
457 a81c53c9 Iustin Pop
                     released
458 a81c53c9 Iustin Pop

459 a81c53c9 Iustin Pop
    """
460 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
461 a81c53c9 Iustin Pop
      if name == instance:
462 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
463 a81c53c9 Iustin Pop
464 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
465 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
466 4a8b186a Michael Hanselmann
    """Get the configuration version.
467 4a8b186a Michael Hanselmann

468 4a8b186a Michael Hanselmann
    @return: Config version
469 4a8b186a Michael Hanselmann

470 4a8b186a Michael Hanselmann
    """
471 4a8b186a Michael Hanselmann
    return self._config_data.version
472 4a8b186a Michael Hanselmann
473 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
474 4a8b186a Michael Hanselmann
  def GetClusterName(self):
475 4a8b186a Michael Hanselmann
    """Get cluster name.
476 4a8b186a Michael Hanselmann

477 4a8b186a Michael Hanselmann
    @return: Cluster name
478 4a8b186a Michael Hanselmann

479 4a8b186a Michael Hanselmann
    """
480 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
481 4a8b186a Michael Hanselmann
482 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
483 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
484 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
485 4a8b186a Michael Hanselmann

486 4a8b186a Michael Hanselmann
    @return: Master hostname
487 4a8b186a Michael Hanselmann

488 4a8b186a Michael Hanselmann
    """
489 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
490 4a8b186a Michael Hanselmann
491 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
492 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
493 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
494 4a8b186a Michael Hanselmann

495 4a8b186a Michael Hanselmann
    @return: Master IP
496 4a8b186a Michael Hanselmann

497 4a8b186a Michael Hanselmann
    """
498 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
499 4a8b186a Michael Hanselmann
500 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
501 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
502 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
503 4a8b186a Michael Hanselmann

504 4a8b186a Michael Hanselmann
    """
505 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
506 4a8b186a Michael Hanselmann
507 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
508 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
509 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
510 4a8b186a Michael Hanselmann

511 4a8b186a Michael Hanselmann
    """
512 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
513 4a8b186a Michael Hanselmann
514 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
515 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
516 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
517 4a8b186a Michael Hanselmann

518 4a8b186a Michael Hanselmann
    """
519 64272529 Iustin Pop
    return self._config_data.cluster.default_hypervisor
520 4a8b186a Michael Hanselmann
521 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
522 a8083063 Iustin Pop
  def GetHostKey(self):
523 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
524 a8083063 Iustin Pop

525 a8083063 Iustin Pop
    Args: None
526 a8083063 Iustin Pop

527 a8083063 Iustin Pop
    Returns: rsa hostkey
528 a8083063 Iustin Pop
    """
529 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
530 a8083063 Iustin Pop
531 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
532 a8083063 Iustin Pop
  def AddInstance(self, instance):
533 a8083063 Iustin Pop
    """Add an instance to the config.
534 a8083063 Iustin Pop

535 a8083063 Iustin Pop
    This should be used after creating a new instance.
536 a8083063 Iustin Pop

537 a8083063 Iustin Pop
    Args:
538 a8083063 Iustin Pop
      instance: the instance object
539 a8083063 Iustin Pop
    """
540 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
541 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
542 a8083063 Iustin Pop
543 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
544 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
545 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
546 923b1523 Iustin Pop
547 b989e85d Iustin Pop
    instance.serial_no = 1
548 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
549 a8083063 Iustin Pop
    self._WriteConfig()
550 a8083063 Iustin Pop
551 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
552 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
553 a8083063 Iustin Pop

554 a8083063 Iustin Pop
    """
555 6a408fb2 Iustin Pop
    if status not in ("up", "down"):
556 6a408fb2 Iustin Pop
      raise errors.ProgrammerError("Invalid status '%s' passed to"
557 6a408fb2 Iustin Pop
                                   " ConfigWriter._SetInstanceStatus()" %
558 6a408fb2 Iustin Pop
                                   status)
559 a8083063 Iustin Pop
560 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
561 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
562 3ecf6786 Iustin Pop
                                      instance_name)
563 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
564 455a3445 Iustin Pop
    if instance.status != status:
565 455a3445 Iustin Pop
      instance.status = status
566 b989e85d Iustin Pop
      instance.serial_no += 1
567 455a3445 Iustin Pop
      self._WriteConfig()
568 a8083063 Iustin Pop
569 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
570 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
571 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
572 6a408fb2 Iustin Pop

573 6a408fb2 Iustin Pop
    """
574 6a408fb2 Iustin Pop
    self._SetInstanceStatus(instance_name, "up")
575 6a408fb2 Iustin Pop
576 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
577 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
578 a8083063 Iustin Pop
    """Remove the instance from the configuration.
579 a8083063 Iustin Pop

580 a8083063 Iustin Pop
    """
581 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
582 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
583 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
584 a8083063 Iustin Pop
    self._WriteConfig()
585 a8083063 Iustin Pop
586 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
587 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
588 fc95f88f Iustin Pop
    """Rename an instance.
589 fc95f88f Iustin Pop

590 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
591 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
592 fc95f88f Iustin Pop
    rename.
593 fc95f88f Iustin Pop

594 fc95f88f Iustin Pop
    """
595 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
596 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
597 fc95f88f Iustin Pop
    inst = self._config_data.instances[old_name]
598 fc95f88f Iustin Pop
    del self._config_data.instances[old_name]
599 fc95f88f Iustin Pop
    inst.name = new_name
600 b23c4333 Manuel Franceschini
601 b23c4333 Manuel Franceschini
    for disk in inst.disks:
602 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
603 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
604 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
605 b23c4333 Manuel Franceschini
        disk.physical_id = disk.logical_id = (disk.logical_id[0],
606 b23c4333 Manuel Franceschini
                                              os.path.join(file_storage_dir,
607 b23c4333 Manuel Franceschini
                                                           inst.name,
608 b23c4333 Manuel Franceschini
                                                           disk.iv_name))
609 b23c4333 Manuel Franceschini
610 fc95f88f Iustin Pop
    self._config_data.instances[inst.name] = inst
611 fc95f88f Iustin Pop
    self._WriteConfig()
612 fc95f88f Iustin Pop
613 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
614 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
615 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
616 a8083063 Iustin Pop

617 a8083063 Iustin Pop
    """
618 6a408fb2 Iustin Pop
    self._SetInstanceStatus(instance_name, "down")
619 a8083063 Iustin Pop
620 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
621 94bbfece Iustin Pop
    """Get the list of instances.
622 94bbfece Iustin Pop

623 94bbfece Iustin Pop
    This function is for internal use, when the config lock is already held.
624 94bbfece Iustin Pop

625 94bbfece Iustin Pop
    """
626 94bbfece Iustin Pop
    return self._config_data.instances.keys()
627 94bbfece Iustin Pop
628 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
629 a8083063 Iustin Pop
  def GetInstanceList(self):
630 a8083063 Iustin Pop
    """Get the list of instances.
631 a8083063 Iustin Pop

632 a8083063 Iustin Pop
    Returns:
633 a8083063 Iustin Pop
      array of instances, ex. ['instance2.example.com','instance1.example.com']
634 a8083063 Iustin Pop
      these contains all the instances, also the ones in Admin_down state
635 a8083063 Iustin Pop

636 a8083063 Iustin Pop
    """
637 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
638 a8083063 Iustin Pop
639 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
640 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
641 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
642 a8083063 Iustin Pop

643 a8083063 Iustin Pop
    """
644 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
645 a8083063 Iustin Pop
                                    self._config_data.instances.keys())
646 a8083063 Iustin Pop
647 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
648 94bbfece Iustin Pop
    """Returns informations about an instance.
649 94bbfece Iustin Pop

650 94bbfece Iustin Pop
    This function is for internal use, when the config lock is already held.
651 94bbfece Iustin Pop

652 94bbfece Iustin Pop
    """
653 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
654 94bbfece Iustin Pop
      return None
655 94bbfece Iustin Pop
656 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
657 94bbfece Iustin Pop
658 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
659 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
660 a8083063 Iustin Pop
    """Returns informations about an instance.
661 a8083063 Iustin Pop

662 a8083063 Iustin Pop
    It takes the information from the configuration file. Other informations of
663 a8083063 Iustin Pop
    an instance are taken from the live systems.
664 a8083063 Iustin Pop

665 a8083063 Iustin Pop
    Args:
666 a8083063 Iustin Pop
      instance: name of the instance, ex instance1.example.com
667 a8083063 Iustin Pop

668 a8083063 Iustin Pop
    Returns:
669 a8083063 Iustin Pop
      the instance object
670 a8083063 Iustin Pop

671 a8083063 Iustin Pop
    """
672 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
673 a8083063 Iustin Pop
674 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
675 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
676 0b2de758 Iustin Pop
    """Get the configuration of all instances.
677 0b2de758 Iustin Pop

678 0b2de758 Iustin Pop
    @rtype: dict
679 0b2de758 Iustin Pop
    @returns: dict of (instance, instance_info), where instance_info is what
680 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
681 0b2de758 Iustin Pop

682 0b2de758 Iustin Pop
    """
683 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
684 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
685 0b2de758 Iustin Pop
    return my_dict
686 0b2de758 Iustin Pop
687 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
688 a8083063 Iustin Pop
  def AddNode(self, node):
689 a8083063 Iustin Pop
    """Add a node to the configuration.
690 a8083063 Iustin Pop

691 a8083063 Iustin Pop
    Args:
692 a8083063 Iustin Pop
      node: an object.Node instance
693 a8083063 Iustin Pop

694 a8083063 Iustin Pop
    """
695 d8470559 Michael Hanselmann
    logging.info("Adding node %s to configuration" % node.name)
696 d8470559 Michael Hanselmann
697 b989e85d Iustin Pop
    node.serial_no = 1
698 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
699 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
700 a8083063 Iustin Pop
    self._WriteConfig()
701 a8083063 Iustin Pop
702 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
703 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
704 a8083063 Iustin Pop
    """Remove a node from the configuration.
705 a8083063 Iustin Pop

706 a8083063 Iustin Pop
    """
707 d8470559 Michael Hanselmann
    logging.info("Removing node %s from configuration" % node_name)
708 d8470559 Michael Hanselmann
709 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
710 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
711 a8083063 Iustin Pop
712 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
713 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
714 a8083063 Iustin Pop
    self._WriteConfig()
715 a8083063 Iustin Pop
716 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
717 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
718 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
719 a8083063 Iustin Pop

720 a8083063 Iustin Pop
    """
721 a8083063 Iustin Pop
    return utils.MatchNameComponent(short_name,
722 a8083063 Iustin Pop
                                    self._config_data.nodes.keys())
723 a8083063 Iustin Pop
724 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
725 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
726 a8083063 Iustin Pop

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

729 a8083063 Iustin Pop
    Args: node: nodename (tuple) of the node
730 a8083063 Iustin Pop

731 a8083063 Iustin Pop
    Returns: the node object
732 a8083063 Iustin Pop

733 a8083063 Iustin Pop
    """
734 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
735 a8083063 Iustin Pop
      return None
736 a8083063 Iustin Pop
737 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
738 a8083063 Iustin Pop
739 f78ede4e Guido Trotter
740 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
741 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
742 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
743 f78ede4e Guido Trotter

744 f78ede4e Guido Trotter
    Args: node: nodename (tuple) of the node
745 f78ede4e Guido Trotter

746 f78ede4e Guido Trotter
    Returns: the node object
747 f78ede4e Guido Trotter

748 f78ede4e Guido Trotter
    """
749 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
750 f78ede4e Guido Trotter
751 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
752 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
753 a8083063 Iustin Pop

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

756 a8083063 Iustin Pop
    """
757 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
758 a8083063 Iustin Pop
759 f78ede4e Guido Trotter
760 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
761 f78ede4e Guido Trotter
  def GetNodeList(self):
762 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
763 f78ede4e Guido Trotter

764 f78ede4e Guido Trotter
    """
765 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
766 f78ede4e Guido Trotter
767 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
768 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
769 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
770 d65e5776 Iustin Pop

771 d65e5776 Iustin Pop
    @rtype: dict
772 d65e5776 Iustin Pop
    @returns: dict of (node, node_info), where node_info is what
773 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
774 d65e5776 Iustin Pop

775 d65e5776 Iustin Pop
    """
776 d65e5776 Iustin Pop
    my_dict = dict([(node, self._UnlockedGetNodeInfo(node))
777 d65e5776 Iustin Pop
                    for node in self._UnlockedGetNodeList()])
778 d65e5776 Iustin Pop
    return my_dict
779 d65e5776 Iustin Pop
780 a8083063 Iustin Pop
  def _BumpSerialNo(self):
781 a8083063 Iustin Pop
    """Bump up the serial number of the config.
782 a8083063 Iustin Pop

783 a8083063 Iustin Pop
    """
784 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
785 a8083063 Iustin Pop
786 a8083063 Iustin Pop
  def _OpenConfig(self):
787 a8083063 Iustin Pop
    """Read the config data from disk.
788 a8083063 Iustin Pop

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

793 a8083063 Iustin Pop
    """
794 a8083063 Iustin Pop
    f = open(self._cfg_file, 'r')
795 a8083063 Iustin Pop
    try:
796 a8083063 Iustin Pop
      try:
797 8d14b30d Iustin Pop
        data = objects.ConfigData.FromDict(serializer.Load(f.read()))
798 a8083063 Iustin Pop
      except Exception, err:
799 3ecf6786 Iustin Pop
        raise errors.ConfigurationError(err)
800 a8083063 Iustin Pop
    finally:
801 a8083063 Iustin Pop
      f.close()
802 5b263ed7 Michael Hanselmann
803 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
804 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
805 5b263ed7 Michael Hanselmann
806 a8083063 Iustin Pop
    if (not hasattr(data, 'cluster') or
807 243cdbcc Michael Hanselmann
        not hasattr(data.cluster, 'rsahostkeypub')):
808 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
809 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
810 a8083063 Iustin Pop
    self._config_data = data
811 0779e3aa Iustin Pop
    # init the last serial as -1 so that the next write will cause
812 0779e3aa Iustin Pop
    # ssconf update
813 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
814 a8083063 Iustin Pop
815 a8083063 Iustin Pop
  def _DistributeConfig(self):
816 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
817 a8083063 Iustin Pop

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

821 a8083063 Iustin Pop
    """
822 a8083063 Iustin Pop
    if self._offline:
823 a8083063 Iustin Pop
      return True
824 a8083063 Iustin Pop
    bad = False
825 a8083063 Iustin Pop
826 6a5b8b4b Iustin Pop
    node_list = []
827 6a5b8b4b Iustin Pop
    addr_list = []
828 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
829 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
830 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
831 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
832 6b294c53 Iustin Pop
    # in between
833 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
834 6a5b8b4b Iustin Pop
      if node_name == myhostname:
835 6a5b8b4b Iustin Pop
        continue
836 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
837 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
838 6a5b8b4b Iustin Pop
        continue
839 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
840 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
841 6b294c53 Iustin Pop
842 6a5b8b4b Iustin Pop
    result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file,
843 6a5b8b4b Iustin Pop
                                            address_list=addr_list)
844 6a5b8b4b Iustin Pop
    for node in node_list:
845 a8083063 Iustin Pop
      if not result[node]:
846 74a48621 Iustin Pop
        logging.error("copy of file %s to node %s failed",
847 74a48621 Iustin Pop
                      self._cfg_file, node)
848 a8083063 Iustin Pop
        bad = True
849 a8083063 Iustin Pop
    return not bad
850 a8083063 Iustin Pop
851 a8083063 Iustin Pop
  def _WriteConfig(self, destination=None):
852 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
853 a8083063 Iustin Pop

854 a8083063 Iustin Pop
    """
855 a8083063 Iustin Pop
    if destination is None:
856 a8083063 Iustin Pop
      destination = self._cfg_file
857 a8083063 Iustin Pop
    self._BumpSerialNo()
858 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
859 a8083063 Iustin Pop
    dir_name, file_name = os.path.split(destination)
860 a8083063 Iustin Pop
    fd, name = tempfile.mkstemp('.newconfig', file_name, dir_name)
861 a8083063 Iustin Pop
    f = os.fdopen(fd, 'w')
862 a8083063 Iustin Pop
    try:
863 8d14b30d Iustin Pop
      f.write(txt)
864 a8083063 Iustin Pop
      os.fsync(f.fileno())
865 a8083063 Iustin Pop
    finally:
866 a8083063 Iustin Pop
      f.close()
867 a8083063 Iustin Pop
    # we don't need to do os.close(fd) as f.close() did it
868 a8083063 Iustin Pop
    os.rename(name, destination)
869 14e15659 Iustin Pop
    self.write_count += 1
870 3d3a04bc Iustin Pop
871 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
872 a8083063 Iustin Pop
    self._DistributeConfig()
873 a8083063 Iustin Pop
874 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
875 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
876 d9a855f1 Michael Hanselmann
      if not self._offline:
877 03d1dba2 Michael Hanselmann
        rpc.RpcRunner.call_write_ssconf_files(self._UnlockedGetNodeList(),
878 03d1dba2 Michael Hanselmann
                                              self._UnlockedGetSsconfValues())
879 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
880 54d1a06e Michael Hanselmann
881 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
882 054596f0 Iustin Pop
    """Return the values needed by ssconf.
883 054596f0 Iustin Pop

884 054596f0 Iustin Pop
    @rtype: dict
885 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
886 054596f0 Iustin Pop
        associated value
887 054596f0 Iustin Pop

888 054596f0 Iustin Pop
    """
889 f56618e0 Iustin Pop
    node_list = utils.NiceSort(self._UnlockedGetNodeList())
890 f56618e0 Iustin Pop
    mc_list = [self._UnlockedGetNodeInfo(name) for name in node_list]
891 f56618e0 Iustin Pop
    mc_list = [node.name for node in mc_list if node.master_candidate]
892 f56618e0 Iustin Pop
    node_list = "\n".join(node_list)
893 f56618e0 Iustin Pop
    mc_list = "\n".join(mc_list)
894 f56618e0 Iustin Pop
895 054596f0 Iustin Pop
    cluster = self._config_data.cluster
896 03d1dba2 Michael Hanselmann
    return {
897 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
898 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
899 f56618e0 Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_list,
900 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
901 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
902 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
903 054596f0 Iustin Pop
      constants.SS_NODE_LIST: node_list,
904 03d1dba2 Michael Hanselmann
      }
905 03d1dba2 Michael Hanselmann
906 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
907 f6bd6e98 Michael Hanselmann
  def InitConfig(self, version, cluster_config, master_node_config):
908 a8083063 Iustin Pop
    """Create the initial cluster configuration.
909 a8083063 Iustin Pop

910 a8083063 Iustin Pop
    It will contain the current node, which will also be the master
911 b9eeeb02 Michael Hanselmann
    node, and no instances.
912 a8083063 Iustin Pop

913 f6bd6e98 Michael Hanselmann
    @type version: int
914 f6bd6e98 Michael Hanselmann
    @param version: Configuration version
915 b9eeeb02 Michael Hanselmann
    @type cluster_config: objects.Cluster
916 b9eeeb02 Michael Hanselmann
    @param cluster_config: Cluster configuration
917 b9eeeb02 Michael Hanselmann
    @type master_node_config: objects.Node
918 b9eeeb02 Michael Hanselmann
    @param master_node_config: Master node configuration
919 b9eeeb02 Michael Hanselmann

920 b9eeeb02 Michael Hanselmann
    """
921 b9eeeb02 Michael Hanselmann
    nodes = {
922 b9eeeb02 Michael Hanselmann
      master_node_config.name: master_node_config,
923 b9eeeb02 Michael Hanselmann
      }
924 b9eeeb02 Michael Hanselmann
925 f6bd6e98 Michael Hanselmann
    self._config_data = objects.ConfigData(version=version,
926 f6bd6e98 Michael Hanselmann
                                           cluster=cluster_config,
927 b9eeeb02 Michael Hanselmann
                                           nodes=nodes,
928 a8083063 Iustin Pop
                                           instances={},
929 9d38c6e1 Iustin Pop
                                           serial_no=1)
930 a8083063 Iustin Pop
    self._WriteConfig()
931 a8083063 Iustin Pop
932 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
933 a8083063 Iustin Pop
  def GetVGName(self):
934 a8083063 Iustin Pop
    """Return the volume group name.
935 a8083063 Iustin Pop

936 a8083063 Iustin Pop
    """
937 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
938 a8083063 Iustin Pop
939 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
940 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
941 89ff8e15 Manuel Franceschini
    """Set the volume group name.
942 89ff8e15 Manuel Franceschini

943 89ff8e15 Manuel Franceschini
    """
944 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
945 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
946 89ff8e15 Manuel Franceschini
    self._WriteConfig()
947 89ff8e15 Manuel Franceschini
948 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
949 a8083063 Iustin Pop
  def GetDefBridge(self):
950 a8083063 Iustin Pop
    """Return the default bridge.
951 a8083063 Iustin Pop

952 a8083063 Iustin Pop
    """
953 a8083063 Iustin Pop
    return self._config_data.cluster.default_bridge
954 a8083063 Iustin Pop
955 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
956 a8083063 Iustin Pop
  def GetMACPrefix(self):
957 a8083063 Iustin Pop
    """Return the mac prefix.
958 a8083063 Iustin Pop

959 a8083063 Iustin Pop
    """
960 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
961 62779dd0 Iustin Pop
962 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
963 62779dd0 Iustin Pop
  def GetClusterInfo(self):
964 62779dd0 Iustin Pop
    """Returns informations about the cluster
965 62779dd0 Iustin Pop

966 62779dd0 Iustin Pop
    Returns:
967 62779dd0 Iustin Pop
      the cluster object
968 62779dd0 Iustin Pop

969 62779dd0 Iustin Pop
    """
970 62779dd0 Iustin Pop
    return self._config_data.cluster
971 e00fb268 Iustin Pop
972 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
973 e00fb268 Iustin Pop
  def Update(self, target):
974 e00fb268 Iustin Pop
    """Notify function to be called after updates.
975 e00fb268 Iustin Pop

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

982 e00fb268 Iustin Pop
    """
983 e00fb268 Iustin Pop
    if self._config_data is None:
984 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
985 3ecf6786 Iustin Pop
                                   " cannot save.")
986 f34901f8 Iustin Pop
    update_serial = False
987 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
988 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
989 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
990 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
991 f34901f8 Iustin Pop
      update_serial = True
992 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
993 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
994 e00fb268 Iustin Pop
    else:
995 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
996 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
997 e00fb268 Iustin Pop
    if not test:
998 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
999 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
1000 f34901f8 Iustin Pop
    target.serial_no += 1
1001 f34901f8 Iustin Pop
1002 cff4c037 Iustin Pop
    if update_serial:
1003 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
1004 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
1005 b989e85d Iustin Pop
1006 e00fb268 Iustin Pop
    self._WriteConfig()