Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ b6dd32db

History | View | Annotate | Download (101.6 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 9a94cee3 Jose A. Lopes
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 b459a848 Andrea Spadaccini
# pylint: disable=R0904
35 d367b66c Manuel Franceschini
# R0904: Too many public methods
36 d367b66c Manuel Franceschini
37 45f62156 Bernardo Dal Seno
import copy
38 a8083063 Iustin Pop
import os
39 a8083063 Iustin Pop
import random
40 d8470559 Michael Hanselmann
import logging
41 d693c864 Iustin Pop
import time
42 54c31fd3 Michael Hanselmann
import itertools
43 a8083063 Iustin Pop
44 a8083063 Iustin Pop
from ganeti import errors
45 f78ede4e Guido Trotter
from ganeti import locking
46 a8083063 Iustin Pop
from ganeti import utils
47 a8083063 Iustin Pop
from ganeti import constants
48 4869595d Petr Pudlak
import ganeti.rpc.node as rpc
49 a8083063 Iustin Pop
from ganeti import objects
50 8d14b30d Iustin Pop
from ganeti import serializer
51 0fbae49a Balazs Lecz
from ganeti import uidpool
52 a744b676 Manuel Franceschini
from ganeti import netutils
53 e60c73a1 René Nussbaumer
from ganeti import runtime
54 57407093 Michael Hanselmann
from ganeti import pathutils
55 6c0a75db Dimitris Aragiorgis
from ganeti import network
56 243cdbcc Michael Hanselmann
57 243cdbcc Michael Hanselmann
58 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
59 f78ede4e Guido Trotter
60 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
61 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
62 4fae38c5 Guido Trotter
63 f78ede4e Guido Trotter
64 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
65 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
66 c41eea6e Iustin Pop

67 c41eea6e Iustin Pop
  This only verifies the version of the configuration.
68 c41eea6e Iustin Pop

69 c41eea6e Iustin Pop
  @raise errors.ConfigurationError: if the version differs from what
70 c41eea6e Iustin Pop
      we expect
71 c41eea6e Iustin Pop

72 c41eea6e Iustin Pop
  """
73 5b263ed7 Michael Hanselmann
  if data.version != constants.CONFIG_VERSION:
74 4b63dc7a Iustin Pop
    raise errors.ConfigVersionMismatch(constants.CONFIG_VERSION, data.version)
75 a8083063 Iustin Pop
76 319856a9 Michael Hanselmann
77 013da361 Guido Trotter
class TemporaryReservationManager:
78 013da361 Guido Trotter
  """A temporary resource reservation manager.
79 013da361 Guido Trotter

80 013da361 Guido Trotter
  This is used to reserve resources in a job, before using them, making sure
81 013da361 Guido Trotter
  other jobs cannot get them in the meantime.
82 013da361 Guido Trotter

83 013da361 Guido Trotter
  """
84 013da361 Guido Trotter
  def __init__(self):
85 013da361 Guido Trotter
    self._ec_reserved = {}
86 013da361 Guido Trotter
87 013da361 Guido Trotter
  def Reserved(self, resource):
88 a7359d91 David Knowles
    for holder_reserved in self._ec_reserved.values():
89 013da361 Guido Trotter
      if resource in holder_reserved:
90 013da361 Guido Trotter
        return True
91 013da361 Guido Trotter
    return False
92 013da361 Guido Trotter
93 013da361 Guido Trotter
  def Reserve(self, ec_id, resource):
94 013da361 Guido Trotter
    if self.Reserved(resource):
95 28a7318f Iustin Pop
      raise errors.ReservationError("Duplicate reservation for resource '%s'"
96 28a7318f Iustin Pop
                                    % str(resource))
97 013da361 Guido Trotter
    if ec_id not in self._ec_reserved:
98 013da361 Guido Trotter
      self._ec_reserved[ec_id] = set([resource])
99 013da361 Guido Trotter
    else:
100 013da361 Guido Trotter
      self._ec_reserved[ec_id].add(resource)
101 013da361 Guido Trotter
102 013da361 Guido Trotter
  def DropECReservations(self, ec_id):
103 013da361 Guido Trotter
    if ec_id in self._ec_reserved:
104 013da361 Guido Trotter
      del self._ec_reserved[ec_id]
105 013da361 Guido Trotter
106 013da361 Guido Trotter
  def GetReserved(self):
107 013da361 Guido Trotter
    all_reserved = set()
108 013da361 Guido Trotter
    for holder_reserved in self._ec_reserved.values():
109 013da361 Guido Trotter
      all_reserved.update(holder_reserved)
110 013da361 Guido Trotter
    return all_reserved
111 013da361 Guido Trotter
112 ad4a9ae7 Dimitris Aragiorgis
  def GetECReserved(self, ec_id):
113 7f033fb3 Dimitris Aragiorgis
    """ Used when you want to retrieve all reservations for a specific
114 7f033fb3 Dimitris Aragiorgis
        execution context. E.g when commiting reserved IPs for a specific
115 7f033fb3 Dimitris Aragiorgis
        network.
116 7f033fb3 Dimitris Aragiorgis

117 7f033fb3 Dimitris Aragiorgis
    """
118 ad4a9ae7 Dimitris Aragiorgis
    ec_reserved = set()
119 ad4a9ae7 Dimitris Aragiorgis
    if ec_id in self._ec_reserved:
120 ad4a9ae7 Dimitris Aragiorgis
      ec_reserved.update(self._ec_reserved[ec_id])
121 ad4a9ae7 Dimitris Aragiorgis
    return ec_reserved
122 ad4a9ae7 Dimitris Aragiorgis
123 013da361 Guido Trotter
  def Generate(self, existing, generate_one_fn, ec_id):
124 013da361 Guido Trotter
    """Generate a new resource of this type
125 013da361 Guido Trotter

126 013da361 Guido Trotter
    """
127 013da361 Guido Trotter
    assert callable(generate_one_fn)
128 013da361 Guido Trotter
129 013da361 Guido Trotter
    all_elems = self.GetReserved()
130 013da361 Guido Trotter
    all_elems.update(existing)
131 013da361 Guido Trotter
    retries = 64
132 013da361 Guido Trotter
    while retries > 0:
133 013da361 Guido Trotter
      new_resource = generate_one_fn()
134 013da361 Guido Trotter
      if new_resource is not None and new_resource not in all_elems:
135 013da361 Guido Trotter
        break
136 013da361 Guido Trotter
    else:
137 013da361 Guido Trotter
      raise errors.ConfigurationError("Not able generate new resource"
138 013da361 Guido Trotter
                                      " (last tried: %s)" % new_resource)
139 013da361 Guido Trotter
    self.Reserve(ec_id, new_resource)
140 013da361 Guido Trotter
    return new_resource
141 013da361 Guido Trotter
142 013da361 Guido Trotter
143 fe698b38 Michael Hanselmann
def _MatchNameComponentIgnoreCase(short_name, names):
144 3a93eebb Michael Hanselmann
  """Wrapper around L{utils.text.MatchNameComponent}.
145 fe698b38 Michael Hanselmann

146 fe698b38 Michael Hanselmann
  """
147 fe698b38 Michael Hanselmann
  return utils.MatchNameComponent(short_name, names, case_sensitive=False)
148 fe698b38 Michael Hanselmann
149 fe698b38 Michael Hanselmann
150 82c54b5b Michael Hanselmann
def _CheckInstanceDiskIvNames(disks):
151 82c54b5b Michael Hanselmann
  """Checks if instance's disks' C{iv_name} attributes are in order.
152 82c54b5b Michael Hanselmann

153 82c54b5b Michael Hanselmann
  @type disks: list of L{objects.Disk}
154 82c54b5b Michael Hanselmann
  @param disks: List of disks
155 82c54b5b Michael Hanselmann
  @rtype: list of tuples; (int, string, string)
156 82c54b5b Michael Hanselmann
  @return: List of wrongly named disks, each tuple contains disk index,
157 82c54b5b Michael Hanselmann
    expected and actual name
158 82c54b5b Michael Hanselmann

159 82c54b5b Michael Hanselmann
  """
160 82c54b5b Michael Hanselmann
  result = []
161 82c54b5b Michael Hanselmann
162 82c54b5b Michael Hanselmann
  for (idx, disk) in enumerate(disks):
163 82c54b5b Michael Hanselmann
    exp_iv_name = "disk/%s" % idx
164 82c54b5b Michael Hanselmann
    if disk.iv_name != exp_iv_name:
165 82c54b5b Michael Hanselmann
      result.append((idx, exp_iv_name, disk.iv_name))
166 82c54b5b Michael Hanselmann
167 82c54b5b Michael Hanselmann
  return result
168 82c54b5b Michael Hanselmann
169 3c286190 Dimitris Aragiorgis
170 3efa7659 Thomas Thrainer
class ConfigWriter(object):
171 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
172 a8083063 Iustin Pop

173 d8aee57e Iustin Pop
  @ivar _temporary_lvs: reservation manager for temporary LVs
174 d8aee57e Iustin Pop
  @ivar _all_rms: a list of all temporary reservation managers
175 d8aee57e Iustin Pop

176 098c0958 Michael Hanselmann
  """
177 eb180fe2 Iustin Pop
  def __init__(self, cfg_file=None, offline=False, _getents=runtime.GetEnts,
178 eb180fe2 Iustin Pop
               accept_foreign=False):
179 14e15659 Iustin Pop
    self.write_count = 0
180 f78ede4e Guido Trotter
    self._lock = _config_lock
181 a8083063 Iustin Pop
    self._config_data = None
182 a8083063 Iustin Pop
    self._offline = offline
183 a8083063 Iustin Pop
    if cfg_file is None:
184 57407093 Michael Hanselmann
      self._cfg_file = pathutils.CLUSTER_CONF_FILE
185 a8083063 Iustin Pop
    else:
186 a8083063 Iustin Pop
      self._cfg_file = cfg_file
187 e60c73a1 René Nussbaumer
    self._getents = _getents
188 4fae38c5 Guido Trotter
    self._temporary_ids = TemporaryReservationManager()
189 a81c53c9 Iustin Pop
    self._temporary_drbds = {}
190 36b66e6e Guido Trotter
    self._temporary_macs = TemporaryReservationManager()
191 afa1386e Guido Trotter
    self._temporary_secrets = TemporaryReservationManager()
192 d8aee57e Iustin Pop
    self._temporary_lvs = TemporaryReservationManager()
193 ad4a9ae7 Dimitris Aragiorgis
    self._temporary_ips = TemporaryReservationManager()
194 d8aee57e Iustin Pop
    self._all_rms = [self._temporary_ids, self._temporary_macs,
195 ad4a9ae7 Dimitris Aragiorgis
                     self._temporary_secrets, self._temporary_lvs,
196 ad4a9ae7 Dimitris Aragiorgis
                     self._temporary_ips]
197 89e1fc26 Iustin Pop
    # Note: in order to prevent errors when resolving our name in
198 89e1fc26 Iustin Pop
    # _DistributeConfig, we compute it here once and reuse it; it's
199 89e1fc26 Iustin Pop
    # better to raise an error before starting to modify the config
200 89e1fc26 Iustin Pop
    # file than after it was modified
201 b705c7a6 Manuel Franceschini
    self._my_hostname = netutils.Hostname.GetSysName()
202 3c7f6c44 Iustin Pop
    self._last_cluster_serial = -1
203 bd407597 Iustin Pop
    self._cfg_id = None
204 b2acdbdc Michael Hanselmann
    self._context = None
205 eb180fe2 Iustin Pop
    self._OpenConfig(accept_foreign)
206 a8083063 Iustin Pop
207 b2acdbdc Michael Hanselmann
  def _GetRpc(self, address_list):
208 b2acdbdc Michael Hanselmann
    """Returns RPC runner for configuration.
209 b2acdbdc Michael Hanselmann

210 b2acdbdc Michael Hanselmann
    """
211 b2acdbdc Michael Hanselmann
    return rpc.ConfigRunner(self._context, address_list)
212 b2acdbdc Michael Hanselmann
213 b2acdbdc Michael Hanselmann
  def SetContext(self, context):
214 b2acdbdc Michael Hanselmann
    """Sets Ganeti context.
215 b2acdbdc Michael Hanselmann

216 b2acdbdc Michael Hanselmann
    """
217 b2acdbdc Michael Hanselmann
    self._context = context
218 b2acdbdc Michael Hanselmann
219 a8083063 Iustin Pop
  # this method needs to be static, so that we can call it on the class
220 a8083063 Iustin Pop
  @staticmethod
221 a8083063 Iustin Pop
  def IsCluster():
222 a8083063 Iustin Pop
    """Check if the cluster is configured.
223 a8083063 Iustin Pop

224 a8083063 Iustin Pop
    """
225 57407093 Michael Hanselmann
    return os.path.exists(pathutils.CLUSTER_CONF_FILE)
226 a8083063 Iustin Pop
227 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
228 5768e6a6 René Nussbaumer
  def GetNdParams(self, node):
229 5768e6a6 René Nussbaumer
    """Get the node params populated with cluster defaults.
230 5768e6a6 René Nussbaumer

231 ce523de1 Michael Hanselmann
    @type node: L{objects.Node}
232 5768e6a6 René Nussbaumer
    @param node: The node we want to know the params for
233 5768e6a6 René Nussbaumer
    @return: A dict with the filled in node params
234 5768e6a6 René Nussbaumer

235 5768e6a6 René Nussbaumer
    """
236 5768e6a6 René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
237 5768e6a6 René Nussbaumer
    return self._config_data.cluster.FillND(node, nodegroup)
238 5768e6a6 René Nussbaumer
239 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
240 6b2a2942 Petr Pudlak
  def GetNdGroupParams(self, nodegroup):
241 6b2a2942 Petr Pudlak
    """Get the node groups params populated with cluster defaults.
242 6b2a2942 Petr Pudlak

243 6b2a2942 Petr Pudlak
    @type nodegroup: L{objects.NodeGroup}
244 6b2a2942 Petr Pudlak
    @param nodegroup: The node group we want to know the params for
245 6b2a2942 Petr Pudlak
    @return: A dict with the filled in node group params
246 6b2a2942 Petr Pudlak

247 6b2a2942 Petr Pudlak
    """
248 6b2a2942 Petr Pudlak
    return self._config_data.cluster.FillNDGroup(nodegroup)
249 6b2a2942 Petr Pudlak
250 6b2a2942 Petr Pudlak
  @locking.ssynchronized(_config_lock, shared=1)
251 8a147bba René Nussbaumer
  def GetInstanceDiskParams(self, instance):
252 8a147bba René Nussbaumer
    """Get the disk params populated with inherit chain.
253 8a147bba René Nussbaumer

254 8a147bba René Nussbaumer
    @type instance: L{objects.Instance}
255 8a147bba René Nussbaumer
    @param instance: The instance we want to know the params for
256 8a147bba René Nussbaumer
    @return: A dict with the filled in disk params
257 8a147bba René Nussbaumer

258 8a147bba René Nussbaumer
    """
259 8a147bba René Nussbaumer
    node = self._UnlockedGetNodeInfo(instance.primary_node)
260 8a147bba René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
261 99ccf8b9 René Nussbaumer
    return self._UnlockedGetGroupDiskParams(nodegroup)
262 2f1c2fe6 Ilias Tsitsimpis
263 2f1c2fe6 Ilias Tsitsimpis
  # pylint: disable=R0201
264 2f1c2fe6 Ilias Tsitsimpis
  def _UnlockedGetInstanceNodes(self, instance, disks=None):
265 2f1c2fe6 Ilias Tsitsimpis
    """Get all disk-releated nodes for an instance.
266 2f1c2fe6 Ilias Tsitsimpis

267 2f1c2fe6 Ilias Tsitsimpis
    This function is for internal use, when the config lock is already held.
268 2f1c2fe6 Ilias Tsitsimpis

269 2f1c2fe6 Ilias Tsitsimpis
    """
270 2f1c2fe6 Ilias Tsitsimpis
    all_nodes = [instance.primary_node]
271 2f1c2fe6 Ilias Tsitsimpis
    inst_disks = instance.disks
272 2f1c2fe6 Ilias Tsitsimpis
    if disks is not None:
273 2f1c2fe6 Ilias Tsitsimpis
      inst_disks.extend(disks)
274 2f1c2fe6 Ilias Tsitsimpis
    for disk in inst_disks:
275 2f1c2fe6 Ilias Tsitsimpis
      all_nodes.extend(disk.all_nodes)
276 2f1c2fe6 Ilias Tsitsimpis
    return tuple(set(all_nodes))
277 2f1c2fe6 Ilias Tsitsimpis
278 2f1c2fe6 Ilias Tsitsimpis
  @locking.ssynchronized(_config_lock, shared=1)
279 2f1c2fe6 Ilias Tsitsimpis
  def GetInstanceNodes(self, instance, disks=None):
280 2f1c2fe6 Ilias Tsitsimpis
    """Get all disk-releated nodes for an instance.
281 2f1c2fe6 Ilias Tsitsimpis

282 2f1c2fe6 Ilias Tsitsimpis
    For non-DRBD, this will be empty, for DRBD it will contain both
283 2f1c2fe6 Ilias Tsitsimpis
    the primary and the secondaries.
284 2f1c2fe6 Ilias Tsitsimpis
    If additional disks are given, include their nodes to the result.
285 2f1c2fe6 Ilias Tsitsimpis
    This is done because these disks may not be attached to the instance yet.
286 2f1c2fe6 Ilias Tsitsimpis

287 2f1c2fe6 Ilias Tsitsimpis
    @type instance: L{objects.Instance}
288 2f1c2fe6 Ilias Tsitsimpis
    @param instance: The instance we want to get nodes for
289 2f1c2fe6 Ilias Tsitsimpis
    @type disks: list of L{objects.Disk}
290 2f1c2fe6 Ilias Tsitsimpis
    @param disks: If given, include these disks to the result
291 2f1c2fe6 Ilias Tsitsimpis
    @return: A list of names for all the nodes of the instance
292 2f1c2fe6 Ilias Tsitsimpis

293 2f1c2fe6 Ilias Tsitsimpis
    """
294 2f1c2fe6 Ilias Tsitsimpis
    return self._UnlockedGetInstanceNodes(instance, disks=disks)
295 2f1c2fe6 Ilias Tsitsimpis
296 2f1c2fe6 Ilias Tsitsimpis
  def _UnlockedGetInstanceSecondaryNodes(self, instance):
297 2f1c2fe6 Ilias Tsitsimpis
    """Get the list of secondary nodes.
298 2f1c2fe6 Ilias Tsitsimpis

299 2f1c2fe6 Ilias Tsitsimpis
    This function is for internal use, when the config lock is already held.
300 2f1c2fe6 Ilias Tsitsimpis

301 2f1c2fe6 Ilias Tsitsimpis
    """
302 2f1c2fe6 Ilias Tsitsimpis
    all_nodes = set(self._UnlockedGetInstanceNodes(instance))
303 2f1c2fe6 Ilias Tsitsimpis
    all_nodes.discard(instance.primary_node)
304 2f1c2fe6 Ilias Tsitsimpis
    return tuple(all_nodes)
305 2f1c2fe6 Ilias Tsitsimpis
306 2f1c2fe6 Ilias Tsitsimpis
  @locking.ssynchronized(_config_lock, shared=1)
307 2f1c2fe6 Ilias Tsitsimpis
  def GetInstanceSecondaryNodes(self, instance):
308 2f1c2fe6 Ilias Tsitsimpis
    """Get the list of secondary nodes.
309 2f1c2fe6 Ilias Tsitsimpis

310 2f1c2fe6 Ilias Tsitsimpis
    This is a simple wrapper over _UnlockedGetInstanceNodes.
311 2f1c2fe6 Ilias Tsitsimpis

312 2f1c2fe6 Ilias Tsitsimpis
    """
313 2f1c2fe6 Ilias Tsitsimpis
    return self._UnlockedGetInstanceSecondaryNodes(instance)
314 7abe5725 Ilias Tsitsimpis
315 7abe5725 Ilias Tsitsimpis
  # pylint: disable=R0201
316 7abe5725 Ilias Tsitsimpis
  def _UnlockedGetInstanceLVsByNode(self, instance, lvmap=None):
317 7abe5725 Ilias Tsitsimpis
    """Provide a mapping of node to LVs a given instance owns.
318 7abe5725 Ilias Tsitsimpis

319 7abe5725 Ilias Tsitsimpis
    This is a simple wrapper over _UnlockedGetInstanceNodes.
320 7abe5725 Ilias Tsitsimpis

321 7abe5725 Ilias Tsitsimpis
    """
322 7abe5725 Ilias Tsitsimpis
    def _MapLVsByNode(lvmap, devs, node_uuid):
323 7abe5725 Ilias Tsitsimpis
      """Recursively helper function."""
324 7abe5725 Ilias Tsitsimpis
      if not node_uuid in lvmap:
325 7abe5725 Ilias Tsitsimpis
        lvmap[node_uuid] = []
326 7abe5725 Ilias Tsitsimpis
327 7abe5725 Ilias Tsitsimpis
      for dev in devs:
328 7abe5725 Ilias Tsitsimpis
        if dev.dev_type == constants.DT_PLAIN:
329 7abe5725 Ilias Tsitsimpis
          lvmap[node_uuid].append(dev.logical_id[0] + "/" + dev.logical_id[1])
330 7abe5725 Ilias Tsitsimpis
331 7abe5725 Ilias Tsitsimpis
        elif dev.dev_type in constants.DTS_DRBD:
332 7abe5725 Ilias Tsitsimpis
          if dev.children:
333 7abe5725 Ilias Tsitsimpis
            _MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
334 7abe5725 Ilias Tsitsimpis
            _MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
335 7abe5725 Ilias Tsitsimpis
336 7abe5725 Ilias Tsitsimpis
        elif dev.children:
337 7abe5725 Ilias Tsitsimpis
          _MapLVsByNode(lvmap, devs.children, node_uuid)
338 7abe5725 Ilias Tsitsimpis
339 7abe5725 Ilias Tsitsimpis
    if lvmap is None:
340 7abe5725 Ilias Tsitsimpis
      lvmap = {}
341 7abe5725 Ilias Tsitsimpis
      ret = lvmap
342 7abe5725 Ilias Tsitsimpis
    else:
343 7abe5725 Ilias Tsitsimpis
      ret = None
344 7abe5725 Ilias Tsitsimpis
345 7abe5725 Ilias Tsitsimpis
    node_uuid = instance.primary_node
346 7abe5725 Ilias Tsitsimpis
    devs = instance.disks
347 7abe5725 Ilias Tsitsimpis
    _MapLVsByNode(lvmap, devs, node_uuid)
348 7abe5725 Ilias Tsitsimpis
    return ret
349 7abe5725 Ilias Tsitsimpis
350 7abe5725 Ilias Tsitsimpis
  @locking.ssynchronized(_config_lock, shared=1)
351 7abe5725 Ilias Tsitsimpis
  def GetInstanceLVsByNode(self, instance, lvmap=None):
352 7abe5725 Ilias Tsitsimpis
    """Provide a mapping of node to LVs a given instance owns.
353 7abe5725 Ilias Tsitsimpis

354 7abe5725 Ilias Tsitsimpis
    This function figures out what logical volums should belong on
355 7abe5725 Ilias Tsitsimpis
    which nodes, recursing through a device tree.
356 7abe5725 Ilias Tsitsimpis

357 7abe5725 Ilias Tsitsimpis
    @type instance: L{objects.Instance}
358 7abe5725 Ilias Tsitsimpis
    @param instance: The instance we want to compute the LVsByNode for
359 7abe5725 Ilias Tsitsimpis
    @type lvmap: dict
360 7abe5725 Ilias Tsitsimpis
    @param lvmap: optional dictionary to receive the
361 7abe5725 Ilias Tsitsimpis
        'node' : ['lv', ...] data.
362 7abe5725 Ilias Tsitsimpis
    @return: None if lvmap arg is given, otherwise, a dictionary of
363 7abe5725 Ilias Tsitsimpis
        the form { 'node_uuid' : ['volume1', 'volume2', ...], ... };
364 7abe5725 Ilias Tsitsimpis
        volumeN is of the form "vg_name/lv_name", compatible with
365 7abe5725 Ilias Tsitsimpis
        GetVolumeList()
366 7abe5725 Ilias Tsitsimpis

367 7abe5725 Ilias Tsitsimpis
    """
368 7abe5725 Ilias Tsitsimpis
    return self._UnlockedGetInstanceLVsByNode(instance, lvmap=lvmap)
369 99ccf8b9 René Nussbaumer
370 99ccf8b9 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
371 99ccf8b9 René Nussbaumer
  def GetGroupDiskParams(self, group):
372 99ccf8b9 René Nussbaumer
    """Get the disk params populated with inherit chain.
373 99ccf8b9 René Nussbaumer

374 af9fb4cc René Nussbaumer
    @type group: L{objects.NodeGroup}
375 99ccf8b9 René Nussbaumer
    @param group: The group we want to know the params for
376 99ccf8b9 René Nussbaumer
    @return: A dict with the filled in disk params
377 99ccf8b9 René Nussbaumer

378 99ccf8b9 René Nussbaumer
    """
379 99ccf8b9 René Nussbaumer
    return self._UnlockedGetGroupDiskParams(group)
380 99ccf8b9 René Nussbaumer
381 99ccf8b9 René Nussbaumer
  def _UnlockedGetGroupDiskParams(self, group):
382 99ccf8b9 René Nussbaumer
    """Get the disk params populated with inherit chain down to node-group.
383 99ccf8b9 René Nussbaumer

384 af9fb4cc René Nussbaumer
    @type group: L{objects.NodeGroup}
385 99ccf8b9 René Nussbaumer
    @param group: The group we want to know the params for
386 99ccf8b9 René Nussbaumer
    @return: A dict with the filled in disk params
387 99ccf8b9 René Nussbaumer

388 99ccf8b9 René Nussbaumer
    """
389 99ccf8b9 René Nussbaumer
    return self._config_data.cluster.SimpleFillDP(group.diskparams)
390 8a147bba René Nussbaumer
391 9ccacbc8 Dimitris Aragiorgis
  def _UnlockedGetNetworkMACPrefix(self, net_uuid):
392 032a7d71 Dimitris Aragiorgis
    """Return the network mac prefix if it exists or the cluster level default.
393 032a7d71 Dimitris Aragiorgis

394 032a7d71 Dimitris Aragiorgis
    """
395 032a7d71 Dimitris Aragiorgis
    prefix = None
396 9ccacbc8 Dimitris Aragiorgis
    if net_uuid:
397 1b68f268 Helga Velroyen
      nobj = self._UnlockedGetNetwork(net_uuid)
398 1b68f268 Helga Velroyen
      if nobj.mac_prefix:
399 1b68f268 Helga Velroyen
        prefix = nobj.mac_prefix
400 032a7d71 Dimitris Aragiorgis
401 032a7d71 Dimitris Aragiorgis
    return prefix
402 032a7d71 Dimitris Aragiorgis
403 032a7d71 Dimitris Aragiorgis
  def _GenerateOneMAC(self, prefix=None):
404 032a7d71 Dimitris Aragiorgis
    """Return a function that randomly generates a MAC suffic
405 032a7d71 Dimitris Aragiorgis
       and appends it to the given prefix. If prefix is not given get
406 032a7d71 Dimitris Aragiorgis
       the cluster level default.
407 032a7d71 Dimitris Aragiorgis

408 032a7d71 Dimitris Aragiorgis
    """
409 032a7d71 Dimitris Aragiorgis
    if not prefix:
410 032a7d71 Dimitris Aragiorgis
      prefix = self._config_data.cluster.mac_prefix
411 032a7d71 Dimitris Aragiorgis
412 032a7d71 Dimitris Aragiorgis
    def GenMac():
413 032a7d71 Dimitris Aragiorgis
      byte1 = random.randrange(0, 256)
414 032a7d71 Dimitris Aragiorgis
      byte2 = random.randrange(0, 256)
415 032a7d71 Dimitris Aragiorgis
      byte3 = random.randrange(0, 256)
416 032a7d71 Dimitris Aragiorgis
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
417 032a7d71 Dimitris Aragiorgis
      return mac
418 032a7d71 Dimitris Aragiorgis
419 032a7d71 Dimitris Aragiorgis
    return GenMac
420 032a7d71 Dimitris Aragiorgis
421 8a147bba René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
422 9ccacbc8 Dimitris Aragiorgis
  def GenerateMAC(self, net_uuid, ec_id):
423 a8083063 Iustin Pop
    """Generate a MAC for an instance.
424 a8083063 Iustin Pop

425 a8083063 Iustin Pop
    This should check the current instances for duplicates.
426 a8083063 Iustin Pop

427 a8083063 Iustin Pop
    """
428 36b66e6e Guido Trotter
    existing = self._AllMACs()
429 9ccacbc8 Dimitris Aragiorgis
    prefix = self._UnlockedGetNetworkMACPrefix(net_uuid)
430 032a7d71 Dimitris Aragiorgis
    gen_mac = self._GenerateOneMAC(prefix)
431 a0af6c80 Dimitris Aragiorgis
    return self._temporary_ids.Generate(existing, gen_mac, ec_id)
432 a8083063 Iustin Pop
433 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
434 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
435 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
436 1862d460 Alexander Schreiber

437 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
438 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
439 1862d460 Alexander Schreiber

440 1862d460 Alexander Schreiber
    """
441 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
442 36b66e6e Guido Trotter
    if mac in all_macs:
443 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
444 36b66e6e Guido Trotter
    else:
445 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
446 1862d460 Alexander Schreiber
447 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedCommitTemporaryIps(self, ec_id):
448 ad4a9ae7 Dimitris Aragiorgis
    """Commit all reserved IP address to their respective pools
449 ad4a9ae7 Dimitris Aragiorgis

450 ad4a9ae7 Dimitris Aragiorgis
    """
451 ad4a9ae7 Dimitris Aragiorgis
    for action, address, net_uuid in self._temporary_ips.GetECReserved(ec_id):
452 ad4a9ae7 Dimitris Aragiorgis
      self._UnlockedCommitIp(action, net_uuid, address)
453 ad4a9ae7 Dimitris Aragiorgis
454 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedCommitIp(self, action, net_uuid, address):
455 ad4a9ae7 Dimitris Aragiorgis
    """Commit a reserved IP address to an IP pool.
456 ad4a9ae7 Dimitris Aragiorgis

457 ad4a9ae7 Dimitris Aragiorgis
    The IP address is taken from the network's IP pool and marked as reserved.
458 ad4a9ae7 Dimitris Aragiorgis

459 ad4a9ae7 Dimitris Aragiorgis
    """
460 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
461 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
462 e81eef56 Dimitris Aragiorgis
    if action == constants.RESERVE_ACTION:
463 ad4a9ae7 Dimitris Aragiorgis
      pool.Reserve(address)
464 e81eef56 Dimitris Aragiorgis
    elif action == constants.RELEASE_ACTION:
465 ad4a9ae7 Dimitris Aragiorgis
      pool.Release(address)
466 ad4a9ae7 Dimitris Aragiorgis
467 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedReleaseIp(self, net_uuid, address, ec_id):
468 ad4a9ae7 Dimitris Aragiorgis
    """Give a specific IP address back to an IP pool.
469 ad4a9ae7 Dimitris Aragiorgis

470 ad4a9ae7 Dimitris Aragiorgis
    The IP address is returned to the IP pool designated by pool_id and marked
471 ad4a9ae7 Dimitris Aragiorgis
    as reserved.
472 ad4a9ae7 Dimitris Aragiorgis

473 ad4a9ae7 Dimitris Aragiorgis
    """
474 e81eef56 Dimitris Aragiorgis
    self._temporary_ips.Reserve(ec_id,
475 e81eef56 Dimitris Aragiorgis
                                (constants.RELEASE_ACTION, address, net_uuid))
476 ad4a9ae7 Dimitris Aragiorgis
477 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
478 9ccacbc8 Dimitris Aragiorgis
  def ReleaseIp(self, net_uuid, address, ec_id):
479 ad4a9ae7 Dimitris Aragiorgis
    """Give a specified IP address back to an IP pool.
480 ad4a9ae7 Dimitris Aragiorgis

481 ad4a9ae7 Dimitris Aragiorgis
    This is just a wrapper around _UnlockedReleaseIp.
482 ad4a9ae7 Dimitris Aragiorgis

483 ad4a9ae7 Dimitris Aragiorgis
    """
484 9ccacbc8 Dimitris Aragiorgis
    if net_uuid:
485 9ccacbc8 Dimitris Aragiorgis
      self._UnlockedReleaseIp(net_uuid, address, ec_id)
486 ad4a9ae7 Dimitris Aragiorgis
487 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
488 9ccacbc8 Dimitris Aragiorgis
  def GenerateIp(self, net_uuid, ec_id):
489 ad4a9ae7 Dimitris Aragiorgis
    """Find a free IPv4 address for an instance.
490 ad4a9ae7 Dimitris Aragiorgis

491 ad4a9ae7 Dimitris Aragiorgis
    """
492 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
493 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
494 ad4a9ae7 Dimitris Aragiorgis
495 ad4a9ae7 Dimitris Aragiorgis
    def gen_one():
496 ad4a9ae7 Dimitris Aragiorgis
      try:
497 1f1d3bf2 Dimitris Aragiorgis
        ip = pool.GenerateFree()
498 1f1d3bf2 Dimitris Aragiorgis
      except errors.AddressPoolError:
499 ad4a9ae7 Dimitris Aragiorgis
        raise errors.ReservationError("Cannot generate IP. Network is full")
500 e81eef56 Dimitris Aragiorgis
      return (constants.RESERVE_ACTION, ip, net_uuid)
501 ad4a9ae7 Dimitris Aragiorgis
502 beb81ea5 Dimitris Aragiorgis
    _, address, _ = self._temporary_ips.Generate([], gen_one, ec_id)
503 ad4a9ae7 Dimitris Aragiorgis
    return address
504 ad4a9ae7 Dimitris Aragiorgis
505 031d2db1 Dimitris Aragiorgis
  def _UnlockedReserveIp(self, net_uuid, address, ec_id, check=True):
506 ad4a9ae7 Dimitris Aragiorgis
    """Reserve a given IPv4 address for use by an instance.
507 ad4a9ae7 Dimitris Aragiorgis

508 ad4a9ae7 Dimitris Aragiorgis
    """
509 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
510 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
511 ad4a9ae7 Dimitris Aragiorgis
    try:
512 ad4a9ae7 Dimitris Aragiorgis
      isreserved = pool.IsReserved(address)
513 031d2db1 Dimitris Aragiorgis
      isextreserved = pool.IsReserved(address, external=True)
514 ad4a9ae7 Dimitris Aragiorgis
    except errors.AddressPoolError:
515 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address not in network")
516 ad4a9ae7 Dimitris Aragiorgis
    if isreserved:
517 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address already in use")
518 031d2db1 Dimitris Aragiorgis
    if check and isextreserved:
519 031d2db1 Dimitris Aragiorgis
      raise errors.ReservationError("IP is externally reserved")
520 ad4a9ae7 Dimitris Aragiorgis
521 e81eef56 Dimitris Aragiorgis
    return self._temporary_ips.Reserve(ec_id,
522 e81eef56 Dimitris Aragiorgis
                                       (constants.RESERVE_ACTION,
523 e81eef56 Dimitris Aragiorgis
                                        address, net_uuid))
524 ad4a9ae7 Dimitris Aragiorgis
525 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
526 031d2db1 Dimitris Aragiorgis
  def ReserveIp(self, net_uuid, address, ec_id, check=True):
527 ad4a9ae7 Dimitris Aragiorgis
    """Reserve a given IPv4 address for use by an instance.
528 ad4a9ae7 Dimitris Aragiorgis

529 ad4a9ae7 Dimitris Aragiorgis
    """
530 9ccacbc8 Dimitris Aragiorgis
    if net_uuid:
531 031d2db1 Dimitris Aragiorgis
      return self._UnlockedReserveIp(net_uuid, address, ec_id, check)
532 ad4a9ae7 Dimitris Aragiorgis
533 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
534 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
535 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
536 d8aee57e Iustin Pop

537 d8aee57e Iustin Pop
    @type lv_name: string
538 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
539 d8aee57e Iustin Pop

540 d8aee57e Iustin Pop
    """
541 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
542 d8aee57e Iustin Pop
    if lv_name in all_lvs:
543 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
544 d8aee57e Iustin Pop
    else:
545 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
546 d8aee57e Iustin Pop
547 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
548 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
549 f9518d38 Iustin Pop
    """Generate a DRBD secret.
550 f9518d38 Iustin Pop

551 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
552 f9518d38 Iustin Pop

553 f9518d38 Iustin Pop
    """
554 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
555 afa1386e Guido Trotter
                                            utils.GenerateSecret,
556 afa1386e Guido Trotter
                                            ec_id)
557 8d9c3bef Michael Hanselmann
558 34e54ebc Iustin Pop
  def _AllLVs(self):
559 923b1523 Iustin Pop
    """Compute the list of all LVs.
560 923b1523 Iustin Pop

561 923b1523 Iustin Pop
    """
562 923b1523 Iustin Pop
    lvnames = set()
563 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
564 b6dd32db Ilias Tsitsimpis
      node_data = self._UnlockedGetInstanceLVsByNode(instance)
565 923b1523 Iustin Pop
      for lv_list in node_data.values():
566 923b1523 Iustin Pop
        lvnames.update(lv_list)
567 923b1523 Iustin Pop
    return lvnames
568 923b1523 Iustin Pop
569 b87a9c5f Christos Stavrakakis
  def _AllDisks(self):
570 79780863 Michele Tartara
    """Compute the list of all Disks (recursively, including children).
571 b87a9c5f Christos Stavrakakis

572 b87a9c5f Christos Stavrakakis
    """
573 79780863 Michele Tartara
    def DiskAndAllChildren(disk):
574 79780863 Michele Tartara
      """Returns a list containing the given disk and all of his children.
575 79780863 Michele Tartara

576 79780863 Michele Tartara
      """
577 79780863 Michele Tartara
      disks = [disk]
578 79780863 Michele Tartara
      if disk.children:
579 79780863 Michele Tartara
        for child_disk in disk.children:
580 79780863 Michele Tartara
          disks.extend(DiskAndAllChildren(child_disk))
581 79780863 Michele Tartara
      return disks
582 79780863 Michele Tartara
583 b87a9c5f Christos Stavrakakis
    disks = []
584 b87a9c5f Christos Stavrakakis
    for instance in self._config_data.instances.values():
585 79780863 Michele Tartara
      for disk in instance.disks:
586 79780863 Michele Tartara
        disks.extend(DiskAndAllChildren(disk))
587 b87a9c5f Christos Stavrakakis
    return disks
588 b87a9c5f Christos Stavrakakis
589 b87a9c5f Christos Stavrakakis
  def _AllNICs(self):
590 b87a9c5f Christos Stavrakakis
    """Compute the list of all NICs.
591 b87a9c5f Christos Stavrakakis

592 b87a9c5f Christos Stavrakakis
    """
593 b87a9c5f Christos Stavrakakis
    nics = []
594 b87a9c5f Christos Stavrakakis
    for instance in self._config_data.instances.values():
595 b87a9c5f Christos Stavrakakis
      nics.extend(instance.nics)
596 b87a9c5f Christos Stavrakakis
    return nics
597 b87a9c5f Christos Stavrakakis
598 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
599 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
600 34e54ebc Iustin Pop

601 34e54ebc Iustin Pop
    @type include_temporary: boolean
602 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
603 34e54ebc Iustin Pop
    @rtype: set
604 34e54ebc Iustin Pop
    @return: a set of IDs
605 34e54ebc Iustin Pop

606 34e54ebc Iustin Pop
    """
607 34e54ebc Iustin Pop
    existing = set()
608 34e54ebc Iustin Pop
    if include_temporary:
609 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
610 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
611 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
612 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
613 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
614 34e54ebc Iustin Pop
    return existing
615 34e54ebc Iustin Pop
616 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
617 430b923c Iustin Pop
    """Generate an unique UUID.
618 923b1523 Iustin Pop

619 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
620 923b1523 Iustin Pop
    duplicates.
621 923b1523 Iustin Pop

622 c41eea6e Iustin Pop
    @rtype: string
623 c41eea6e Iustin Pop
    @return: the unique id
624 923b1523 Iustin Pop

625 923b1523 Iustin Pop
    """
626 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
627 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
628 923b1523 Iustin Pop
629 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
630 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
631 430b923c Iustin Pop
    """Generate an unique ID.
632 430b923c Iustin Pop

633 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
634 430b923c Iustin Pop

635 4fae38c5 Guido Trotter
    @type ec_id: string
636 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
637 34d657ba Iustin Pop

638 34d657ba Iustin Pop
    """
639 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
640 34d657ba Iustin Pop
641 a8083063 Iustin Pop
  def _AllMACs(self):
642 a8083063 Iustin Pop
    """Return all MACs present in the config.
643 a8083063 Iustin Pop

644 c41eea6e Iustin Pop
    @rtype: list
645 c41eea6e Iustin Pop
    @return: the list of all MACs
646 c41eea6e Iustin Pop

647 a8083063 Iustin Pop
    """
648 a8083063 Iustin Pop
    result = []
649 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
650 a8083063 Iustin Pop
      for nic in instance.nics:
651 a8083063 Iustin Pop
        result.append(nic.mac)
652 a8083063 Iustin Pop
653 a8083063 Iustin Pop
    return result
654 a8083063 Iustin Pop
655 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
656 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
657 f9518d38 Iustin Pop

658 c41eea6e Iustin Pop
    @rtype: list
659 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
660 c41eea6e Iustin Pop

661 f9518d38 Iustin Pop
    """
662 f9518d38 Iustin Pop
    def helper(disk, result):
663 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
664 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
665 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
666 f9518d38 Iustin Pop
      if disk.children:
667 f9518d38 Iustin Pop
        for child in disk.children:
668 f9518d38 Iustin Pop
          helper(child, result)
669 f9518d38 Iustin Pop
670 f9518d38 Iustin Pop
    result = []
671 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
672 f9518d38 Iustin Pop
      for disk in instance.disks:
673 f9518d38 Iustin Pop
        helper(disk, result)
674 f9518d38 Iustin Pop
675 f9518d38 Iustin Pop
    return result
676 f9518d38 Iustin Pop
677 a57e502a Thomas Thrainer
  def _CheckDiskIDs(self, disk, l_ids):
678 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
679 4b98ac29 Iustin Pop

680 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
681 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
682 4b98ac29 Iustin Pop
    @type l_ids: list
683 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
684 4b98ac29 Iustin Pop
    @rtype: list
685 4b98ac29 Iustin Pop
    @return: a list of error messages
686 4b98ac29 Iustin Pop

687 4b98ac29 Iustin Pop
    """
688 4b98ac29 Iustin Pop
    result = []
689 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
690 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
691 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
692 25ae22e4 Iustin Pop
      else:
693 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
694 4b98ac29 Iustin Pop
695 4b98ac29 Iustin Pop
    if disk.children:
696 4b98ac29 Iustin Pop
      for child in disk.children:
697 a57e502a Thomas Thrainer
        result.extend(self._CheckDiskIDs(child, l_ids))
698 4b98ac29 Iustin Pop
    return result
699 4b98ac29 Iustin Pop
700 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
701 a8efbb40 Iustin Pop
    """Verify function.
702 a8efbb40 Iustin Pop

703 4a89c54a Iustin Pop
    @rtype: list
704 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
705 4a89c54a Iustin Pop
        configuration errors
706 4a89c54a Iustin Pop

707 a8083063 Iustin Pop
    """
708 b459a848 Andrea Spadaccini
    # pylint: disable=R0914
709 a8083063 Iustin Pop
    result = []
710 a8083063 Iustin Pop
    seen_macs = []
711 48ce9fd9 Iustin Pop
    ports = {}
712 a8083063 Iustin Pop
    data = self._config_data
713 7e01d204 Iustin Pop
    cluster = data.cluster
714 4b98ac29 Iustin Pop
    seen_lids = []
715 9a5fba23 Guido Trotter
716 9a5fba23 Guido Trotter
    # global cluster checks
717 7e01d204 Iustin Pop
    if not cluster.enabled_hypervisors:
718 9a5fba23 Guido Trotter
      result.append("enabled hypervisors list doesn't have any entries")
719 7e01d204 Iustin Pop
    invalid_hvs = set(cluster.enabled_hypervisors) - constants.HYPER_TYPES
720 9a5fba23 Guido Trotter
    if invalid_hvs:
721 9a5fba23 Guido Trotter
      result.append("enabled hypervisors contains invalid entries: %s" %
722 3da6e141 Helga Velroyen
                    utils.CommaJoin(invalid_hvs))
723 7e01d204 Iustin Pop
    missing_hvp = (set(cluster.enabled_hypervisors) -
724 7e01d204 Iustin Pop
                   set(cluster.hvparams.keys()))
725 9f3ac970 Iustin Pop
    if missing_hvp:
726 9f3ac970 Iustin Pop
      result.append("hypervisor parameters missing for the enabled"
727 9f3ac970 Iustin Pop
                    " hypervisor(s) %s" % utils.CommaJoin(missing_hvp))
728 9a5fba23 Guido Trotter
729 3da6e141 Helga Velroyen
    if not cluster.enabled_disk_templates:
730 3da6e141 Helga Velroyen
      result.append("enabled disk templates list doesn't have any entries")
731 3da6e141 Helga Velroyen
    invalid_disk_templates = set(cluster.enabled_disk_templates) \
732 3da6e141 Helga Velroyen
                               - constants.DISK_TEMPLATES
733 3da6e141 Helga Velroyen
    if invalid_disk_templates:
734 3da6e141 Helga Velroyen
      result.append("enabled disk templates list contains invalid entries:"
735 3da6e141 Helga Velroyen
                    " %s" % utils.CommaJoin(invalid_disk_templates))
736 3da6e141 Helga Velroyen
737 7e01d204 Iustin Pop
    if cluster.master_node not in data.nodes:
738 9a5fba23 Guido Trotter
      result.append("cluster has invalid primary node '%s'" %
739 7e01d204 Iustin Pop
                    cluster.master_node)
740 9a5fba23 Guido Trotter
741 26f2fd8d Iustin Pop
    def _helper(owner, attr, value, template):
742 26f2fd8d Iustin Pop
      try:
743 26f2fd8d Iustin Pop
        utils.ForceDictType(value, template)
744 26f2fd8d Iustin Pop
      except errors.GenericError, err:
745 26f2fd8d Iustin Pop
        result.append("%s has invalid %s: %s" % (owner, attr, err))
746 26f2fd8d Iustin Pop
747 26f2fd8d Iustin Pop
    def _helper_nic(owner, params):
748 26f2fd8d Iustin Pop
      try:
749 26f2fd8d Iustin Pop
        objects.NIC.CheckParameterSyntax(params)
750 26f2fd8d Iustin Pop
      except errors.ConfigurationError, err:
751 26f2fd8d Iustin Pop
        result.append("%s has invalid nicparams: %s" % (owner, err))
752 26f2fd8d Iustin Pop
753 da5f09ef Bernardo Dal Seno
    def _helper_ipolicy(owner, ipolicy, iscluster):
754 918eb80b Agata Murawska
      try:
755 da5f09ef Bernardo Dal Seno
        objects.InstancePolicy.CheckParameterSyntax(ipolicy, iscluster)
756 918eb80b Agata Murawska
      except errors.ConfigurationError, err:
757 918eb80b Agata Murawska
        result.append("%s has invalid instance policy: %s" % (owner, err))
758 da5f09ef Bernardo Dal Seno
      for key, value in ipolicy.items():
759 da5f09ef Bernardo Dal Seno
        if key == constants.ISPECS_MINMAX:
760 41044e04 Bernardo Dal Seno
          for k in range(len(value)):
761 41044e04 Bernardo Dal Seno
            _helper_ispecs(owner, "ipolicy/%s[%s]" % (key, k), value[k])
762 da5f09ef Bernardo Dal Seno
        elif key == constants.ISPECS_STD:
763 da5f09ef Bernardo Dal Seno
          _helper(owner, "ipolicy/" + key, value,
764 da5f09ef Bernardo Dal Seno
                  constants.ISPECS_PARAMETER_TYPES)
765 2cc673a3 Iustin Pop
        else:
766 2cc673a3 Iustin Pop
          # FIXME: assuming list type
767 ff6c5e55 Iustin Pop
          if key in constants.IPOLICY_PARAMETERS:
768 ff6c5e55 Iustin Pop
            exp_type = float
769 ff6c5e55 Iustin Pop
          else:
770 ff6c5e55 Iustin Pop
            exp_type = list
771 ff6c5e55 Iustin Pop
          if not isinstance(value, exp_type):
772 2cc673a3 Iustin Pop
            result.append("%s has invalid instance policy: for %s,"
773 ff6c5e55 Iustin Pop
                          " expecting %s, got %s" %
774 ff6c5e55 Iustin Pop
                          (owner, key, exp_type.__name__, type(value)))
775 918eb80b Agata Murawska
776 da5f09ef Bernardo Dal Seno
    def _helper_ispecs(owner, parentkey, params):
777 da5f09ef Bernardo Dal Seno
      for (key, value) in params.items():
778 da5f09ef Bernardo Dal Seno
        fullkey = "/".join([parentkey, key])
779 da5f09ef Bernardo Dal Seno
        _helper(owner, fullkey, value, constants.ISPECS_PARAMETER_TYPES)
780 da5f09ef Bernardo Dal Seno
781 26f2fd8d Iustin Pop
    # check cluster parameters
782 26f2fd8d Iustin Pop
    _helper("cluster", "beparams", cluster.SimpleFillBE({}),
783 26f2fd8d Iustin Pop
            constants.BES_PARAMETER_TYPES)
784 26f2fd8d Iustin Pop
    _helper("cluster", "nicparams", cluster.SimpleFillNIC({}),
785 26f2fd8d Iustin Pop
            constants.NICS_PARAMETER_TYPES)
786 26f2fd8d Iustin Pop
    _helper_nic("cluster", cluster.SimpleFillNIC({}))
787 26f2fd8d Iustin Pop
    _helper("cluster", "ndparams", cluster.SimpleFillND({}),
788 26f2fd8d Iustin Pop
            constants.NDS_PARAMETER_TYPES)
789 da5f09ef Bernardo Dal Seno
    _helper_ipolicy("cluster", cluster.ipolicy, True)
790 26f2fd8d Iustin Pop
791 6488e5bc Santi Raffa
    for disk_template in cluster.diskparams:
792 6488e5bc Santi Raffa
      if disk_template not in constants.DTS_HAVE_ACCESS:
793 6488e5bc Santi Raffa
        continue
794 6488e5bc Santi Raffa
795 6488e5bc Santi Raffa
      access = cluster.diskparams[disk_template].get(constants.LDP_ACCESS,
796 6488e5bc Santi Raffa
                                                     constants.DISK_KERNELSPACE)
797 294254b1 Raffa Santi
      if access not in constants.DISK_VALID_ACCESS_MODES:
798 294254b1 Raffa Santi
        result.append(
799 294254b1 Raffa Santi
          "Invalid value of '%s:%s': '%s' (expected one of %s)" % (
800 6488e5bc Santi Raffa
            disk_template, constants.LDP_ACCESS, access,
801 294254b1 Raffa Santi
            utils.CommaJoin(constants.DISK_VALID_ACCESS_MODES)
802 294254b1 Raffa Santi
          )
803 294254b1 Raffa Santi
        )
804 294254b1 Raffa Santi
805 9a5fba23 Guido Trotter
    # per-instance checks
806 da4a52a3 Thomas Thrainer
    for instance_uuid in data.instances:
807 da4a52a3 Thomas Thrainer
      instance = data.instances[instance_uuid]
808 da4a52a3 Thomas Thrainer
      if instance.uuid != instance_uuid:
809 da4a52a3 Thomas Thrainer
        result.append("instance '%s' is indexed by wrong UUID '%s'" %
810 da4a52a3 Thomas Thrainer
                      (instance.name, instance_uuid))
811 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
812 8522ceeb Iustin Pop
        result.append("instance '%s' has invalid primary node '%s'" %
813 da4a52a3 Thomas Thrainer
                      (instance.name, instance.primary_node))
814 6ccce5d4 Ilias Tsitsimpis
      for snode in self._UnlockedGetInstanceSecondaryNodes(instance):
815 a8083063 Iustin Pop
        if snode not in data.nodes:
816 8522ceeb Iustin Pop
          result.append("instance '%s' has invalid secondary node '%s'" %
817 da4a52a3 Thomas Thrainer
                        (instance.name, snode))
818 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
819 a8083063 Iustin Pop
        if nic.mac in seen_macs:
820 8522ceeb Iustin Pop
          result.append("instance '%s' has NIC %d mac %s duplicate" %
821 da4a52a3 Thomas Thrainer
                        (instance.name, idx, nic.mac))
822 a8083063 Iustin Pop
        else:
823 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
824 26f2fd8d Iustin Pop
        if nic.nicparams:
825 26f2fd8d Iustin Pop
          filled = cluster.SimpleFillNIC(nic.nicparams)
826 26f2fd8d Iustin Pop
          owner = "instance %s nic %d" % (instance.name, idx)
827 26f2fd8d Iustin Pop
          _helper(owner, "nicparams",
828 26f2fd8d Iustin Pop
                  filled, constants.NICS_PARAMETER_TYPES)
829 26f2fd8d Iustin Pop
          _helper_nic(owner, filled)
830 26f2fd8d Iustin Pop
831 3da6e141 Helga Velroyen
      # disk template checks
832 3da6e141 Helga Velroyen
      if not instance.disk_template in data.cluster.enabled_disk_templates:
833 3da6e141 Helga Velroyen
        result.append("instance '%s' uses the disabled disk template '%s'." %
834 da4a52a3 Thomas Thrainer
                      (instance.name, instance.disk_template))
835 3da6e141 Helga Velroyen
836 26f2fd8d Iustin Pop
      # parameter checks
837 26f2fd8d Iustin Pop
      if instance.beparams:
838 26f2fd8d Iustin Pop
        _helper("instance %s" % instance.name, "beparams",
839 26f2fd8d Iustin Pop
                cluster.FillBE(instance), constants.BES_PARAMETER_TYPES)
840 48ce9fd9 Iustin Pop
841 48ce9fd9 Iustin Pop
      # gather the drbd ports for duplicate checks
842 e2569c1d Michael Hanselmann
      for (idx, dsk) in enumerate(instance.disks):
843 66a37e7a Helga Velroyen
        if dsk.dev_type in constants.DTS_DRBD:
844 48ce9fd9 Iustin Pop
          tcp_port = dsk.logical_id[2]
845 48ce9fd9 Iustin Pop
          if tcp_port not in ports:
846 48ce9fd9 Iustin Pop
            ports[tcp_port] = []
847 e2569c1d Michael Hanselmann
          ports[tcp_port].append((instance.name, "drbd disk %s" % idx))
848 48ce9fd9 Iustin Pop
      # gather network port reservation
849 48ce9fd9 Iustin Pop
      net_port = getattr(instance, "network_port", None)
850 48ce9fd9 Iustin Pop
      if net_port is not None:
851 48ce9fd9 Iustin Pop
        if net_port not in ports:
852 48ce9fd9 Iustin Pop
          ports[net_port] = []
853 48ce9fd9 Iustin Pop
        ports[net_port].append((instance.name, "network port"))
854 48ce9fd9 Iustin Pop
855 332d0e37 Iustin Pop
      # instance disk verify
856 332d0e37 Iustin Pop
      for idx, disk in enumerate(instance.disks):
857 332d0e37 Iustin Pop
        result.extend(["instance '%s' disk %d error: %s" %
858 332d0e37 Iustin Pop
                       (instance.name, idx, msg) for msg in disk.Verify()])
859 a57e502a Thomas Thrainer
        result.extend(self._CheckDiskIDs(disk, seen_lids))
860 332d0e37 Iustin Pop
861 82c54b5b Michael Hanselmann
      wrong_names = _CheckInstanceDiskIvNames(instance.disks)
862 82c54b5b Michael Hanselmann
      if wrong_names:
863 82c54b5b Michael Hanselmann
        tmp = "; ".join(("name of disk %s should be '%s', but is '%s'" %
864 82c54b5b Michael Hanselmann
                         (idx, exp_name, actual_name))
865 82c54b5b Michael Hanselmann
                        for (idx, exp_name, actual_name) in wrong_names)
866 82c54b5b Michael Hanselmann
867 82c54b5b Michael Hanselmann
        result.append("Instance '%s' has wrongly named disks: %s" %
868 82c54b5b Michael Hanselmann
                      (instance.name, tmp))
869 82c54b5b Michael Hanselmann
870 48ce9fd9 Iustin Pop
    # cluster-wide pool of free ports
871 7e01d204 Iustin Pop
    for free_port in cluster.tcpudp_port_pool:
872 48ce9fd9 Iustin Pop
      if free_port not in ports:
873 48ce9fd9 Iustin Pop
        ports[free_port] = []
874 48ce9fd9 Iustin Pop
      ports[free_port].append(("cluster", "port marked as free"))
875 48ce9fd9 Iustin Pop
876 48ce9fd9 Iustin Pop
    # compute tcp/udp duplicate ports
877 48ce9fd9 Iustin Pop
    keys = ports.keys()
878 48ce9fd9 Iustin Pop
    keys.sort()
879 48ce9fd9 Iustin Pop
    for pnum in keys:
880 48ce9fd9 Iustin Pop
      pdata = ports[pnum]
881 48ce9fd9 Iustin Pop
      if len(pdata) > 1:
882 1f864b60 Iustin Pop
        txt = utils.CommaJoin(["%s/%s" % val for val in pdata])
883 48ce9fd9 Iustin Pop
        result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
884 48ce9fd9 Iustin Pop
885 48ce9fd9 Iustin Pop
    # highest used tcp port check
886 48ce9fd9 Iustin Pop
    if keys:
887 7e01d204 Iustin Pop
      if keys[-1] > cluster.highest_used_port:
888 48ce9fd9 Iustin Pop
        result.append("Highest used port mismatch, saved %s, computed %s" %
889 7e01d204 Iustin Pop
                      (cluster.highest_used_port, keys[-1]))
890 a8efbb40 Iustin Pop
891 7e01d204 Iustin Pop
    if not data.nodes[cluster.master_node].master_candidate:
892 3a26773f Iustin Pop
      result.append("Master node is not a master candidate")
893 3a26773f Iustin Pop
894 4a89c54a Iustin Pop
    # master candidate checks
895 e623dbe3 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats()
896 ec0292f1 Iustin Pop
    if mc_now < mc_max:
897 ec0292f1 Iustin Pop
      result.append("Not enough master candidates: actual %d, target %d" %
898 ec0292f1 Iustin Pop
                    (mc_now, mc_max))
899 48ce9fd9 Iustin Pop
900 5bf07049 Iustin Pop
    # node checks
901 1c3231aa Thomas Thrainer
    for node_uuid, node in data.nodes.items():
902 1c3231aa Thomas Thrainer
      if node.uuid != node_uuid:
903 1c3231aa Thomas Thrainer
        result.append("Node '%s' is indexed by wrong UUID '%s'" %
904 1c3231aa Thomas Thrainer
                      (node.name, node_uuid))
905 5bf07049 Iustin Pop
      if [node.master_candidate, node.drained, node.offline].count(True) > 1:
906 5bf07049 Iustin Pop
        result.append("Node %s state is invalid: master_candidate=%s,"
907 5bf07049 Iustin Pop
                      " drain=%s, offline=%s" %
908 3d889a7d Michael Hanselmann
                      (node.name, node.master_candidate, node.drained,
909 5bf07049 Iustin Pop
                       node.offline))
910 26f2fd8d Iustin Pop
      if node.group not in data.nodegroups:
911 26f2fd8d Iustin Pop
        result.append("Node '%s' has invalid group '%s'" %
912 26f2fd8d Iustin Pop
                      (node.name, node.group))
913 26f2fd8d Iustin Pop
      else:
914 26f2fd8d Iustin Pop
        _helper("node %s" % node.name, "ndparams",
915 26f2fd8d Iustin Pop
                cluster.FillND(node, data.nodegroups[node.group]),
916 26f2fd8d Iustin Pop
                constants.NDS_PARAMETER_TYPES)
917 3697def0 Bernardo Dal Seno
      used_globals = constants.NDC_GLOBALS.intersection(node.ndparams)
918 3697def0 Bernardo Dal Seno
      if used_globals:
919 3697def0 Bernardo Dal Seno
        result.append("Node '%s' has some global parameters set: %s" %
920 3697def0 Bernardo Dal Seno
                      (node.name, utils.CommaJoin(used_globals)))
921 5bf07049 Iustin Pop
922 6520ba14 Guido Trotter
    # nodegroups checks
923 ace16501 Guido Trotter
    nodegroups_names = set()
924 6520ba14 Guido Trotter
    for nodegroup_uuid in data.nodegroups:
925 6520ba14 Guido Trotter
      nodegroup = data.nodegroups[nodegroup_uuid]
926 6520ba14 Guido Trotter
      if nodegroup.uuid != nodegroup_uuid:
927 913cc25e Adeodato Simo
        result.append("node group '%s' (uuid: '%s') indexed by wrong uuid '%s'"
928 6520ba14 Guido Trotter
                      % (nodegroup.name, nodegroup.uuid, nodegroup_uuid))
929 485ba212 Guido Trotter
      if utils.UUID_RE.match(nodegroup.name.lower()):
930 913cc25e Adeodato Simo
        result.append("node group '%s' (uuid: '%s') has uuid-like name" %
931 485ba212 Guido Trotter
                      (nodegroup.name, nodegroup.uuid))
932 ace16501 Guido Trotter
      if nodegroup.name in nodegroups_names:
933 913cc25e Adeodato Simo
        result.append("duplicate node group name '%s'" % nodegroup.name)
934 ace16501 Guido Trotter
      else:
935 ace16501 Guido Trotter
        nodegroups_names.add(nodegroup.name)
936 81e3ab4f Agata Murawska
      group_name = "group %s" % nodegroup.name
937 8b057218 René Nussbaumer
      _helper_ipolicy(group_name, cluster.SimpleFillIPolicy(nodegroup.ipolicy),
938 8b057218 René Nussbaumer
                      False)
939 26f2fd8d Iustin Pop
      if nodegroup.ndparams:
940 81e3ab4f Agata Murawska
        _helper(group_name, "ndparams",
941 26f2fd8d Iustin Pop
                cluster.SimpleFillND(nodegroup.ndparams),
942 26f2fd8d Iustin Pop
                constants.NDS_PARAMETER_TYPES)
943 26f2fd8d Iustin Pop
944 4a89c54a Iustin Pop
    # drbd minors check
945 1122eb25 Iustin Pop
    _, duplicates = self._UnlockedComputeDRBDMap()
946 4a89c54a Iustin Pop
    for node, minor, instance_a, instance_b in duplicates:
947 4a89c54a Iustin Pop
      result.append("DRBD minor %d on node %s is assigned twice to instances"
948 4a89c54a Iustin Pop
                    " %s and %s" % (minor, node, instance_a, instance_b))
949 4a89c54a Iustin Pop
950 0ce8f948 Iustin Pop
    # IP checks
951 7e01d204 Iustin Pop
    default_nicparams = cluster.nicparams[constants.PP_DEFAULT]
952 b8716596 Michael Hanselmann
    ips = {}
953 b8716596 Michael Hanselmann
954 b8716596 Michael Hanselmann
    def _AddIpAddress(ip, name):
955 b8716596 Michael Hanselmann
      ips.setdefault(ip, []).append(name)
956 b8716596 Michael Hanselmann
957 7e01d204 Iustin Pop
    _AddIpAddress(cluster.master_ip, "cluster_ip")
958 0ce8f948 Iustin Pop
959 0ce8f948 Iustin Pop
    for node in data.nodes.values():
960 b8716596 Michael Hanselmann
      _AddIpAddress(node.primary_ip, "node:%s/primary" % node.name)
961 0ce8f948 Iustin Pop
      if node.secondary_ip != node.primary_ip:
962 b8716596 Michael Hanselmann
        _AddIpAddress(node.secondary_ip, "node:%s/secondary" % node.name)
963 b8716596 Michael Hanselmann
964 b8716596 Michael Hanselmann
    for instance in data.instances.values():
965 b8716596 Michael Hanselmann
      for idx, nic in enumerate(instance.nics):
966 b8716596 Michael Hanselmann
        if nic.ip is None:
967 b8716596 Michael Hanselmann
          continue
968 b8716596 Michael Hanselmann
969 b8716596 Michael Hanselmann
        nicparams = objects.FillDict(default_nicparams, nic.nicparams)
970 b8716596 Michael Hanselmann
        nic_mode = nicparams[constants.NIC_MODE]
971 b8716596 Michael Hanselmann
        nic_link = nicparams[constants.NIC_LINK]
972 b8716596 Michael Hanselmann
973 b8716596 Michael Hanselmann
        if nic_mode == constants.NIC_MODE_BRIDGED:
974 b8716596 Michael Hanselmann
          link = "bridge:%s" % nic_link
975 b8716596 Michael Hanselmann
        elif nic_mode == constants.NIC_MODE_ROUTED:
976 b8716596 Michael Hanselmann
          link = "route:%s" % nic_link
977 b8716596 Michael Hanselmann
        else:
978 b8716596 Michael Hanselmann
          raise errors.ProgrammerError("NIC mode '%s' not handled" % nic_mode)
979 b8716596 Michael Hanselmann
980 ad4a9ae7 Dimitris Aragiorgis
        _AddIpAddress("%s/%s/%s" % (link, nic.ip, nic.network),
981 b8716596 Michael Hanselmann
                      "instance:%s/nic:%d" % (instance.name, idx))
982 0ce8f948 Iustin Pop
983 0ce8f948 Iustin Pop
    for ip, owners in ips.items():
984 0ce8f948 Iustin Pop
      if len(owners) > 1:
985 0ce8f948 Iustin Pop
        result.append("IP address %s is used by multiple owners: %s" %
986 1f864b60 Iustin Pop
                      (ip, utils.CommaJoin(owners)))
987 b8716596 Michael Hanselmann
988 a8083063 Iustin Pop
    return result
989 a8083063 Iustin Pop
990 4a89c54a Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
991 4a89c54a Iustin Pop
  def VerifyConfig(self):
992 4a89c54a Iustin Pop
    """Verify function.
993 4a89c54a Iustin Pop

994 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
995 4a89c54a Iustin Pop

996 4a89c54a Iustin Pop
    @rtype: list
997 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
998 4a89c54a Iustin Pop
        configuration errors
999 4a89c54a Iustin Pop

1000 4a89c54a Iustin Pop
    """
1001 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
1002 4a89c54a Iustin Pop
1003 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1004 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
1005 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
1006 b2fddf63 Iustin Pop

1007 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
1008 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
1009 3b3b1bca Dimitris Aragiorgis
        configuration is stable
1010 3b3b1bca Dimitris Aragiorgis

1011 b2fddf63 Iustin Pop
    """
1012 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
1013 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
1014 264bb3c5 Michael Hanselmann
1015 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
1016 264bb3c5 Michael Hanselmann
1017 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1018 b2fddf63 Iustin Pop
  def GetPortList(self):
1019 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
1020 264bb3c5 Michael Hanselmann

1021 264bb3c5 Michael Hanselmann
    """
1022 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
1023 264bb3c5 Michael Hanselmann
1024 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1025 a8083063 Iustin Pop
  def AllocatePort(self):
1026 a8083063 Iustin Pop
    """Allocate a port.
1027 a8083063 Iustin Pop

1028 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
1029 b2fddf63 Iustin Pop
    default port range (and in this case we increase
1030 b2fddf63 Iustin Pop
    highest_used_port).
1031 a8083063 Iustin Pop

1032 a8083063 Iustin Pop
    """
1033 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
1034 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
1035 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
1036 264bb3c5 Michael Hanselmann
    else:
1037 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
1038 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
1039 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
1040 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
1041 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
1042 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
1043 a8083063 Iustin Pop
1044 a8083063 Iustin Pop
    self._WriteConfig()
1045 a8083063 Iustin Pop
    return port
1046 a8083063 Iustin Pop
1047 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
1048 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
1049 a81c53c9 Iustin Pop

1050 4a89c54a Iustin Pop
    @rtype: (dict, list)
1051 da4a52a3 Thomas Thrainer
    @return: dictionary of node_uuid: dict of minor: instance_uuid;
1052 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
1053 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
1054 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
1055 4a89c54a Iustin Pop
        should raise an exception
1056 a81c53c9 Iustin Pop

1057 a81c53c9 Iustin Pop
    """
1058 da4a52a3 Thomas Thrainer
    def _AppendUsedMinors(get_node_name_fn, instance, disk, used):
1059 4a89c54a Iustin Pop
      duplicates = []
1060 cd3b4ff4 Helga Velroyen
      if disk.dev_type == constants.DT_DRBD8 and len(disk.logical_id) >= 5:
1061 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
1062 da4a52a3 Thomas Thrainer
        for node_uuid, minor in ((node_a, minor_a), (node_b, minor_b)):
1063 b691385f Thomas Thrainer
          assert node_uuid in used, \
1064 b691385f Thomas Thrainer
            ("Node '%s' of instance '%s' not found in node list" %
1065 da4a52a3 Thomas Thrainer
             (get_node_name_fn(node_uuid), instance.name))
1066 da4a52a3 Thomas Thrainer
          if minor in used[node_uuid]:
1067 da4a52a3 Thomas Thrainer
            duplicates.append((node_uuid, minor, instance.uuid,
1068 da4a52a3 Thomas Thrainer
                               used[node_uuid][minor]))
1069 4a89c54a Iustin Pop
          else:
1070 da4a52a3 Thomas Thrainer
            used[node_uuid][minor] = instance.uuid
1071 a81c53c9 Iustin Pop
      if disk.children:
1072 a81c53c9 Iustin Pop
        for child in disk.children:
1073 da4a52a3 Thomas Thrainer
          duplicates.extend(_AppendUsedMinors(get_node_name_fn, instance, child,
1074 da4a52a3 Thomas Thrainer
                                              used))
1075 4a89c54a Iustin Pop
      return duplicates
1076 a81c53c9 Iustin Pop
1077 4a89c54a Iustin Pop
    duplicates = []
1078 da4a52a3 Thomas Thrainer
    my_dict = dict((node_uuid, {}) for node_uuid in self._config_data.nodes)
1079 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
1080 79b26a7a Iustin Pop
      for disk in instance.disks:
1081 da4a52a3 Thomas Thrainer
        duplicates.extend(_AppendUsedMinors(self._UnlockedGetNodeName,
1082 da4a52a3 Thomas Thrainer
                                            instance, disk, my_dict))
1083 da4a52a3 Thomas Thrainer
    for (node_uuid, minor), inst_uuid in self._temporary_drbds.iteritems():
1084 da4a52a3 Thomas Thrainer
      if minor in my_dict[node_uuid] and my_dict[node_uuid][minor] != inst_uuid:
1085 da4a52a3 Thomas Thrainer
        duplicates.append((node_uuid, minor, inst_uuid,
1086 da4a52a3 Thomas Thrainer
                           my_dict[node_uuid][minor]))
1087 4a89c54a Iustin Pop
      else:
1088 da4a52a3 Thomas Thrainer
        my_dict[node_uuid][minor] = inst_uuid
1089 4a89c54a Iustin Pop
    return my_dict, duplicates
1090 a81c53c9 Iustin Pop
1091 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
1092 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
1093 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
1094 6d2e83d5 Iustin Pop

1095 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
1096 6d2e83d5 Iustin Pop

1097 da4a52a3 Thomas Thrainer
    @return: dictionary of node_uuid: dict of minor: instance_uuid;
1098 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
1099 6d2e83d5 Iustin Pop
        an empty list).
1100 6d2e83d5 Iustin Pop

1101 6d2e83d5 Iustin Pop
    """
1102 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
1103 4a89c54a Iustin Pop
    if duplicates:
1104 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
1105 4a89c54a Iustin Pop
                                      str(duplicates))
1106 4a89c54a Iustin Pop
    return d_map
1107 6d2e83d5 Iustin Pop
1108 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
1109 da4a52a3 Thomas Thrainer
  def AllocateDRBDMinor(self, node_uuids, inst_uuid):
1110 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
1111 a81c53c9 Iustin Pop

1112 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
1113 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
1114 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
1115 a81c53c9 Iustin Pop
    order as the passed nodes.
1116 a81c53c9 Iustin Pop

1117 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1118 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which we allocate minors
1119 32388e6d Iustin Pop

1120 a81c53c9 Iustin Pop
    """
1121 da4a52a3 Thomas Thrainer
    assert isinstance(inst_uuid, basestring), \
1122 da4a52a3 Thomas Thrainer
           "Invalid argument '%s' passed to AllocateDRBDMinor" % inst_uuid
1123 32388e6d Iustin Pop
1124 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
1125 4a89c54a Iustin Pop
    if duplicates:
1126 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
1127 4a89c54a Iustin Pop
                                      str(duplicates))
1128 a81c53c9 Iustin Pop
    result = []
1129 1c3231aa Thomas Thrainer
    for nuuid in node_uuids:
1130 1c3231aa Thomas Thrainer
      ndata = d_map[nuuid]
1131 a81c53c9 Iustin Pop
      if not ndata:
1132 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
1133 a81c53c9 Iustin Pop
        result.append(0)
1134 da4a52a3 Thomas Thrainer
        ndata[0] = inst_uuid
1135 da4a52a3 Thomas Thrainer
        self._temporary_drbds[(nuuid, 0)] = inst_uuid
1136 a81c53c9 Iustin Pop
        continue
1137 a81c53c9 Iustin Pop
      keys = ndata.keys()
1138 a81c53c9 Iustin Pop
      keys.sort()
1139 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
1140 a81c53c9 Iustin Pop
      if ffree is None:
1141 a81c53c9 Iustin Pop
        # return the next minor
1142 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
1143 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
1144 a81c53c9 Iustin Pop
      else:
1145 a81c53c9 Iustin Pop
        minor = ffree
1146 4a89c54a Iustin Pop
      # double-check minor against current instances
1147 1c3231aa Thomas Thrainer
      assert minor not in d_map[nuuid], \
1148 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
1149 4a89c54a Iustin Pop
              " already allocated to instance %s" %
1150 1c3231aa Thomas Thrainer
              (minor, nuuid, d_map[nuuid][minor]))
1151 da4a52a3 Thomas Thrainer
      ndata[minor] = inst_uuid
1152 4a89c54a Iustin Pop
      # double-check minor against reservation
1153 1c3231aa Thomas Thrainer
      r_key = (nuuid, minor)
1154 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
1155 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
1156 4a89c54a Iustin Pop
              " reserved for instance %s" %
1157 1c3231aa Thomas Thrainer
              (minor, nuuid, self._temporary_drbds[r_key]))
1158 da4a52a3 Thomas Thrainer
      self._temporary_drbds[r_key] = inst_uuid
1159 4a89c54a Iustin Pop
      result.append(minor)
1160 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
1161 1c3231aa Thomas Thrainer
                  node_uuids, result)
1162 a81c53c9 Iustin Pop
    return result
1163 a81c53c9 Iustin Pop
1164 da4a52a3 Thomas Thrainer
  def _UnlockedReleaseDRBDMinors(self, inst_uuid):
1165 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
1166 a81c53c9 Iustin Pop

1167 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1168 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1169 da4a52a3 Thomas Thrainer
                      released
1170 a81c53c9 Iustin Pop

1171 a81c53c9 Iustin Pop
    """
1172 da4a52a3 Thomas Thrainer
    assert isinstance(inst_uuid, basestring), \
1173 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
1174 da4a52a3 Thomas Thrainer
    for key, uuid in self._temporary_drbds.items():
1175 da4a52a3 Thomas Thrainer
      if uuid == inst_uuid:
1176 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
1177 a81c53c9 Iustin Pop
1178 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
1179 da4a52a3 Thomas Thrainer
  def ReleaseDRBDMinors(self, inst_uuid):
1180 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
1181 61cf6b5e Iustin Pop

1182 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
1183 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
1184 61cf6b5e Iustin Pop
    functions.
1185 61cf6b5e Iustin Pop

1186 61cf6b5e Iustin Pop
    This function is just a wrapper over L{_UnlockedReleaseDRBDMinors}.
1187 61cf6b5e Iustin Pop

1188 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1189 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1190 da4a52a3 Thomas Thrainer
                      released
1191 61cf6b5e Iustin Pop

1192 61cf6b5e Iustin Pop
    """
1193 da4a52a3 Thomas Thrainer
    self._UnlockedReleaseDRBDMinors(inst_uuid)
1194 61cf6b5e Iustin Pop
1195 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1196 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
1197 4a8b186a Michael Hanselmann
    """Get the configuration version.
1198 4a8b186a Michael Hanselmann

1199 4a8b186a Michael Hanselmann
    @return: Config version
1200 4a8b186a Michael Hanselmann

1201 4a8b186a Michael Hanselmann
    """
1202 4a8b186a Michael Hanselmann
    return self._config_data.version
1203 4a8b186a Michael Hanselmann
1204 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1205 4a8b186a Michael Hanselmann
  def GetClusterName(self):
1206 4a8b186a Michael Hanselmann
    """Get cluster name.
1207 4a8b186a Michael Hanselmann

1208 4a8b186a Michael Hanselmann
    @return: Cluster name
1209 4a8b186a Michael Hanselmann

1210 4a8b186a Michael Hanselmann
    """
1211 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
1212 4a8b186a Michael Hanselmann
1213 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1214 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
1215 1c3231aa Thomas Thrainer
    """Get the UUID of the master node for this cluster.
1216 4a8b186a Michael Hanselmann

1217 1c3231aa Thomas Thrainer
    @return: Master node UUID
1218 4a8b186a Michael Hanselmann

1219 4a8b186a Michael Hanselmann
    """
1220 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
1221 4a8b186a Michael Hanselmann
1222 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1223 1c3231aa Thomas Thrainer
  def GetMasterNodeName(self):
1224 1c3231aa Thomas Thrainer
    """Get the hostname of the master node for this cluster.
1225 1c3231aa Thomas Thrainer

1226 1c3231aa Thomas Thrainer
    @return: Master node hostname
1227 1c3231aa Thomas Thrainer

1228 1c3231aa Thomas Thrainer
    """
1229 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(self._config_data.cluster.master_node)
1230 1c3231aa Thomas Thrainer
1231 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1232 b730e2a7 Thomas Thrainer
  def GetMasterNodeInfo(self):
1233 b730e2a7 Thomas Thrainer
    """Get the master node information for this cluster.
1234 b730e2a7 Thomas Thrainer

1235 b730e2a7 Thomas Thrainer
    @rtype: objects.Node
1236 b730e2a7 Thomas Thrainer
    @return: Master node L{objects.Node} object
1237 b730e2a7 Thomas Thrainer

1238 b730e2a7 Thomas Thrainer
    """
1239 b730e2a7 Thomas Thrainer
    return self._UnlockedGetNodeInfo(self._config_data.cluster.master_node)
1240 b730e2a7 Thomas Thrainer
1241 b730e2a7 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1242 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
1243 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
1244 4a8b186a Michael Hanselmann

1245 4a8b186a Michael Hanselmann
    @return: Master IP
1246 4a8b186a Michael Hanselmann

1247 4a8b186a Michael Hanselmann
    """
1248 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
1249 4a8b186a Michael Hanselmann
1250 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1251 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
1252 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
1253 4a8b186a Michael Hanselmann

1254 4a8b186a Michael Hanselmann
    """
1255 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
1256 4a8b186a Michael Hanselmann
1257 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1258 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
1259 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
1260 5a8648eb Andrea Spadaccini

1261 5a8648eb Andrea Spadaccini
    """
1262 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
1263 5a8648eb Andrea Spadaccini
1264 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1265 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
1266 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
1267 33be7576 Andrea Spadaccini

1268 33be7576 Andrea Spadaccini
    """
1269 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
1270 33be7576 Andrea Spadaccini
1271 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1272 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
1273 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
1274 4a8b186a Michael Hanselmann

1275 4a8b186a Michael Hanselmann
    """
1276 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
1277 4a8b186a Michael Hanselmann
1278 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1279 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
1280 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
1281 4b97f902 Apollon Oikonomopoulos

1282 4b97f902 Apollon Oikonomopoulos
    """
1283 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1284 4b97f902 Apollon Oikonomopoulos
1285 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1286 d3e6fd0e Santi Raffa
  def GetGlusterStorageDir(self):
1287 d3e6fd0e Santi Raffa
    """Get the Gluster storage dir for this cluster.
1288 d3e6fd0e Santi Raffa

1289 d3e6fd0e Santi Raffa
    """
1290 d3e6fd0e Santi Raffa
    return self._config_data.cluster.gluster_storage_dir
1291 d3e6fd0e Santi Raffa
1292 d3e6fd0e Santi Raffa
  @locking.ssynchronized(_config_lock, shared=1)
1293 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1294 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1295 4a8b186a Michael Hanselmann

1296 4a8b186a Michael Hanselmann
    """
1297 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
1298 4a8b186a Michael Hanselmann
1299 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1300 a9542a4f Thomas Thrainer
  def GetRsaHostKey(self):
1301 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1302 a8083063 Iustin Pop

1303 c41eea6e Iustin Pop
    @rtype: string
1304 c41eea6e Iustin Pop
    @return: the rsa hostkey
1305 a8083063 Iustin Pop

1306 a8083063 Iustin Pop
    """
1307 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1308 a8083063 Iustin Pop
1309 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1310 a9542a4f Thomas Thrainer
  def GetDsaHostKey(self):
1311 a9542a4f Thomas Thrainer
    """Return the dsa hostkey from the config.
1312 a9542a4f Thomas Thrainer

1313 a9542a4f Thomas Thrainer
    @rtype: string
1314 a9542a4f Thomas Thrainer
    @return: the dsa hostkey
1315 a9542a4f Thomas Thrainer

1316 a9542a4f Thomas Thrainer
    """
1317 a9542a4f Thomas Thrainer
    return self._config_data.cluster.dsahostkeypub
1318 a9542a4f Thomas Thrainer
1319 a9542a4f Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1320 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1321 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1322 bf4af505 Apollon Oikonomopoulos

1323 bf4af505 Apollon Oikonomopoulos
    """
1324 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1325 bf4af505 Apollon Oikonomopoulos
1326 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1327 0359e5d0 Spyros Trigazis
  def GetDefaultIAllocatorParameters(self):
1328 0359e5d0 Spyros Trigazis
    """Get the default instance allocator parameters for this cluster.
1329 0359e5d0 Spyros Trigazis

1330 0359e5d0 Spyros Trigazis
    @rtype: dict
1331 0359e5d0 Spyros Trigazis
    @return: dict of iallocator parameters
1332 0359e5d0 Spyros Trigazis

1333 0359e5d0 Spyros Trigazis
    """
1334 0359e5d0 Spyros Trigazis
    return self._config_data.cluster.default_iallocator_params
1335 0359e5d0 Spyros Trigazis
1336 0359e5d0 Spyros Trigazis
  @locking.ssynchronized(_config_lock, shared=1)
1337 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1338 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1339 868a98ca Manuel Franceschini

1340 868a98ca Manuel Franceschini
    @return: primary ip family
1341 868a98ca Manuel Franceschini

1342 868a98ca Manuel Franceschini
    """
1343 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1344 868a98ca Manuel Franceschini
1345 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1346 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1347 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1348 c9f4b8e6 Andrea Spadaccini

1349 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1350 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1351 c9f4b8e6 Andrea Spadaccini

1352 c9f4b8e6 Andrea Spadaccini
    """
1353 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1354 5ae4945a Iustin Pop
    result = objects.MasterNetworkParameters(
1355 1c3231aa Thomas Thrainer
      uuid=cluster.master_node, ip=cluster.master_ip,
1356 5ae4945a Iustin Pop
      netmask=cluster.master_netmask, netdev=cluster.master_netdev,
1357 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1358 c9f4b8e6 Andrea Spadaccini
1359 f9d20654 Andrea Spadaccini
    return result
1360 f9d20654 Andrea Spadaccini
1361 9a94cee3 Jose A. Lopes
  @locking.ssynchronized(_config_lock, shared=1)
1362 9a94cee3 Jose A. Lopes
  def GetInstanceCommunicationNetwork(self):
1363 9a94cee3 Jose A. Lopes
    """Get cluster instance communication network
1364 9a94cee3 Jose A. Lopes

1365 9a94cee3 Jose A. Lopes
    @rtype: string
1366 9a94cee3 Jose A. Lopes
    @return: instance communication network, which is the name of the
1367 9a94cee3 Jose A. Lopes
             network used for instance communication
1368 9a94cee3 Jose A. Lopes

1369 9a94cee3 Jose A. Lopes
    """
1370 9a94cee3 Jose A. Lopes
    return self._config_data.cluster.instance_communication_network
1371 9a94cee3 Jose A. Lopes
1372 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1373 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1374 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1375 e11a1b77 Adeodato Simo

1376 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1377 90e99856 Adeodato Simo
    according to their default values.
1378 90e99856 Adeodato Simo

1379 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1380 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1381 e11a1b77 Adeodato Simo
    @type ec_id: string
1382 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1383 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1384 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1385 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1386 e11a1b77 Adeodato Simo
                       configuration already
1387 e11a1b77 Adeodato Simo

1388 e11a1b77 Adeodato Simo
    """
1389 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1390 e11a1b77 Adeodato Simo
    self._WriteConfig()
1391 e11a1b77 Adeodato Simo
1392 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1393 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1394 e11a1b77 Adeodato Simo

1395 e11a1b77 Adeodato Simo
    """
1396 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
1397 e11a1b77 Adeodato Simo
1398 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
1399 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
1400 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
1401 e11a1b77 Adeodato Simo
    if check_uuid:
1402 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
1403 e11a1b77 Adeodato Simo
1404 18ffc0fe Stephen Shirley
    try:
1405 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
1406 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
1407 18ffc0fe Stephen Shirley
      pass
1408 18ffc0fe Stephen Shirley
    else:
1409 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
1410 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
1411 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
1412 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
1413 18ffc0fe Stephen Shirley
1414 e11a1b77 Adeodato Simo
    group.serial_no = 1
1415 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
1416 90e99856 Adeodato Simo
    group.UpgradeConfig()
1417 e11a1b77 Adeodato Simo
1418 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
1419 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1420 e11a1b77 Adeodato Simo
1421 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1422 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
1423 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
1424 e11a1b77 Adeodato Simo

1425 e11a1b77 Adeodato Simo
    @type group_uuid: string
1426 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1427 e11a1b77 Adeodato Simo

1428 e11a1b77 Adeodato Simo
    """
1429 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1430 e11a1b77 Adeodato Simo
1431 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1432 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1433 e11a1b77 Adeodato Simo
1434 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1435 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1436 0389c42a Stephen Shirley
1437 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1438 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1439 e11a1b77 Adeodato Simo
    self._WriteConfig()
1440 e11a1b77 Adeodato Simo
1441 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1442 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1443 eaa98a04 Guido Trotter

1444 eaa98a04 Guido Trotter
    @type target: string or None
1445 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1446 eaa98a04 Guido Trotter
    @rtype: string
1447 412b3531 Guido Trotter
    @return: nodegroup UUID
1448 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1449 eaa98a04 Guido Trotter

1450 eaa98a04 Guido Trotter
    """
1451 eaa98a04 Guido Trotter
    if target is None:
1452 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1453 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1454 2ed0e208 Iustin Pop
                                   " group must be specified explicitly.")
1455 eaa98a04 Guido Trotter
      else:
1456 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1457 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1458 eaa98a04 Guido Trotter
      return target
1459 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1460 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1461 eaa98a04 Guido Trotter
        return nodegroup.uuid
1462 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1463 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1464 eaa98a04 Guido Trotter
1465 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1466 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1467 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1468 e85d8982 Stephen Shirley

1469 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1470 e85d8982 Stephen Shirley

1471 e85d8982 Stephen Shirley
    @type target: string or None
1472 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1473 e85d8982 Stephen Shirley
    @rtype: string
1474 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1475 e85d8982 Stephen Shirley

1476 e85d8982 Stephen Shirley
    """
1477 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1478 e85d8982 Stephen Shirley
1479 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1480 648e4196 Guido Trotter
    """Lookup a node group.
1481 648e4196 Guido Trotter

1482 648e4196 Guido Trotter
    @type uuid: string
1483 648e4196 Guido Trotter
    @param uuid: group UUID
1484 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1485 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1486 648e4196 Guido Trotter

1487 648e4196 Guido Trotter
    """
1488 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1489 648e4196 Guido Trotter
      return None
1490 648e4196 Guido Trotter
1491 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1492 648e4196 Guido Trotter
1493 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1494 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1495 5768e6a6 René Nussbaumer
    """Lookup a node group.
1496 5768e6a6 René Nussbaumer

1497 5768e6a6 René Nussbaumer
    @type uuid: string
1498 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1499 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1500 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1501 5768e6a6 René Nussbaumer

1502 5768e6a6 René Nussbaumer
    """
1503 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1504 5768e6a6 René Nussbaumer
1505 6b2a2942 Petr Pudlak
  def _UnlockedGetAllNodeGroupsInfo(self):
1506 6b2a2942 Petr Pudlak
    """Get the configuration of all node groups.
1507 6b2a2942 Petr Pudlak

1508 6b2a2942 Petr Pudlak
    """
1509 6b2a2942 Petr Pudlak
    return dict(self._config_data.nodegroups)
1510 6b2a2942 Petr Pudlak
1511 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1512 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1513 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1514 622444e5 Iustin Pop

1515 622444e5 Iustin Pop
    """
1516 6b2a2942 Petr Pudlak
    return self._UnlockedGetAllNodeGroupsInfo()
1517 622444e5 Iustin Pop
1518 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1519 a9f33339 Petr Pudlak
  def GetAllNodeGroupsInfoDict(self):
1520 a9f33339 Petr Pudlak
    """Get the configuration of all node groups expressed as a dictionary of
1521 a9f33339 Petr Pudlak
    dictionaries.
1522 a9f33339 Petr Pudlak

1523 a9f33339 Petr Pudlak
    """
1524 a9f33339 Petr Pudlak
    return dict(map(lambda (uuid, ng): (uuid, ng.ToDict()),
1525 a9f33339 Petr Pudlak
                    self._UnlockedGetAllNodeGroupsInfo().items()))
1526 a9f33339 Petr Pudlak
1527 a9f33339 Petr Pudlak
  @locking.ssynchronized(_config_lock, shared=1)
1528 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1529 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1530 1ac6f2ad Guido Trotter

1531 1ac6f2ad Guido Trotter
    """
1532 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1533 1ac6f2ad Guido Trotter
1534 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1535 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1536 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1537 dac81741 Michael Hanselmann

1538 dac81741 Michael Hanselmann
    """
1539 1c3231aa Thomas Thrainer
    ngfn = lambda node_uuid: self._UnlockedGetNodeInfo(node_uuid).group
1540 1c3231aa Thomas Thrainer
    return frozenset(member_uuid
1541 1c3231aa Thomas Thrainer
                     for node_uuid in nodes
1542 1c3231aa Thomas Thrainer
                     for member_uuid in
1543 1c3231aa Thomas Thrainer
                       self._UnlockedGetNodeGroup(ngfn(node_uuid)).members)
1544 dac81741 Michael Hanselmann
1545 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1546 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1547 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1548 080fbeea Michael Hanselmann

1549 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1550 080fbeea Michael Hanselmann
    @rtype: list
1551 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1552 080fbeea Michael Hanselmann

1553 080fbeea Michael Hanselmann
    """
1554 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1555 080fbeea Michael Hanselmann
1556 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1557 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1558 a8083063 Iustin Pop
    """Add an instance to the config.
1559 a8083063 Iustin Pop

1560 a8083063 Iustin Pop
    This should be used after creating a new instance.
1561 a8083063 Iustin Pop

1562 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1563 c41eea6e Iustin Pop
    @param instance: the instance object
1564 c41eea6e Iustin Pop

1565 a8083063 Iustin Pop
    """
1566 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1567 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1568 a8083063 Iustin Pop
1569 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1570 b6dd32db Ilias Tsitsimpis
      all_lvs = self._UnlockedGetInstanceLVsByNode(instance)
1571 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1572 923b1523 Iustin Pop
1573 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1574 e4640214 Guido Trotter
    for nic in instance.nics:
1575 e4640214 Guido Trotter
      if nic.mac in all_macs:
1576 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1577 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1578 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1579 430b923c Iustin Pop
1580 da4a52a3 Thomas Thrainer
    self._CheckUniqueUUID(instance, include_temporary=False)
1581 e4640214 Guido Trotter
1582 b989e85d Iustin Pop
    instance.serial_no = 1
1583 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1584 da4a52a3 Thomas Thrainer
    self._config_data.instances[instance.uuid] = instance
1585 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1586 da4a52a3 Thomas Thrainer
    self._UnlockedReleaseDRBDMinors(instance.uuid)
1587 e8e079f3 Dimitris Aragiorgis
    self._UnlockedCommitTemporaryIps(ec_id)
1588 a8083063 Iustin Pop
    self._WriteConfig()
1589 a8083063 Iustin Pop
1590 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1591 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1592 430b923c Iustin Pop

1593 430b923c Iustin Pop
    @param item: the instance or node to be checked
1594 0debfb35 Guido Trotter
    @param ec_id: the execution context id for the uuid reservation
1595 430b923c Iustin Pop

1596 430b923c Iustin Pop
    """
1597 430b923c Iustin Pop
    if not item.uuid:
1598 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1599 da4a52a3 Thomas Thrainer
    else:
1600 da4a52a3 Thomas Thrainer
      self._CheckUniqueUUID(item, include_temporary=True)
1601 da4a52a3 Thomas Thrainer
1602 da4a52a3 Thomas Thrainer
  def _CheckUniqueUUID(self, item, include_temporary):
1603 da4a52a3 Thomas Thrainer
    """Checks that the UUID of the given object is unique.
1604 da4a52a3 Thomas Thrainer

1605 da4a52a3 Thomas Thrainer
    @param item: the instance or node to be checked
1606 da4a52a3 Thomas Thrainer
    @param include_temporary: whether temporarily generated UUID's should be
1607 da4a52a3 Thomas Thrainer
              included in the check. If the UUID of the item to be checked is
1608 da4a52a3 Thomas Thrainer
              a temporarily generated one, this has to be C{False}.
1609 da4a52a3 Thomas Thrainer

1610 da4a52a3 Thomas Thrainer
    """
1611 da4a52a3 Thomas Thrainer
    if not item.uuid:
1612 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("'%s' must have an UUID" % (item.name,))
1613 da4a52a3 Thomas Thrainer
    if item.uuid in self._AllIDs(include_temporary=include_temporary):
1614 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1615 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1616 430b923c Iustin Pop
1617 da4a52a3 Thomas Thrainer
  def _SetInstanceStatus(self, inst_uuid, status, disks_active):
1618 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1619 a8083063 Iustin Pop

1620 a8083063 Iustin Pop
    """
1621 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1622 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1623 da4a52a3 Thomas Thrainer
                                      inst_uuid)
1624 da4a52a3 Thomas Thrainer
    instance = self._config_data.instances[inst_uuid]
1625 1d4a4b26 Thomas Thrainer
1626 1d4a4b26 Thomas Thrainer
    if status is None:
1627 1d4a4b26 Thomas Thrainer
      status = instance.admin_state
1628 1d4a4b26 Thomas Thrainer
    if disks_active is None:
1629 1d4a4b26 Thomas Thrainer
      disks_active = instance.disks_active
1630 1d4a4b26 Thomas Thrainer
1631 1d4a4b26 Thomas Thrainer
    assert status in constants.ADMINST_ALL, \
1632 1d4a4b26 Thomas Thrainer
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1633 1d4a4b26 Thomas Thrainer
1634 1d4a4b26 Thomas Thrainer
    if instance.admin_state != status or \
1635 1d4a4b26 Thomas Thrainer
       instance.disks_active != disks_active:
1636 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1637 1d4a4b26 Thomas Thrainer
      instance.disks_active = disks_active
1638 b989e85d Iustin Pop
      instance.serial_no += 1
1639 d693c864 Iustin Pop
      instance.mtime = time.time()
1640 455a3445 Iustin Pop
      self._WriteConfig()
1641 a8083063 Iustin Pop
1642 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1643 da4a52a3 Thomas Thrainer
  def MarkInstanceUp(self, inst_uuid):
1644 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1645 6a408fb2 Iustin Pop

1646 1d4a4b26 Thomas Thrainer
    This also sets the instance disks active flag.
1647 1d4a4b26 Thomas Thrainer

1648 6a408fb2 Iustin Pop
    """
1649 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_UP, True)
1650 57de31c0 Agata Murawska
1651 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1652 da4a52a3 Thomas Thrainer
  def MarkInstanceOffline(self, inst_uuid):
1653 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1654 57de31c0 Agata Murawska

1655 1d4a4b26 Thomas Thrainer
    This also clears the instance disks active flag.
1656 1d4a4b26 Thomas Thrainer

1657 57de31c0 Agata Murawska
    """
1658 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_OFFLINE, False)
1659 6a408fb2 Iustin Pop
1660 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1661 da4a52a3 Thomas Thrainer
  def RemoveInstance(self, inst_uuid):
1662 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1663 a8083063 Iustin Pop

1664 a8083063 Iustin Pop
    """
1665 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1666 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1667 f396ad8c Vangelis Koukis
1668 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1669 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1670 da4a52a3 Thomas Thrainer
    inst = self._config_data.instances[inst_uuid]
1671 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1672 f396ad8c Vangelis Koukis
    if network_port is not None:
1673 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1674 f396ad8c Vangelis Koukis
1675 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1676 ced51149 Dimitris Aragiorgis
1677 ced51149 Dimitris Aragiorgis
    for nic in instance.nics:
1678 9394f4d1 Dimitris Aragiorgis
      if nic.network and nic.ip:
1679 1b68f268 Helga Velroyen
        # Return all IP addresses to the respective address pools
1680 9394f4d1 Dimitris Aragiorgis
        self._UnlockedCommitIp(constants.RELEASE_ACTION, nic.network, nic.ip)
1681 ced51149 Dimitris Aragiorgis
1682 da4a52a3 Thomas Thrainer
    del self._config_data.instances[inst_uuid]
1683 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1684 a8083063 Iustin Pop
    self._WriteConfig()
1685 a8083063 Iustin Pop
1686 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1687 da4a52a3 Thomas Thrainer
  def RenameInstance(self, inst_uuid, new_name):
1688 fc95f88f Iustin Pop
    """Rename an instance.
1689 fc95f88f Iustin Pop

1690 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1691 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1692 fc95f88f Iustin Pop
    rename.
1693 fc95f88f Iustin Pop

1694 fc95f88f Iustin Pop
    """
1695 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1696 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1697 ea642319 Michael Hanselmann
1698 da4a52a3 Thomas Thrainer
    inst = self._config_data.instances[inst_uuid]
1699 fc95f88f Iustin Pop
    inst.name = new_name
1700 b23c4333 Manuel Franceschini
1701 9e14897d Klaus Aehlig
    for (_, disk) in enumerate(inst.disks):
1702 cd3b4ff4 Helga Velroyen
      if disk.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1703 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1704 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1705 ea642319 Michael Hanselmann
        disk.logical_id = (disk.logical_id[0],
1706 ea642319 Michael Hanselmann
                           utils.PathJoin(file_storage_dir, inst.name,
1707 9e14897d Klaus Aehlig
                                          os.path.basename(disk.logical_id[1])))
1708 ea642319 Michael Hanselmann
1709 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1710 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1711 1fc34c48 Michael Hanselmann
1712 fc95f88f Iustin Pop
    self._WriteConfig()
1713 fc95f88f Iustin Pop
1714 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1715 da4a52a3 Thomas Thrainer
  def MarkInstanceDown(self, inst_uuid):
1716 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1717 a8083063 Iustin Pop

1718 1d4a4b26 Thomas Thrainer
    This does not touch the instance disks active flag, as shut down instances
1719 1d4a4b26 Thomas Thrainer
    can still have active disks.
1720 1d4a4b26 Thomas Thrainer

1721 1d4a4b26 Thomas Thrainer
    """
1722 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None)
1723 1d4a4b26 Thomas Thrainer
1724 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1725 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksActive(self, inst_uuid):
1726 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks active.
1727 1d4a4b26 Thomas Thrainer

1728 1d4a4b26 Thomas Thrainer
    """
1729 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, True)
1730 1d4a4b26 Thomas Thrainer
1731 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1732 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksInactive(self, inst_uuid):
1733 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks inactive.
1734 1d4a4b26 Thomas Thrainer

1735 a8083063 Iustin Pop
    """
1736 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, False)
1737 a8083063 Iustin Pop
1738 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1739 94bbfece Iustin Pop
    """Get the list of instances.
1740 94bbfece Iustin Pop

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

1743 94bbfece Iustin Pop
    """
1744 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1745 94bbfece Iustin Pop
1746 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1747 a8083063 Iustin Pop
  def GetInstanceList(self):
1748 a8083063 Iustin Pop
    """Get the list of instances.
1749 a8083063 Iustin Pop

1750 da4a52a3 Thomas Thrainer
    @return: array of instances, ex. ['instance2-uuid', 'instance1-uuid']
1751 a8083063 Iustin Pop

1752 a8083063 Iustin Pop
    """
1753 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1754 a8083063 Iustin Pop
1755 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1756 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1757 a8083063 Iustin Pop

1758 a8083063 Iustin Pop
    """
1759 da4a52a3 Thomas Thrainer
    # Locking is done in L{ConfigWriter.GetAllInstancesInfo}
1760 da4a52a3 Thomas Thrainer
    all_insts = self.GetAllInstancesInfo().values()
1761 da4a52a3 Thomas Thrainer
    expanded_name = _MatchNameComponentIgnoreCase(
1762 da4a52a3 Thomas Thrainer
                      short_name, [inst.name for inst in all_insts])
1763 da4a52a3 Thomas Thrainer
1764 da4a52a3 Thomas Thrainer
    if expanded_name is not None:
1765 da4a52a3 Thomas Thrainer
      # there has to be exactly one instance with that name
1766 da4a52a3 Thomas Thrainer
      inst = (filter(lambda n: n.name == expanded_name, all_insts)[0])
1767 da4a52a3 Thomas Thrainer
      return (inst.uuid, inst.name)
1768 da4a52a3 Thomas Thrainer
    else:
1769 738436bf Thomas Thrainer
      return (None, None)
1770 a8083063 Iustin Pop
1771 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceInfo(self, inst_uuid):
1772 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1773 94bbfece Iustin Pop

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

1776 94bbfece Iustin Pop
    """
1777 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1778 94bbfece Iustin Pop
      return None
1779 94bbfece Iustin Pop
1780 da4a52a3 Thomas Thrainer
    return self._config_data.instances[inst_uuid]
1781 94bbfece Iustin Pop
1782 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1783 da4a52a3 Thomas Thrainer
  def GetInstanceInfo(self, inst_uuid):
1784 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1785 a8083063 Iustin Pop

1786 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1787 a8083063 Iustin Pop
    an instance are taken from the live systems.
1788 a8083063 Iustin Pop

1789 da4a52a3 Thomas Thrainer
    @param inst_uuid: UUID of the instance
1790 a8083063 Iustin Pop

1791 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1792 c41eea6e Iustin Pop
    @return: the instance object
1793 a8083063 Iustin Pop

1794 a8083063 Iustin Pop
    """
1795 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfo(inst_uuid)
1796 a8083063 Iustin Pop
1797 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1798 da4a52a3 Thomas Thrainer
  def GetInstanceNodeGroups(self, inst_uuid, primary_only=False):
1799 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1800 2674690b Michael Hanselmann

1801 2674690b Michael Hanselmann
    @rtype: frozenset
1802 2674690b Michael Hanselmann

1803 2674690b Michael Hanselmann
    """
1804 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1805 2674690b Michael Hanselmann
    if not instance:
1806 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1807 2674690b Michael Hanselmann
1808 2674690b Michael Hanselmann
    if primary_only:
1809 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1810 2674690b Michael Hanselmann
    else:
1811 4e7f986e Ilias Tsitsimpis
      nodes = self._UnlockedGetInstanceNodes(instance)
1812 2674690b Michael Hanselmann
1813 1c3231aa Thomas Thrainer
    return frozenset(self._UnlockedGetNodeInfo(node_uuid).group
1814 1c3231aa Thomas Thrainer
                     for node_uuid in nodes)
1815 2674690b Michael Hanselmann
1816 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1817 da4a52a3 Thomas Thrainer
  def GetInstanceNetworks(self, inst_uuid):
1818 922610c9 Dimitris Aragiorgis
    """Returns set of network UUIDs for instance's nics.
1819 922610c9 Dimitris Aragiorgis

1820 922610c9 Dimitris Aragiorgis
    @rtype: frozenset
1821 922610c9 Dimitris Aragiorgis

1822 922610c9 Dimitris Aragiorgis
    """
1823 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1824 922610c9 Dimitris Aragiorgis
    if not instance:
1825 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1826 922610c9 Dimitris Aragiorgis
1827 922610c9 Dimitris Aragiorgis
    networks = set()
1828 922610c9 Dimitris Aragiorgis
    for nic in instance.nics:
1829 922610c9 Dimitris Aragiorgis
      if nic.network:
1830 922610c9 Dimitris Aragiorgis
        networks.add(nic.network)
1831 922610c9 Dimitris Aragiorgis
1832 922610c9 Dimitris Aragiorgis
    return frozenset(networks)
1833 922610c9 Dimitris Aragiorgis
1834 922610c9 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
1835 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfo(self, inst_uuids):
1836 da4a52a3 Thomas Thrainer
    """Get the configuration of multiple instances.
1837 da4a52a3 Thomas Thrainer

1838 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs
1839 da4a52a3 Thomas Thrainer
    @rtype: list
1840 da4a52a3 Thomas Thrainer
    @return: list of tuples (instance UUID, instance_info), where
1841 da4a52a3 Thomas Thrainer
        instance_info is what would GetInstanceInfo return for the
1842 da4a52a3 Thomas Thrainer
        node, while keeping the original order
1843 da4a52a3 Thomas Thrainer

1844 da4a52a3 Thomas Thrainer
    """
1845 da4a52a3 Thomas Thrainer
    return [(uuid, self._UnlockedGetInstanceInfo(uuid)) for uuid in inst_uuids]
1846 da4a52a3 Thomas Thrainer
1847 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1848 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfoByName(self, inst_names):
1849 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1850 71333cb9 Iustin Pop

1851 da4a52a3 Thomas Thrainer
    @param inst_names: list of instance names
1852 71333cb9 Iustin Pop
    @rtype: list
1853 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1854 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1855 71333cb9 Iustin Pop
        node, while keeping the original order
1856 71333cb9 Iustin Pop

1857 71333cb9 Iustin Pop
    """
1858 da4a52a3 Thomas Thrainer
    result = []
1859 da4a52a3 Thomas Thrainer
    for name in inst_names:
1860 da4a52a3 Thomas Thrainer
      instance = self._UnlockedGetInstanceInfoByName(name)
1861 da4a52a3 Thomas Thrainer
      result.append((instance.uuid, instance))
1862 da4a52a3 Thomas Thrainer
    return result
1863 71333cb9 Iustin Pop
1864 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1865 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1866 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1867 0b2de758 Iustin Pop

1868 0b2de758 Iustin Pop
    @rtype: dict
1869 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1870 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1871 0b2de758 Iustin Pop

1872 0b2de758 Iustin Pop
    """
1873 da4a52a3 Thomas Thrainer
    return self._UnlockedGetAllInstancesInfo()
1874 da4a52a3 Thomas Thrainer
1875 da4a52a3 Thomas Thrainer
  def _UnlockedGetAllInstancesInfo(self):
1876 da4a52a3 Thomas Thrainer
    my_dict = dict([(inst_uuid, self._UnlockedGetInstanceInfo(inst_uuid))
1877 da4a52a3 Thomas Thrainer
                    for inst_uuid in self._UnlockedGetInstanceList()])
1878 0b2de758 Iustin Pop
    return my_dict
1879 0b2de758 Iustin Pop
1880 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1881 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1882 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1883 cc19798f Michael Hanselmann

1884 cc19798f Michael Hanselmann
    @type filter_fn: callable
1885 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1886 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1887 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1888 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1889 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1890 cc19798f Michael Hanselmann

1891 cc19798f Michael Hanselmann
    """
1892 da4a52a3 Thomas Thrainer
    return dict((uuid, inst)
1893 da4a52a3 Thomas Thrainer
                for (uuid, inst) in self._config_data.instances.items()
1894 cc19798f Michael Hanselmann
                if filter_fn(inst))
1895 cc19798f Michael Hanselmann
1896 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1897 da4a52a3 Thomas Thrainer
  def GetInstanceInfoByName(self, inst_name):
1898 da4a52a3 Thomas Thrainer
    """Get the L{objects.Instance} object for a named instance.
1899 da4a52a3 Thomas Thrainer

1900 da4a52a3 Thomas Thrainer
    @param inst_name: name of the instance to get information for
1901 da4a52a3 Thomas Thrainer
    @type inst_name: string
1902 da4a52a3 Thomas Thrainer
    @return: the corresponding L{objects.Instance} instance or None if no
1903 da4a52a3 Thomas Thrainer
          information is available
1904 da4a52a3 Thomas Thrainer

1905 da4a52a3 Thomas Thrainer
    """
1906 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfoByName(inst_name)
1907 da4a52a3 Thomas Thrainer
1908 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceInfoByName(self, inst_name):
1909 da4a52a3 Thomas Thrainer
    for inst in self._UnlockedGetAllInstancesInfo().values():
1910 da4a52a3 Thomas Thrainer
      if inst.name == inst_name:
1911 da4a52a3 Thomas Thrainer
        return inst
1912 da4a52a3 Thomas Thrainer
    return None
1913 da4a52a3 Thomas Thrainer
1914 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceName(self, inst_uuid):
1915 da4a52a3 Thomas Thrainer
    inst_info = self._UnlockedGetInstanceInfo(inst_uuid)
1916 da4a52a3 Thomas Thrainer
    if inst_info is None:
1917 da4a52a3 Thomas Thrainer
      raise errors.OpExecError("Unknown instance: %s" % inst_uuid)
1918 da4a52a3 Thomas Thrainer
    return inst_info.name
1919 da4a52a3 Thomas Thrainer
1920 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1921 da4a52a3 Thomas Thrainer
  def GetInstanceName(self, inst_uuid):
1922 da4a52a3 Thomas Thrainer
    """Gets the instance name for the passed instance.
1923 da4a52a3 Thomas Thrainer

1924 da4a52a3 Thomas Thrainer
    @param inst_uuid: instance UUID to get name for
1925 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1926 da4a52a3 Thomas Thrainer
    @rtype: string
1927 da4a52a3 Thomas Thrainer
    @return: instance name
1928 da4a52a3 Thomas Thrainer

1929 da4a52a3 Thomas Thrainer
    """
1930 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceName(inst_uuid)
1931 da4a52a3 Thomas Thrainer
1932 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1933 da4a52a3 Thomas Thrainer
  def GetInstanceNames(self, inst_uuids):
1934 da4a52a3 Thomas Thrainer
    """Gets the instance names for the passed list of nodes.
1935 da4a52a3 Thomas Thrainer

1936 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs to get names for
1937 da4a52a3 Thomas Thrainer
    @type inst_uuids: list of strings
1938 da4a52a3 Thomas Thrainer
    @rtype: list of strings
1939 da4a52a3 Thomas Thrainer
    @return: list of instance names
1940 da4a52a3 Thomas Thrainer

1941 da4a52a3 Thomas Thrainer
    """
1942 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceNames(inst_uuids)
1943 da4a52a3 Thomas Thrainer
1944 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceNames(self, inst_uuids):
1945 da4a52a3 Thomas Thrainer
    return [self._UnlockedGetInstanceName(uuid) for uuid in inst_uuids]
1946 da4a52a3 Thomas Thrainer
1947 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1948 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1949 a8083063 Iustin Pop
    """Add a node to the configuration.
1950 a8083063 Iustin Pop

1951 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1952 c41eea6e Iustin Pop
    @param node: a Node instance
1953 a8083063 Iustin Pop

1954 a8083063 Iustin Pop
    """
1955 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1956 d8470559 Michael Hanselmann
1957 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1958 430b923c Iustin Pop
1959 b989e85d Iustin Pop
    node.serial_no = 1
1960 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1961 1c3231aa Thomas Thrainer
    self._UnlockedAddNodeToGroup(node.uuid, node.group)
1962 1c3231aa Thomas Thrainer
    self._config_data.nodes[node.uuid] = node
1963 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1964 a8083063 Iustin Pop
    self._WriteConfig()
1965 a8083063 Iustin Pop
1966 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1967 1c3231aa Thomas Thrainer
  def RemoveNode(self, node_uuid):
1968 a8083063 Iustin Pop
    """Remove a node from the configuration.
1969 a8083063 Iustin Pop

1970 a8083063 Iustin Pop
    """
1971 1c3231aa Thomas Thrainer
    logging.info("Removing node %s from configuration", node_uuid)
1972 d8470559 Michael Hanselmann
1973 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
1974 1c3231aa Thomas Thrainer
      raise errors.ConfigurationError("Unknown node '%s'" % node_uuid)
1975 a8083063 Iustin Pop
1976 1c3231aa Thomas Thrainer
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_uuid])
1977 1c3231aa Thomas Thrainer
    del self._config_data.nodes[node_uuid]
1978 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1979 a8083063 Iustin Pop
    self._WriteConfig()
1980 a8083063 Iustin Pop
1981 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1982 1c3231aa Thomas Thrainer
    """Attempt to expand an incomplete node name into a node UUID.
1983 a8083063 Iustin Pop

1984 a8083063 Iustin Pop
    """
1985 1c3231aa Thomas Thrainer
    # Locking is done in L{ConfigWriter.GetAllNodesInfo}
1986 1c3231aa Thomas Thrainer
    all_nodes = self.GetAllNodesInfo().values()
1987 1c3231aa Thomas Thrainer
    expanded_name = _MatchNameComponentIgnoreCase(
1988 1c3231aa Thomas Thrainer
                      short_name, [node.name for node in all_nodes])
1989 a8083063 Iustin Pop
1990 1c3231aa Thomas Thrainer
    if expanded_name is not None:
1991 da4a52a3 Thomas Thrainer
      # there has to be exactly one node with that name
1992 1c3231aa Thomas Thrainer
      node = (filter(lambda n: n.name == expanded_name, all_nodes)[0])
1993 1c3231aa Thomas Thrainer
      return (node.uuid, node.name)
1994 1c3231aa Thomas Thrainer
    else:
1995 738436bf Thomas Thrainer
      return (None, None)
1996 1c3231aa Thomas Thrainer
1997 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfo(self, node_uuid):
1998 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1999 a8083063 Iustin Pop

2000 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
2001 c41eea6e Iustin Pop
    held.
2002 f78ede4e Guido Trotter

2003 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
2004 a8083063 Iustin Pop

2005 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
2006 c41eea6e Iustin Pop
    @return: the node object
2007 a8083063 Iustin Pop

2008 a8083063 Iustin Pop
    """
2009 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
2010 a8083063 Iustin Pop
      return None
2011 a8083063 Iustin Pop
2012 1c3231aa Thomas Thrainer
    return self._config_data.nodes[node_uuid]
2013 a8083063 Iustin Pop
2014 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2015 1c3231aa Thomas Thrainer
  def GetNodeInfo(self, node_uuid):
2016 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
2017 f78ede4e Guido Trotter

2018 c41eea6e Iustin Pop
    This is just a locked wrapper over L{_UnlockedGetNodeInfo}.
2019 f78ede4e Guido Trotter

2020 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
2021 c41eea6e Iustin Pop

2022 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
2023 c41eea6e Iustin Pop
    @return: the node object
2024 f78ede4e Guido Trotter

2025 f78ede4e Guido Trotter
    """
2026 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfo(node_uuid)
2027 f78ede4e Guido Trotter
2028 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2029 1c3231aa Thomas Thrainer
  def GetNodeInstances(self, node_uuid):
2030 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
2031 8bf9e9a5 Iustin Pop

2032 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
2033 8bf9e9a5 Iustin Pop

2034 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
2035 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
2036 8bf9e9a5 Iustin Pop

2037 8bf9e9a5 Iustin Pop
    """
2038 8bf9e9a5 Iustin Pop
    pri = []
2039 8bf9e9a5 Iustin Pop
    sec = []
2040 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
2041 1c3231aa Thomas Thrainer
      if inst.primary_node == node_uuid:
2042 da4a52a3 Thomas Thrainer
        pri.append(inst.uuid)
2043 6ccce5d4 Ilias Tsitsimpis
      if node_uuid in self._UnlockedGetInstanceSecondaryNodes(inst):
2044 da4a52a3 Thomas Thrainer
        sec.append(inst.uuid)
2045 8bf9e9a5 Iustin Pop
    return (pri, sec)
2046 8bf9e9a5 Iustin Pop
2047 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
2048 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
2049 c71b049c Michael Hanselmann
    """Get the instances of a node group.
2050 c71b049c Michael Hanselmann

2051 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
2052 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
2053 c71b049c Michael Hanselmann
    @rtype: frozenset
2054 da4a52a3 Thomas Thrainer
    @return: List of instance UUIDs in node group
2055 c71b049c Michael Hanselmann

2056 c71b049c Michael Hanselmann
    """
2057 c71b049c Michael Hanselmann
    if primary_only:
2058 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
2059 c71b049c Michael Hanselmann
    else:
2060 4e7f986e Ilias Tsitsimpis
      nodes_fn = self._UnlockedGetInstanceNodes
2061 c71b049c Michael Hanselmann
2062 da4a52a3 Thomas Thrainer
    return frozenset(inst.uuid
2063 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
2064 1c3231aa Thomas Thrainer
                     for node_uuid in nodes_fn(inst)
2065 1c3231aa Thomas Thrainer
                     if self._UnlockedGetNodeInfo(node_uuid).group == uuid)
2066 c71b049c Michael Hanselmann
2067 def6577f Helga Velroyen
  def _UnlockedGetHvparamsString(self, hvname):
2068 def6577f Helga Velroyen
    """Return the string representation of the list of hyervisor parameters of
2069 def6577f Helga Velroyen
    the given hypervisor.
2070 def6577f Helga Velroyen

2071 def6577f Helga Velroyen
    @see: C{GetHvparams}
2072 def6577f Helga Velroyen

2073 def6577f Helga Velroyen
    """
2074 def6577f Helga Velroyen
    result = ""
2075 def6577f Helga Velroyen
    hvparams = self._config_data.cluster.hvparams[hvname]
2076 def6577f Helga Velroyen
    for key in hvparams:
2077 def6577f Helga Velroyen
      result += "%s=%s\n" % (key, hvparams[key])
2078 def6577f Helga Velroyen
    return result
2079 def6577f Helga Velroyen
2080 def6577f Helga Velroyen
  @locking.ssynchronized(_config_lock, shared=1)
2081 def6577f Helga Velroyen
  def GetHvparamsString(self, hvname):
2082 def6577f Helga Velroyen
    """Return the hypervisor parameters of the given hypervisor.
2083 def6577f Helga Velroyen

2084 def6577f Helga Velroyen
    @type hvname: string
2085 def6577f Helga Velroyen
    @param hvname: name of a hypervisor
2086 def6577f Helga Velroyen
    @rtype: string
2087 def6577f Helga Velroyen
    @return: string containing key-value-pairs, one pair on each line;
2088 def6577f Helga Velroyen
      format: KEY=VALUE
2089 def6577f Helga Velroyen

2090 def6577f Helga Velroyen
    """
2091 def6577f Helga Velroyen
    return self._UnlockedGetHvparamsString(hvname)
2092 def6577f Helga Velroyen
2093 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
2094 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
2095 a8083063 Iustin Pop

2096 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
2097 c41eea6e Iustin Pop
    held.
2098 c41eea6e Iustin Pop

2099 c41eea6e Iustin Pop
    @rtype: list
2100 f78ede4e Guido Trotter

2101 a8083063 Iustin Pop
    """
2102 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
2103 a8083063 Iustin Pop
2104 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2105 f78ede4e Guido Trotter
  def GetNodeList(self):
2106 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
2107 f78ede4e Guido Trotter

2108 f78ede4e Guido Trotter
    """
2109 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
2110 f78ede4e Guido Trotter
2111 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
2112 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
2113 94a02bb5 Iustin Pop

2114 94a02bb5 Iustin Pop
    """
2115 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
2116 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
2117 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.offline]
2118 94a02bb5 Iustin Pop
2119 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2120 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
2121 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
2122 6819dc49 Iustin Pop

2123 6819dc49 Iustin Pop
    """
2124 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
2125 6819dc49 Iustin Pop
2126 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2127 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
2128 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
2129 075b62ca Iustin Pop

2130 075b62ca Iustin Pop
    """
2131 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
2132 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
2133 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if node.vm_capable]
2134 075b62ca Iustin Pop
2135 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2136 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
2137 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
2138 8bf9e9a5 Iustin Pop

2139 8bf9e9a5 Iustin Pop
    """
2140 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
2141 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
2142 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.vm_capable]
2143 8bf9e9a5 Iustin Pop
2144 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2145 1c3231aa Thomas Thrainer
  def GetMultiNodeInfo(self, node_uuids):
2146 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
2147 f5eaa3c1 Iustin Pop

2148 1c3231aa Thomas Thrainer
    @param node_uuids: list of node UUIDs
2149 f5eaa3c1 Iustin Pop
    @rtype: list
2150 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
2151 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
2152 f5eaa3c1 Iustin Pop
        order
2153 f5eaa3c1 Iustin Pop

2154 f5eaa3c1 Iustin Pop
    """
2155 1c3231aa Thomas Thrainer
    return [(uuid, self._UnlockedGetNodeInfo(uuid)) for uuid in node_uuids]
2156 1c3231aa Thomas Thrainer
2157 1c3231aa Thomas Thrainer
  def _UnlockedGetAllNodesInfo(self):
2158 1c3231aa Thomas Thrainer
    """Gets configuration of all nodes.
2159 1c3231aa Thomas Thrainer

2160 1c3231aa Thomas Thrainer
    @note: See L{GetAllNodesInfo}
2161 1c3231aa Thomas Thrainer

2162 1c3231aa Thomas Thrainer
    """
2163 1c3231aa Thomas Thrainer
    return dict([(node_uuid, self._UnlockedGetNodeInfo(node_uuid))
2164 1c3231aa Thomas Thrainer
                 for node_uuid in self._UnlockedGetNodeList()])
2165 f5eaa3c1 Iustin Pop
2166 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2167 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
2168 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
2169 d65e5776 Iustin Pop

2170 d65e5776 Iustin Pop
    @rtype: dict
2171 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
2172 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
2173 d65e5776 Iustin Pop

2174 d65e5776 Iustin Pop
    """
2175 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
2176 ee14d800 Michael Hanselmann
2177 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfoByName(self, node_name):
2178 1c3231aa Thomas Thrainer
    for node in self._UnlockedGetAllNodesInfo().values():
2179 1c3231aa Thomas Thrainer
      if node.name == node_name:
2180 1c3231aa Thomas Thrainer
        return node
2181 1c3231aa Thomas Thrainer
    return None
2182 ee14d800 Michael Hanselmann
2183 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2184 1c3231aa Thomas Thrainer
  def GetNodeInfoByName(self, node_name):
2185 1c3231aa Thomas Thrainer
    """Get the L{objects.Node} object for a named node.
2186 1c3231aa Thomas Thrainer

2187 1c3231aa Thomas Thrainer
    @param node_name: name of the node to get information for
2188 1c3231aa Thomas Thrainer
    @type node_name: string
2189 1c3231aa Thomas Thrainer
    @return: the corresponding L{objects.Node} instance or None if no
2190 1c3231aa Thomas Thrainer
          information is available
2191 1c3231aa Thomas Thrainer

2192 1c3231aa Thomas Thrainer
    """
2193 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfoByName(node_name)
2194 1c3231aa Thomas Thrainer
2195 6b2a2942 Petr Pudlak
  @locking.ssynchronized(_config_lock, shared=1)
2196 6b2a2942 Petr Pudlak
  def GetNodeGroupInfoByName(self, nodegroup_name):
2197 6b2a2942 Petr Pudlak
    """Get the L{objects.NodeGroup} object for a named node group.
2198 6b2a2942 Petr Pudlak

2199 6b2a2942 Petr Pudlak
    @param nodegroup_name: name of the node group to get information for
2200 6b2a2942 Petr Pudlak
    @type nodegroup_name: string
2201 6b2a2942 Petr Pudlak
    @return: the corresponding L{objects.NodeGroup} instance or None if no
2202 6b2a2942 Petr Pudlak
          information is available
2203 6b2a2942 Petr Pudlak

2204 6b2a2942 Petr Pudlak
    """
2205 6b2a2942 Petr Pudlak
    for nodegroup in self._UnlockedGetAllNodeGroupsInfo().values():
2206 6b2a2942 Petr Pudlak
      if nodegroup.name == nodegroup_name:
2207 6b2a2942 Petr Pudlak
        return nodegroup
2208 6b2a2942 Petr Pudlak
    return None
2209 6b2a2942 Petr Pudlak
2210 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeName(self, node_spec):
2211 1c3231aa Thomas Thrainer
    if isinstance(node_spec, objects.Node):
2212 1c3231aa Thomas Thrainer
      return node_spec.name
2213 1c3231aa Thomas Thrainer
    elif isinstance(node_spec, basestring):
2214 1c3231aa Thomas Thrainer
      node_info = self._UnlockedGetNodeInfo(node_spec)
2215 1c3231aa Thomas Thrainer
      if node_info is None:
2216 1c3231aa Thomas Thrainer
        raise errors.OpExecError("Unknown node: %s" % node_spec)
2217 1c3231aa Thomas Thrainer
      return node_info.name
2218 1c3231aa Thomas Thrainer
    else:
2219 1c3231aa Thomas Thrainer
      raise errors.ProgrammerError("Can't handle node spec '%s'" % node_spec)
2220 1c3231aa Thomas Thrainer
2221 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2222 1c3231aa Thomas Thrainer
  def GetNodeName(self, node_spec):
2223 1c3231aa Thomas Thrainer
    """Gets the node name for the passed node.
2224 1c3231aa Thomas Thrainer

2225 1c3231aa Thomas Thrainer
    @param node_spec: node to get names for
2226 1c3231aa Thomas Thrainer
    @type node_spec: either node UUID or a L{objects.Node} object
2227 1c3231aa Thomas Thrainer
    @rtype: string
2228 1c3231aa Thomas Thrainer
    @return: node name
2229 1c3231aa Thomas Thrainer

2230 1c3231aa Thomas Thrainer
    """
2231 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(node_spec)
2232 1c3231aa Thomas Thrainer
2233 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeNames(self, node_specs):
2234 1c3231aa Thomas Thrainer
    return [self._UnlockedGetNodeName(node_spec) for node_spec in node_specs]
2235 1c3231aa Thomas Thrainer
2236 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2237 1c3231aa Thomas Thrainer
  def GetNodeNames(self, node_specs):
2238 1c3231aa Thomas Thrainer
    """Gets the node names for the passed list of nodes.
2239 1c3231aa Thomas Thrainer

2240 1c3231aa Thomas Thrainer
    @param node_specs: list of nodes to get names for
2241 1c3231aa Thomas Thrainer
    @type node_specs: list of either node UUIDs or L{objects.Node} objects
2242 1c3231aa Thomas Thrainer
    @rtype: list of strings
2243 1c3231aa Thomas Thrainer
    @return: list of node names
2244 ee14d800 Michael Hanselmann

2245 ee14d800 Michael Hanselmann
    """
2246 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeNames(node_specs)
2247 d65e5776 Iustin Pop
2248 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
2249 1c3231aa Thomas Thrainer
  def GetNodeGroupsFromNodes(self, node_uuids):
2250 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
2251 9d5b1371 Michael Hanselmann

2252 1c3231aa Thomas Thrainer
    @type node_uuids: list of string
2253 1c3231aa Thomas Thrainer
    @param node_uuids: List of node UUIDs
2254 9d5b1371 Michael Hanselmann
    @rtype: frozenset
2255 9d5b1371 Michael Hanselmann

2256 9d5b1371 Michael Hanselmann
    """
2257 1c3231aa Thomas Thrainer
    return frozenset(self._UnlockedGetNodeInfo(uuid).group
2258 1c3231aa Thomas Thrainer
                     for uuid in node_uuids)
2259 9d5b1371 Michael Hanselmann
2260 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
2261 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
2262 ec0292f1 Iustin Pop

2263 23f06b2b Iustin Pop
    @type exceptions: list
2264 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
2265 ec0292f1 Iustin Pop
    @rtype: tuple
2266 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
2267 ec0292f1 Iustin Pop

2268 ec0292f1 Iustin Pop
    """
2269 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
2270 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
2271 1c3231aa Thomas Thrainer
      if exceptions and node.uuid in exceptions:
2272 23f06b2b Iustin Pop
        continue
2273 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
2274 ec0292f1 Iustin Pop
        mc_max += 1
2275 ec0292f1 Iustin Pop
      if node.master_candidate:
2276 ec0292f1 Iustin Pop
        mc_now += 1
2277 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
2278 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
2279 ec0292f1 Iustin Pop
2280 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2281 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
2282 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
2283 ec0292f1 Iustin Pop

2284 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
2285 ec0292f1 Iustin Pop

2286 23f06b2b Iustin Pop
    @type exceptions: list
2287 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
2288 ec0292f1 Iustin Pop
    @rtype: tuple
2289 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
2290 ec0292f1 Iustin Pop

2291 ec0292f1 Iustin Pop
    """
2292 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
2293 ec0292f1 Iustin Pop
2294 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
2295 1c3231aa Thomas Thrainer
  def MaintainCandidatePool(self, exception_node_uuids):
2296 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
2297 ec0292f1 Iustin Pop

2298 1c3231aa Thomas Thrainer
    @type exception_node_uuids: list
2299 1c3231aa Thomas Thrainer
    @param exception_node_uuids: if passed, list of nodes that should be ignored
2300 ec0292f1 Iustin Pop
    @rtype: list
2301 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
2302 ec0292f1 Iustin Pop

2303 ec0292f1 Iustin Pop
    """
2304 1c3231aa Thomas Thrainer
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(
2305 1c3231aa Thomas Thrainer
                          exception_node_uuids)
2306 ec0292f1 Iustin Pop
    mod_list = []
2307 ec0292f1 Iustin Pop
    if mc_now < mc_max:
2308 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
2309 ec0292f1 Iustin Pop
      random.shuffle(node_list)
2310 1c3231aa Thomas Thrainer
      for uuid in node_list:
2311 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
2312 ec0292f1 Iustin Pop
          break
2313 1c3231aa Thomas Thrainer
        node = self._config_data.nodes[uuid]
2314 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
2315 1c3231aa Thomas Thrainer
            node.uuid in exception_node_uuids or not node.master_capable):
2316 ec0292f1 Iustin Pop
          continue
2317 ee513a66 Iustin Pop
        mod_list.append(node)
2318 ec0292f1 Iustin Pop
        node.master_candidate = True
2319 ec0292f1 Iustin Pop
        node.serial_no += 1
2320 ec0292f1 Iustin Pop
        mc_now += 1
2321 ec0292f1 Iustin Pop
      if mc_now != mc_max:
2322 ec0292f1 Iustin Pop
        # this should not happen
2323 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
2324 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
2325 ec0292f1 Iustin Pop
      if mod_list:
2326 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
2327 ec0292f1 Iustin Pop
        self._WriteConfig()
2328 ec0292f1 Iustin Pop
2329 ec0292f1 Iustin Pop
    return mod_list
2330 ec0292f1 Iustin Pop
2331 1c3231aa Thomas Thrainer
  def _UnlockedAddNodeToGroup(self, node_uuid, nodegroup_uuid):
2332 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
2333 190e3cb6 Guido Trotter

2334 190e3cb6 Guido Trotter
    """
2335 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
2336 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
2337 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
2338 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
2339 190e3cb6 Guido Trotter
      # is not found anymore.
2340 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
2341 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodegroups[nodegroup_uuid].members:
2342 1c3231aa Thomas Thrainer
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_uuid)
2343 190e3cb6 Guido Trotter
2344 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
2345 190e3cb6 Guido Trotter
    """Remove a given node from its group.
2346 190e3cb6 Guido Trotter

2347 190e3cb6 Guido Trotter
    """
2348 f936c153 Iustin Pop
    nodegroup = node.group
2349 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
2350 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
2351 1c3231aa Thomas Thrainer
                      " (while being removed from it)", node.uuid, nodegroup)
2352 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
2353 1c3231aa Thomas Thrainer
    if node.uuid not in nodegroup_obj.members:
2354 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
2355 1c3231aa Thomas Thrainer
                      " (while being removed from it)", node.uuid, nodegroup)
2356 190e3cb6 Guido Trotter
    else:
2357 1c3231aa Thomas Thrainer
      nodegroup_obj.members.remove(node.uuid)
2358 190e3cb6 Guido Trotter
2359 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
2360 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
2361 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
2362 54c31fd3 Michael Hanselmann

2363 54c31fd3 Michael Hanselmann
    @type mods: list of tuples; (node name, new group UUID)
2364 1d4930b9 Michael Hanselmann
    @param mods: Node membership modifications
2365 54c31fd3 Michael Hanselmann

2366 54c31fd3 Michael Hanselmann
    """
2367 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
2368 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
2369 54c31fd3 Michael Hanselmann
2370 54c31fd3 Michael Hanselmann
    resmod = []
2371 54c31fd3 Michael Hanselmann
2372 1c3231aa Thomas Thrainer
    # Try to resolve UUIDs first
2373 1c3231aa Thomas Thrainer
    for (node_uuid, new_group_uuid) in mods:
2374 54c31fd3 Michael Hanselmann
      try:
2375 1c3231aa Thomas Thrainer
        node = nodes[node_uuid]
2376 54c31fd3 Michael Hanselmann
      except KeyError:
2377 1c3231aa Thomas Thrainer
        raise errors.ConfigurationError("Unable to find node '%s'" % node_uuid)
2378 54c31fd3 Michael Hanselmann
2379 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
2380 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
2381 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
2382 1c3231aa Thomas Thrainer
                      node_uuid, node.group)
2383 54c31fd3 Michael Hanselmann
        continue
2384 54c31fd3 Michael Hanselmann
2385 54c31fd3 Michael Hanselmann
      # Try to find current group of node
2386 54c31fd3 Michael Hanselmann
      try:
2387 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
2388 54c31fd3 Michael Hanselmann
      except KeyError:
2389 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
2390 54c31fd3 Michael Hanselmann
                                        node.group)
2391 54c31fd3 Michael Hanselmann
2392 54c31fd3 Michael Hanselmann
      # Try to find new group for node
2393 54c31fd3 Michael Hanselmann
      try:
2394 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
2395 54c31fd3 Michael Hanselmann
      except KeyError:
2396 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
2397 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
2398 54c31fd3 Michael Hanselmann
2399 1c3231aa Thomas Thrainer
      assert node.uuid in old_group.members, \
2400 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
2401 1c3231aa Thomas Thrainer
         " old group '%s'" % (node.uuid, old_group.uuid))
2402 1c3231aa Thomas Thrainer
      assert node.uuid not in new_group.members, \
2403 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
2404 1c3231aa Thomas Thrainer
         " its new group '%s'" % (node.uuid, new_group.uuid))
2405 54c31fd3 Michael Hanselmann
2406 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
2407 54c31fd3 Michael Hanselmann
2408 54c31fd3 Michael Hanselmann
    # Apply changes
2409 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
2410 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
2411 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
2412 54c31fd3 Michael Hanselmann
2413 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
2414 54c31fd3 Michael Hanselmann
2415 54c31fd3 Michael Hanselmann
      # Update members of involved groups
2416 1c3231aa Thomas Thrainer
      if node.uuid in old_group.members:
2417 1c3231aa Thomas Thrainer
        old_group.members.remove(node.uuid)
2418 1c3231aa Thomas Thrainer
      if node.uuid not in new_group.members:
2419 1c3231aa Thomas Thrainer
        new_group.members.append(node.uuid)
2420 54c31fd3 Michael Hanselmann
2421 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
2422 54c31fd3 Michael Hanselmann
    now = time.time()
2423 75191077 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142
2424 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
2425 54c31fd3 Michael Hanselmann
      obj.mtime = now
2426 54c31fd3 Michael Hanselmann
2427 54c31fd3 Michael Hanselmann
    # Force ssconf update
2428 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
2429 54c31fd3 Michael Hanselmann
2430 54c31fd3 Michael Hanselmann
    self._WriteConfig()
2431 54c31fd3 Michael Hanselmann
2432 a8083063 Iustin Pop
  def _BumpSerialNo(self):
2433 a8083063 Iustin Pop
    """Bump up the serial number of the config.
2434 a8083063 Iustin Pop

2435 a8083063 Iustin Pop
    """
2436 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
2437 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
2438 a8083063 Iustin Pop
2439 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
2440 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
2441 76d5d3a3 Iustin Pop

2442 76d5d3a3 Iustin Pop
    """
2443 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
2444 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
2445 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
2446 e0519c34 Dimitris Aragiorgis
            self._config_data.networks.values() +
2447 b87a9c5f Christos Stavrakakis
            self._AllDisks() +
2448 b87a9c5f Christos Stavrakakis
            self._AllNICs() +
2449 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
2450 76d5d3a3 Iustin Pop
2451 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
2452 a8083063 Iustin Pop
    """Read the config data from disk.
2453 a8083063 Iustin Pop

2454 a8083063 Iustin Pop
    """
2455 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
2456 13998ef2 Michael Hanselmann
2457 a8083063 Iustin Pop
    try:
2458 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
2459 13998ef2 Michael Hanselmann
    except Exception, err:
2460 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
2461 5b263ed7 Michael Hanselmann
2462 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
2463 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
2464 5b263ed7 Michael Hanselmann
2465 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
2466 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
2467 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
2468 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
2469 90d726a8 Iustin Pop
2470 1c3231aa Thomas Thrainer
    if not data.cluster.master_node in data.nodes:
2471 1c3231aa Thomas Thrainer
      msg = ("The configuration denotes node %s as master, but does not"
2472 1c3231aa Thomas Thrainer
             " contain information about this node" %
2473 1c3231aa Thomas Thrainer
             data.cluster.master_node)
2474 1c3231aa Thomas Thrainer
      raise errors.ConfigurationError(msg)
2475 1c3231aa Thomas Thrainer
2476 1c3231aa Thomas Thrainer
    master_info = data.nodes[data.cluster.master_node]
2477 1c3231aa Thomas Thrainer
    if master_info.name != self._my_hostname and not accept_foreign:
2478 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
2479 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
2480 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
2481 1c3231aa Thomas Thrainer
             (master_info.name, self._my_hostname))
2482 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
2483 eb180fe2 Iustin Pop
2484 a8083063 Iustin Pop
    self._config_data = data
2485 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
2486 0779e3aa Iustin Pop
    # ssconf update
2487 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
2488 a8083063 Iustin Pop
2489 45f62156 Bernardo Dal Seno
    # Upgrade configuration if needed
2490 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
2491 76d5d3a3 Iustin Pop
2492 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
2493 bd407597 Iustin Pop
2494 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
2495 45f62156 Bernardo Dal Seno
    """Run any upgrade steps.
2496 76d5d3a3 Iustin Pop

2497 45f62156 Bernardo Dal Seno
    This method performs both in-object upgrades and also update some data
2498 45f62156 Bernardo Dal Seno
    elements that need uniqueness across the whole configuration or interact
2499 45f62156 Bernardo Dal Seno
    with other objects.
2500 76d5d3a3 Iustin Pop

2501 111c4e2f Guido Trotter
    @warning: this function will call L{_WriteConfig()}, but also
2502 111c4e2f Guido Trotter
        L{DropECReservations} so it needs to be called only from a
2503 111c4e2f Guido Trotter
        "safe" place (the constructor). If one wanted to call it with
2504 111c4e2f Guido Trotter
        the lock held, a DropECReservationUnlocked would need to be
2505 111c4e2f Guido Trotter
        created first, to avoid causing deadlock.
2506 76d5d3a3 Iustin Pop

2507 76d5d3a3 Iustin Pop
    """
2508 45f62156 Bernardo Dal Seno
    # Keep a copy of the persistent part of _config_data to check for changes
2509 45f62156 Bernardo Dal Seno
    # Serialization doesn't guarantee order in dictionaries
2510 45f62156 Bernardo Dal Seno
    oldconf = copy.deepcopy(self._config_data.ToDict())
2511 45f62156 Bernardo Dal Seno
2512 45f62156 Bernardo Dal Seno
    # In-object upgrades
2513 45f62156 Bernardo Dal Seno
    self._config_data.UpgradeConfig()
2514 45f62156 Bernardo Dal Seno
2515 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
2516 76d5d3a3 Iustin Pop
      if item.uuid is None:
2517 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
2518 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
2519 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
2520 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
2521 75cf411a Adeodato Simo
                                            members=[])
2522 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
2523 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
2524 f936c153 Iustin Pop
      if not node.group:
2525 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
2526 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
2527 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
2528 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
2529 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
2530 1c3231aa Thomas Thrainer
      self._UnlockedAddNodeToGroup(node.uuid, node.group)
2531 45f62156 Bernardo Dal Seno
2532 45f62156 Bernardo Dal Seno
    modified = (oldconf != self._config_data.ToDict())
2533 76d5d3a3 Iustin Pop
    if modified:
2534 76d5d3a3 Iustin Pop
      self._WriteConfig()
2535 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
2536 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
2537 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
2538 04db1880 Bernardo Dal Seno
    else:
2539 04db1880 Bernardo Dal Seno
      config_errors = self._UnlockedVerifyConfig()
2540 04db1880 Bernardo Dal Seno
      if config_errors:
2541 04db1880 Bernardo Dal Seno
        errmsg = ("Loaded configuration data is not consistent: %s" %
2542 04db1880 Bernardo Dal Seno
                  (utils.CommaJoin(config_errors)))
2543 04db1880 Bernardo Dal Seno
        logging.critical(errmsg)
2544 4fae38c5 Guido Trotter
2545 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
2546 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
2547 a8083063 Iustin Pop

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

2551 a8083063 Iustin Pop
    """
2552 a8083063 Iustin Pop
    if self._offline:
2553 a8083063 Iustin Pop
      return True
2554 a4eae71f Michael Hanselmann
2555 a8083063 Iustin Pop
    bad = False
2556 a8083063 Iustin Pop
2557 6a5b8b4b Iustin Pop
    node_list = []
2558 6a5b8b4b Iustin Pop
    addr_list = []
2559 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
2560 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
2561 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
2562 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
2563 6b294c53 Iustin Pop
    # in between
2564 1c3231aa Thomas Thrainer
    for node_uuid in self._UnlockedGetNodeList():
2565 1c3231aa Thomas Thrainer
      node_info = self._UnlockedGetNodeInfo(node_uuid)
2566 1c3231aa Thomas Thrainer
      if node_info.name == myhostname or not node_info.master_candidate:
2567 6a5b8b4b Iustin Pop
        continue
2568 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
2569 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
2570 6b294c53 Iustin Pop
2571 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
2572 415a7304 Michael Hanselmann
    result = \
2573 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
2574 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
2575 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
2576 1b54fc6c Guido Trotter
      if msg:
2577 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
2578 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
2579 1b54fc6c Guido Trotter
        logging.error(msg)
2580 a4eae71f Michael Hanselmann
2581 a4eae71f Michael Hanselmann
        if feedback_fn:
2582 a4eae71f Michael Hanselmann
          feedback_fn(msg)
2583 a4eae71f Michael Hanselmann
2584 a8083063 Iustin Pop
        bad = True
2585 a4eae71f Michael Hanselmann
2586 a8083063 Iustin Pop
    return not bad
2587 a8083063 Iustin Pop
2588 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
2589 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
2590 a8083063 Iustin Pop

2591 a8083063 Iustin Pop
    """
2592 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
2593 a4eae71f Michael Hanselmann
2594 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
2595 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
2596 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
2597 d2231b8c Iustin Pop
    # recovery to the user
2598 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
2599 4a89c54a Iustin Pop
    if config_errors:
2600 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
2601 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
2602 d2231b8c Iustin Pop
      logging.critical(errmsg)
2603 d2231b8c Iustin Pop
      if feedback_fn:
2604 d2231b8c Iustin Pop
        feedback_fn(errmsg)
2605 d2231b8c Iustin Pop
2606 a8083063 Iustin Pop
    if destination is None:
2607 a8083063 Iustin Pop
      destination = self._cfg_file
2608 a8083063 Iustin Pop
    self._BumpSerialNo()
2609 4884f187 Santi Raffa
    txt = serializer.DumpJson(
2610 4884f187 Santi Raffa
      self._config_data.ToDict(_with_private=True),
2611 4884f187 Santi Raffa
      private_encoder=serializer.EncodeWithPrivateFields
2612 4884f187 Santi Raffa
    )
2613 13998ef2 Michael Hanselmann
2614 e60c73a1 René Nussbaumer
    getents = self._getents()
2615 bd407597 Iustin Pop
    try:
2616 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
2617 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
2618 bd407597 Iustin Pop
    except errors.LockError:
2619 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
2620 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
2621 bd407597 Iustin Pop
                                      " update")
2622 bd407597 Iustin Pop
    try:
2623 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
2624 bd407597 Iustin Pop
    finally:
2625 bd407597 Iustin Pop
      os.close(fd)
2626 13998ef2 Michael Hanselmann
2627 14e15659 Iustin Pop
    self.write_count += 1
2628 3d3a04bc Iustin Pop
2629 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
2630 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
2631 a8083063 Iustin Pop
2632 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
2633 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
2634 d9a855f1 Michael Hanselmann
      if not self._offline:
2635 b2acdbdc Michael Hanselmann
        result = self._GetRpc(None).call_write_ssconf_files(
2636 1c3231aa Thomas Thrainer
          self._UnlockedGetNodeNames(self._UnlockedGetOnlineNodeList()),
2637 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
2638 a4eae71f Michael Hanselmann
2639 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
2640 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
2641 e1e75d00 Iustin Pop
          if msg:
2642 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
2643 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
2644 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
2645 a4eae71f Michael Hanselmann
2646 a4eae71f Michael Hanselmann
            if feedback_fn:
2647 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
2648 a4eae71f Michael Hanselmann
2649 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
2650 54d1a06e Michael Hanselmann
2651 def6577f Helga Velroyen
  def _GetAllHvparamsStrings(self, hypervisors):
2652 def6577f Helga Velroyen
    """Get the hvparams of all given hypervisors from the config.
2653 def6577f Helga Velroyen

2654 def6577f Helga Velroyen
    @type hypervisors: list of string
2655 def6577f Helga Velroyen
    @param hypervisors: list of hypervisor names
2656 def6577f Helga Velroyen
    @rtype: dict of strings
2657 def6577f Helga Velroyen
    @returns: dictionary mapping the hypervisor name to a string representation
2658 def6577f Helga Velroyen
      of the hypervisor's hvparams
2659 def6577f Helga Velroyen

2660 def6577f Helga Velroyen
    """
2661 def6577f Helga Velroyen
    hvparams = {}
2662 def6577f Helga Velroyen
    for hv in hypervisors:
2663 def6577f Helga Velroyen
      hvparams[hv] = self._UnlockedGetHvparamsString(hv)
2664 def6577f Helga Velroyen
    return hvparams
2665 def6577f Helga Velroyen
2666 def6577f Helga Velroyen
  @staticmethod
2667 def6577f Helga Velroyen
  def _ExtendByAllHvparamsStrings(ssconf_values, all_hvparams):
2668 def6577f Helga Velroyen
    """Extends the ssconf_values dictionary by hvparams.
2669 def6577f Helga Velroyen

2670 def6577f Helga Velroyen
    @type ssconf_values: dict of strings
2671 def6577f Helga Velroyen
    @param ssconf_values: dictionary mapping ssconf_keys to strings
2672 def6577f Helga Velroyen
      representing the content of ssconf files
2673 def6577f Helga Velroyen
    @type all_hvparams: dict of strings
2674 def6577f Helga Velroyen
    @param all_hvparams: dictionary mapping hypervisor names to a string
2675 def6577f Helga Velroyen
      representation of their hvparams
2676 def6577f Helga Velroyen
    @rtype: same as ssconf_values
2677 def6577f Helga Velroyen
    @returns: the ssconf_values dictionary extended by hvparams
2678 def6577f Helga Velroyen

2679 def6577f Helga Velroyen
    """
2680 def6577f Helga Velroyen
    for hv in all_hvparams:
2681 def6577f Helga Velroyen
      ssconf_key = constants.SS_HVPARAMS_PREF + hv
2682 def6577f Helga Velroyen
      ssconf_values[ssconf_key] = all_hvparams[hv]
2683 def6577f Helga Velroyen
    return ssconf_values
2684 def6577f Helga Velroyen
2685 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
2686 054596f0 Iustin Pop
    """Return the values needed by ssconf.
2687 054596f0 Iustin Pop

2688 054596f0 Iustin Pop
    @rtype: dict
2689 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2690 054596f0 Iustin Pop
        associated value
2691 054596f0 Iustin Pop

2692 054596f0 Iustin Pop
    """
2693 a3316e4a Iustin Pop
    fn = "\n".join
2694 da4a52a3 Thomas Thrainer
    instance_names = utils.NiceSort(
2695 da4a52a3 Thomas Thrainer
                       [inst.name for inst in
2696 da4a52a3 Thomas Thrainer
                        self._UnlockedGetAllInstancesInfo().values()])
2697 1c3231aa Thomas Thrainer
    node_infos = self._UnlockedGetAllNodesInfo().values()
2698 1c3231aa Thomas Thrainer
    node_names = [node.name for node in node_infos]
2699 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
2700 1c3231aa Thomas Thrainer
                    for ninfo in node_infos]
2701 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
2702 1c3231aa Thomas Thrainer
                    for ninfo in node_infos]
2703 a3316e4a Iustin Pop
2704 81a49123 Iustin Pop
    instance_data = fn(instance_names)
2705 1c3231aa Thomas Thrainer
    off_data = fn(node.name for node in node_infos if node.offline)
2706 1c3231aa Thomas Thrainer
    on_data = fn(node.name for node in node_infos if not node.offline)
2707 1c3231aa Thomas Thrainer
    mc_data = fn(node.name for node in node_infos if node.master_candidate)
2708 1c3231aa Thomas Thrainer
    mc_ips_data = fn(node.primary_ip for node in node_infos
2709 8113a52e Luca Bigliardi
                     if node.master_candidate)
2710 a3316e4a Iustin Pop
    node_data = fn(node_names)
2711 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
2712 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
2713 f56618e0 Iustin Pop
2714 054596f0 Iustin Pop
    cluster = self._config_data.cluster
2715 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
2716 4f7a6a10 Iustin Pop
2717 1059337d Helga Velroyen
    master_candidates_certs = fn("%s=%s" % (mc_uuid, mc_cert)
2718 1059337d Helga Velroyen
                                 for mc_uuid, mc_cert
2719 1059337d Helga Velroyen
                                 in cluster.candidate_certs.items())
2720 1059337d Helga Velroyen
2721 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
2722 def6577f Helga Velroyen
    all_hvparams = self._GetAllHvparamsStrings(constants.HYPER_TYPES)
2723 4f7a6a10 Iustin Pop
2724 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
2725 0fbae49a Balazs Lecz
2726 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
2727 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
2728 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
2729 6c0a75db Dimitris Aragiorgis
    networks = ["%s %s" % (net.uuid, net.name) for net in
2730 6c0a75db Dimitris Aragiorgis
                self._config_data.networks.values()]
2731 6c0a75db Dimitris Aragiorgis
    networks_data = fn(utils.NiceSort(networks))
2732 6f076453 Guido Trotter
2733 2afc9238 Iustin Pop
    ssconf_values = {
2734 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
2735 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
2736 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
2737 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
2738 d3e6fd0e Santi Raffa
      constants.SS_GLUSTER_STORAGE_DIR: cluster.gluster_storage_dir,
2739 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
2740 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
2741 1059337d Helga Velroyen
      constants.SS_MASTER_CANDIDATES_CERTS: master_candidates_certs,
2742 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
2743 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
2744 5a8648eb Andrea Spadaccini
      constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
2745 1c3231aa Thomas Thrainer
      constants.SS_MASTER_NODE: self._UnlockedGetNodeName(cluster.master_node),
2746 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
2747 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
2748 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
2749 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
2750 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
2751 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
2752 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
2753 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
2754 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
2755 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
2756 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
2757 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
2758 6c0a75db Dimitris Aragiorgis
      constants.SS_NETWORKS: networks_data,
2759 03d1dba2 Michael Hanselmann
      }
2760 def6577f Helga Velroyen
    ssconf_values = self._ExtendByAllHvparamsStrings(ssconf_values,
2761 def6577f Helga Velroyen
                                                     all_hvparams)
2762 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
2763 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
2764 2afc9238 Iustin Pop
    if bad_values:
2765 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
2766 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
2767 2afc9238 Iustin Pop
                                      " values: %s" % err)
2768 2afc9238 Iustin Pop
    return ssconf_values
2769 03d1dba2 Michael Hanselmann
2770 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2771 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
2772 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
2773 d367b66c Manuel Franceschini

2774 d367b66c Manuel Franceschini
    """
2775 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2776 d367b66c Manuel Franceschini
2777 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2778 a8083063 Iustin Pop
  def GetVGName(self):
2779 a8083063 Iustin Pop
    """Return the volume group name.
2780 a8083063 Iustin Pop

2781 a8083063 Iustin Pop
    """
2782 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2783 a8083063 Iustin Pop
2784 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2785 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2786 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2787 89ff8e15 Manuel Franceschini

2788 89ff8e15 Manuel Franceschini
    """
2789 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2790 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2791 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2792 89ff8e15 Manuel Franceschini
2793 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2794 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2795 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2796 9e33896b Luca Bigliardi

2797 9e33896b Luca Bigliardi
    """
2798 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2799 9e33896b Luca Bigliardi
2800 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2801 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2802 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2803 9e33896b Luca Bigliardi

2804 9e33896b Luca Bigliardi
    """
2805 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2806 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2807 9e33896b Luca Bigliardi
    self._WriteConfig()
2808 9e33896b Luca Bigliardi
2809 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2810 a8083063 Iustin Pop
  def GetMACPrefix(self):
2811 a8083063 Iustin Pop
    """Return the mac prefix.
2812 a8083063 Iustin Pop

2813 a8083063 Iustin Pop
    """
2814 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2815 62779dd0 Iustin Pop
2816 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2817 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2818 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2819 62779dd0 Iustin Pop

2820 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2821 c41eea6e Iustin Pop
    @return: the cluster object
2822 62779dd0 Iustin Pop

2823 62779dd0 Iustin Pop
    """
2824 62779dd0 Iustin Pop
    return self._config_data.cluster
2825 e00fb268 Iustin Pop
2826 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2827 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2828 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2829 51cb1581 Luca Bigliardi

2830 51cb1581 Luca Bigliardi
    """
2831 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2832 51cb1581 Luca Bigliardi
2833 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2834 d1547283 Dimitris Aragiorgis
  def Update(self, target, feedback_fn, ec_id=None):
2835 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2836 e00fb268 Iustin Pop

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

2843 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2844 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2845 c41eea6e Iustin Pop
        the cluster
2846 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2847 c41eea6e Iustin Pop

2848 e00fb268 Iustin Pop
    """
2849 e00fb268 Iustin Pop
    if self._config_data is None:
2850 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2851 3ecf6786 Iustin Pop
                                   " cannot save.")
2852 f34901f8 Iustin Pop
    update_serial = False
2853 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2854 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2855 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2856 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2857 f34901f8 Iustin Pop
      update_serial = True
2858 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2859 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2860 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2861 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2862 1e0d3321 Dimitris Aragiorgis
    elif isinstance(target, objects.Network):
2863 1e0d3321 Dimitris Aragiorgis
      test = target in self._config_data.networks.values()
2864 e00fb268 Iustin Pop
    else:
2865 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2866 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2867 e00fb268 Iustin Pop
    if not test:
2868 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2869 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2870 f34901f8 Iustin Pop
    target.serial_no += 1
2871 d693c864 Iustin Pop
    target.mtime = now = time.time()
2872 f34901f8 Iustin Pop
2873 cff4c037 Iustin Pop
    if update_serial:
2874 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2875 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2876 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2877 b989e85d Iustin Pop
2878 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2879 da4a52a3 Thomas Thrainer
      self._UnlockedReleaseDRBDMinors(target.uuid)
2880 61cf6b5e Iustin Pop
2881 d1547283 Dimitris Aragiorgis
    if ec_id is not None:
2882 d1547283 Dimitris Aragiorgis
      # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams
2883 d1547283 Dimitris Aragiorgis
      self._UnlockedCommitTemporaryIps(ec_id)
2884 d1547283 Dimitris Aragiorgis
2885 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2886 73064714 Guido Trotter
2887 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2888 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2889 73064714 Guido Trotter
    """Drop per-execution-context reservations
2890 73064714 Guido Trotter

2891 73064714 Guido Trotter
    """
2892 d8aee57e Iustin Pop
    for rm in self._all_rms:
2893 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)
2894 6c0a75db Dimitris Aragiorgis
2895 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2896 6c0a75db Dimitris Aragiorgis
  def GetAllNetworksInfo(self):
2897 6a94d553 Dimitris Aragiorgis
    """Get configuration info of all the networks.
2898 6c0a75db Dimitris Aragiorgis

2899 6c0a75db Dimitris Aragiorgis
    """
2900 6c0a75db Dimitris Aragiorgis
    return dict(self._config_data.networks)
2901 6c0a75db Dimitris Aragiorgis
2902 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetworkList(self):
2903 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2904 6c0a75db Dimitris Aragiorgis

2905 6c0a75db Dimitris Aragiorgis
    This function is for internal use, when the config lock is already held.
2906 6c0a75db Dimitris Aragiorgis

2907 6c0a75db Dimitris Aragiorgis
    """
2908 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks.keys()
2909 6c0a75db Dimitris Aragiorgis
2910 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2911 6c0a75db Dimitris Aragiorgis
  def GetNetworkList(self):
2912 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2913 6c0a75db Dimitris Aragiorgis

2914 6c0a75db Dimitris Aragiorgis
    @return: array of networks, ex. ["main", "vlan100", "200]
2915 6c0a75db Dimitris Aragiorgis

2916 6c0a75db Dimitris Aragiorgis
    """
2917 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetworkList()
2918 6c0a75db Dimitris Aragiorgis
2919 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2920 6c0a75db Dimitris Aragiorgis
  def GetNetworkNames(self):
2921 6c0a75db Dimitris Aragiorgis
    """Get a list of network names
2922 6c0a75db Dimitris Aragiorgis

2923 6c0a75db Dimitris Aragiorgis
    """
2924 beb81ea5 Dimitris Aragiorgis
    names = [net.name
2925 beb81ea5 Dimitris Aragiorgis
             for net in self._config_data.networks.values()]
2926 6c0a75db Dimitris Aragiorgis
    return names
2927 6c0a75db Dimitris Aragiorgis
2928 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetwork(self, uuid):
2929 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2930 6c0a75db Dimitris Aragiorgis

2931 6c0a75db Dimitris Aragiorgis
    This function is for internal use, when the config lock is already held.
2932 6c0a75db Dimitris Aragiorgis

2933 6c0a75db Dimitris Aragiorgis
    """
2934 6c0a75db Dimitris Aragiorgis
    if uuid not in self._config_data.networks:
2935 6c0a75db Dimitris Aragiorgis
      return None
2936 6c0a75db Dimitris Aragiorgis
2937 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks[uuid]
2938 6c0a75db Dimitris Aragiorgis
2939 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2940 6c0a75db Dimitris Aragiorgis
  def GetNetwork(self, uuid):
2941 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2942 6c0a75db Dimitris Aragiorgis

2943 6c0a75db Dimitris Aragiorgis
    It takes the information from the configuration file.
2944 6c0a75db Dimitris Aragiorgis

2945 6c0a75db Dimitris Aragiorgis
    @param uuid: UUID of the network
2946 6c0a75db Dimitris Aragiorgis

2947 6c0a75db Dimitris Aragiorgis
    @rtype: L{objects.Network}
2948 6c0a75db Dimitris Aragiorgis
    @return: the network object
2949 6c0a75db Dimitris Aragiorgis

2950 6c0a75db Dimitris Aragiorgis
    """
2951 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetwork(uuid)
2952 6c0a75db Dimitris Aragiorgis
2953 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2954 6c0a75db Dimitris Aragiorgis
  def AddNetwork(self, net, ec_id, check_uuid=True):
2955 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2956 6c0a75db Dimitris Aragiorgis

2957 6c0a75db Dimitris Aragiorgis
    @type net: L{objects.Network}
2958 6c0a75db Dimitris Aragiorgis
    @param net: the Network object to add
2959 6c0a75db Dimitris Aragiorgis
    @type ec_id: string
2960 6c0a75db Dimitris Aragiorgis
    @param ec_id: unique id for the job to use when creating a missing UUID
2961 6c0a75db Dimitris Aragiorgis

2962 6c0a75db Dimitris Aragiorgis
    """
2963 6c0a75db Dimitris Aragiorgis
    self._UnlockedAddNetwork(net, ec_id, check_uuid)
2964 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2965 6c0a75db Dimitris Aragiorgis
2966 6c0a75db Dimitris Aragiorgis
  def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
2967 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2968 6c0a75db Dimitris Aragiorgis

2969 6c0a75db Dimitris Aragiorgis
    """
2970 6c0a75db Dimitris Aragiorgis
    logging.info("Adding network %s to configuration", net.name)
2971 6c0a75db Dimitris Aragiorgis
2972 6c0a75db Dimitris Aragiorgis
    if check_uuid:
2973 6c0a75db Dimitris Aragiorgis
      self._EnsureUUID(net, ec_id)
2974 6c0a75db Dimitris Aragiorgis
2975 6c0a75db Dimitris Aragiorgis
    net.serial_no = 1
2976 22ff02a7 Christos Stavrakakis
    net.ctime = net.mtime = time.time()
2977 6c0a75db Dimitris Aragiorgis
    self._config_data.networks[net.uuid] = net
2978 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2979 6c0a75db Dimitris Aragiorgis
2980 6c0a75db Dimitris Aragiorgis
  def _UnlockedLookupNetwork(self, target):
2981 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2982 6c0a75db Dimitris Aragiorgis

2983 6c0a75db Dimitris Aragiorgis
    @type target: string
2984 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2985 6c0a75db Dimitris Aragiorgis
    @rtype: string
2986 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2987 6c0a75db Dimitris Aragiorgis
    @raises errors.OpPrereqError: when the target network cannot be found
2988 6c0a75db Dimitris Aragiorgis

2989 6c0a75db Dimitris Aragiorgis
    """
2990 9394f4d1 Dimitris Aragiorgis
    if target is None:
2991 9394f4d1 Dimitris Aragiorgis
      return None
2992 6c0a75db Dimitris Aragiorgis
    if target in self._config_data.networks:
2993 6c0a75db Dimitris Aragiorgis
      return target
2994 6c0a75db Dimitris Aragiorgis
    for net in self._config_data.networks.values():
2995 6c0a75db Dimitris Aragiorgis
      if net.name == target:
2996 6c0a75db Dimitris Aragiorgis
        return net.uuid
2997 1b68f268 Helga Velroyen
    raise errors.OpPrereqError("Network '%s' not found" % target,
2998 1b68f268 Helga Velroyen
                               errors.ECODE_NOENT)
2999 6c0a75db Dimitris Aragiorgis
3000 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
3001 6c0a75db Dimitris Aragiorgis
  def LookupNetwork(self, target):
3002 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
3003 6c0a75db Dimitris Aragiorgis

3004 6c0a75db Dimitris Aragiorgis
    This function is just a wrapper over L{_UnlockedLookupNetwork}.
3005 6c0a75db Dimitris Aragiorgis

3006 6c0a75db Dimitris Aragiorgis
    @type target: string
3007 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
3008 6c0a75db Dimitris Aragiorgis
    @rtype: string
3009 6c0a75db Dimitris Aragiorgis
    @return: network UUID
3010 6c0a75db Dimitris Aragiorgis

3011 6c0a75db Dimitris Aragiorgis
    """
3012 6c0a75db Dimitris Aragiorgis
    return self._UnlockedLookupNetwork(target)
3013 6c0a75db Dimitris Aragiorgis
3014 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
3015 6c0a75db Dimitris Aragiorgis
  def RemoveNetwork(self, network_uuid):
3016 6c0a75db Dimitris Aragiorgis
    """Remove a network from the configuration.
3017 6c0a75db Dimitris Aragiorgis

3018 6c0a75db Dimitris Aragiorgis
    @type network_uuid: string
3019 6c0a75db Dimitris Aragiorgis
    @param network_uuid: the UUID of the network to remove
3020 6c0a75db Dimitris Aragiorgis

3021 6c0a75db Dimitris Aragiorgis
    """
3022 6c0a75db Dimitris Aragiorgis
    logging.info("Removing network %s from configuration", network_uuid)
3023 6c0a75db Dimitris Aragiorgis
3024 6c0a75db Dimitris Aragiorgis
    if network_uuid not in self._config_data.networks:
3025 6c0a75db Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
3026 6c0a75db Dimitris Aragiorgis
3027 6c0a75db Dimitris Aragiorgis
    del self._config_data.networks[network_uuid]
3028 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
3029 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
3030 ad4a9ae7 Dimitris Aragiorgis
3031 1c3231aa Thomas Thrainer
  def _UnlockedGetGroupNetParams(self, net_uuid, node_uuid):
3032 ad4a9ae7 Dimitris Aragiorgis
    """Get the netparams (mode, link) of a network.
3033 ad4a9ae7 Dimitris Aragiorgis

3034 ad4a9ae7 Dimitris Aragiorgis
    Get a network's netparams for a given node.
3035 ad4a9ae7 Dimitris Aragiorgis

3036 9ccacbc8 Dimitris Aragiorgis
    @type net_uuid: string
3037 9ccacbc8 Dimitris Aragiorgis
    @param net_uuid: network uuid
3038 1c3231aa Thomas Thrainer
    @type node_uuid: string
3039 1c3231aa Thomas Thrainer
    @param node_uuid: node UUID
3040 ad4a9ae7 Dimitris Aragiorgis
    @rtype: dict or None
3041 ad4a9ae7 Dimitris Aragiorgis
    @return: netparams
3042 ad4a9ae7 Dimitris Aragiorgis

3043 ad4a9ae7 Dimitris Aragiorgis
    """
3044 1c3231aa Thomas Thrainer
    node_info = self._UnlockedGetNodeInfo(node_uuid)
3045 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
3046 ad4a9ae7 Dimitris Aragiorgis
    netparams = nodegroup_info.networks.get(net_uuid, None)
3047 ad4a9ae7 Dimitris Aragiorgis
3048 ad4a9ae7 Dimitris Aragiorgis
    return netparams
3049 ad4a9ae7 Dimitris Aragiorgis
3050 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
3051 1c3231aa Thomas Thrainer
  def GetGroupNetParams(self, net_uuid, node_uuid):
3052 ad4a9ae7 Dimitris Aragiorgis
    """Locking wrapper of _UnlockedGetGroupNetParams()
3053 ad4a9ae7 Dimitris Aragiorgis

3054 ad4a9ae7 Dimitris Aragiorgis
    """
3055 1c3231aa Thomas Thrainer
    return self._UnlockedGetGroupNetParams(net_uuid, node_uuid)
3056 ad4a9ae7 Dimitris Aragiorgis
3057 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
3058 1c3231aa Thomas Thrainer
  def CheckIPInNodeGroup(self, ip, node_uuid):
3059 6a94d553 Dimitris Aragiorgis
    """Check IP uniqueness in nodegroup.
3060 6a94d553 Dimitris Aragiorgis

3061 6a94d553 Dimitris Aragiorgis
    Check networks that are connected in the node's node group
3062 6a94d553 Dimitris Aragiorgis
    if ip is contained in any of them. Used when creating/adding
3063 6a94d553 Dimitris Aragiorgis
    a NIC to ensure uniqueness among nodegroups.
3064 ad4a9ae7 Dimitris Aragiorgis

3065 ad4a9ae7 Dimitris Aragiorgis
    @type ip: string
3066 ad4a9ae7 Dimitris Aragiorgis
    @param ip: ip address
3067 1c3231aa Thomas Thrainer
    @type node_uuid: string
3068 1c3231aa Thomas Thrainer
    @param node_uuid: node UUID
3069 ad4a9ae7 Dimitris Aragiorgis
    @rtype: (string, dict) or (None, None)
3070 ad4a9ae7 Dimitris Aragiorgis
    @return: (network name, netparams)
3071 ad4a9ae7 Dimitris Aragiorgis

3072 ad4a9ae7 Dimitris Aragiorgis
    """
3073 ad4a9ae7 Dimitris Aragiorgis
    if ip is None:
3074 ad4a9ae7 Dimitris Aragiorgis
      return (None, None)
3075 1c3231aa Thomas Thrainer
    node_info = self._UnlockedGetNodeInfo(node_uuid)
3076 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
3077 ad4a9ae7 Dimitris Aragiorgis
    for net_uuid in nodegroup_info.networks.keys():
3078 ad4a9ae7 Dimitris Aragiorgis
      net_info = self._UnlockedGetNetwork(net_uuid)
3079 ad4a9ae7 Dimitris Aragiorgis
      pool = network.AddressPool(net_info)
3080 beb81ea5 Dimitris Aragiorgis
      if pool.Contains(ip):
3081 ad4a9ae7 Dimitris Aragiorgis
        return (net_info.name, nodegroup_info.networks[net_uuid])
3082 ad4a9ae7 Dimitris Aragiorgis
3083 ad4a9ae7 Dimitris Aragiorgis
    return (None, None)