Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ be9150ea

History | View | Annotate | Download (95.8 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 04db1880 Bernardo Dal Seno
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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 a8083063 Iustin Pop
from ganeti import 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 a8083063 Iustin Pop
class ConfigWriter:
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 8a147bba René Nussbaumer
  def GetInstanceDiskParams(self, instance):
241 8a147bba René Nussbaumer
    """Get the disk params populated with inherit chain.
242 8a147bba René Nussbaumer

243 8a147bba René Nussbaumer
    @type instance: L{objects.Instance}
244 8a147bba René Nussbaumer
    @param instance: The instance we want to know the params for
245 8a147bba René Nussbaumer
    @return: A dict with the filled in disk params
246 8a147bba René Nussbaumer

247 8a147bba René Nussbaumer
    """
248 8a147bba René Nussbaumer
    node = self._UnlockedGetNodeInfo(instance.primary_node)
249 8a147bba René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
250 99ccf8b9 René Nussbaumer
    return self._UnlockedGetGroupDiskParams(nodegroup)
251 99ccf8b9 René Nussbaumer
252 99ccf8b9 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
253 99ccf8b9 René Nussbaumer
  def GetGroupDiskParams(self, group):
254 99ccf8b9 René Nussbaumer
    """Get the disk params populated with inherit chain.
255 99ccf8b9 René Nussbaumer

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

260 99ccf8b9 René Nussbaumer
    """
261 99ccf8b9 René Nussbaumer
    return self._UnlockedGetGroupDiskParams(group)
262 99ccf8b9 René Nussbaumer
263 99ccf8b9 René Nussbaumer
  def _UnlockedGetGroupDiskParams(self, group):
264 99ccf8b9 René Nussbaumer
    """Get the disk params populated with inherit chain down to node-group.
265 99ccf8b9 René Nussbaumer

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

270 99ccf8b9 René Nussbaumer
    """
271 99ccf8b9 René Nussbaumer
    return self._config_data.cluster.SimpleFillDP(group.diskparams)
272 8a147bba René Nussbaumer
273 9ccacbc8 Dimitris Aragiorgis
  def _UnlockedGetNetworkMACPrefix(self, net_uuid):
274 032a7d71 Dimitris Aragiorgis
    """Return the network mac prefix if it exists or the cluster level default.
275 032a7d71 Dimitris Aragiorgis

276 032a7d71 Dimitris Aragiorgis
    """
277 032a7d71 Dimitris Aragiorgis
    prefix = None
278 9ccacbc8 Dimitris Aragiorgis
    if net_uuid:
279 1b68f268 Helga Velroyen
      nobj = self._UnlockedGetNetwork(net_uuid)
280 1b68f268 Helga Velroyen
      if nobj.mac_prefix:
281 1b68f268 Helga Velroyen
        prefix = nobj.mac_prefix
282 032a7d71 Dimitris Aragiorgis
283 032a7d71 Dimitris Aragiorgis
    return prefix
284 032a7d71 Dimitris Aragiorgis
285 032a7d71 Dimitris Aragiorgis
  def _GenerateOneMAC(self, prefix=None):
286 032a7d71 Dimitris Aragiorgis
    """Return a function that randomly generates a MAC suffic
287 032a7d71 Dimitris Aragiorgis
       and appends it to the given prefix. If prefix is not given get
288 032a7d71 Dimitris Aragiorgis
       the cluster level default.
289 032a7d71 Dimitris Aragiorgis

290 032a7d71 Dimitris Aragiorgis
    """
291 032a7d71 Dimitris Aragiorgis
    if not prefix:
292 032a7d71 Dimitris Aragiorgis
      prefix = self._config_data.cluster.mac_prefix
293 032a7d71 Dimitris Aragiorgis
294 032a7d71 Dimitris Aragiorgis
    def GenMac():
295 032a7d71 Dimitris Aragiorgis
      byte1 = random.randrange(0, 256)
296 032a7d71 Dimitris Aragiorgis
      byte2 = random.randrange(0, 256)
297 032a7d71 Dimitris Aragiorgis
      byte3 = random.randrange(0, 256)
298 032a7d71 Dimitris Aragiorgis
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
299 032a7d71 Dimitris Aragiorgis
      return mac
300 032a7d71 Dimitris Aragiorgis
301 032a7d71 Dimitris Aragiorgis
    return GenMac
302 032a7d71 Dimitris Aragiorgis
303 8a147bba René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
304 9ccacbc8 Dimitris Aragiorgis
  def GenerateMAC(self, net_uuid, ec_id):
305 a8083063 Iustin Pop
    """Generate a MAC for an instance.
306 a8083063 Iustin Pop

307 a8083063 Iustin Pop
    This should check the current instances for duplicates.
308 a8083063 Iustin Pop

309 a8083063 Iustin Pop
    """
310 36b66e6e Guido Trotter
    existing = self._AllMACs()
311 9ccacbc8 Dimitris Aragiorgis
    prefix = self._UnlockedGetNetworkMACPrefix(net_uuid)
312 032a7d71 Dimitris Aragiorgis
    gen_mac = self._GenerateOneMAC(prefix)
313 a0af6c80 Dimitris Aragiorgis
    return self._temporary_ids.Generate(existing, gen_mac, ec_id)
314 a8083063 Iustin Pop
315 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
316 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
317 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
318 1862d460 Alexander Schreiber

319 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
320 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
321 1862d460 Alexander Schreiber

322 1862d460 Alexander Schreiber
    """
323 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
324 36b66e6e Guido Trotter
    if mac in all_macs:
325 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
326 36b66e6e Guido Trotter
    else:
327 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
328 1862d460 Alexander Schreiber
329 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedCommitTemporaryIps(self, ec_id):
330 ad4a9ae7 Dimitris Aragiorgis
    """Commit all reserved IP address to their respective pools
331 ad4a9ae7 Dimitris Aragiorgis

332 ad4a9ae7 Dimitris Aragiorgis
    """
333 ad4a9ae7 Dimitris Aragiorgis
    for action, address, net_uuid in self._temporary_ips.GetECReserved(ec_id):
334 ad4a9ae7 Dimitris Aragiorgis
      self._UnlockedCommitIp(action, net_uuid, address)
335 ad4a9ae7 Dimitris Aragiorgis
336 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedCommitIp(self, action, net_uuid, address):
337 ad4a9ae7 Dimitris Aragiorgis
    """Commit a reserved IP address to an IP pool.
338 ad4a9ae7 Dimitris Aragiorgis

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

341 ad4a9ae7 Dimitris Aragiorgis
    """
342 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
343 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
344 e81eef56 Dimitris Aragiorgis
    if action == constants.RESERVE_ACTION:
345 ad4a9ae7 Dimitris Aragiorgis
      pool.Reserve(address)
346 e81eef56 Dimitris Aragiorgis
    elif action == constants.RELEASE_ACTION:
347 ad4a9ae7 Dimitris Aragiorgis
      pool.Release(address)
348 ad4a9ae7 Dimitris Aragiorgis
349 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedReleaseIp(self, net_uuid, address, ec_id):
350 ad4a9ae7 Dimitris Aragiorgis
    """Give a specific IP address back to an IP pool.
351 ad4a9ae7 Dimitris Aragiorgis

352 ad4a9ae7 Dimitris Aragiorgis
    The IP address is returned to the IP pool designated by pool_id and marked
353 ad4a9ae7 Dimitris Aragiorgis
    as reserved.
354 ad4a9ae7 Dimitris Aragiorgis

355 ad4a9ae7 Dimitris Aragiorgis
    """
356 e81eef56 Dimitris Aragiorgis
    self._temporary_ips.Reserve(ec_id,
357 e81eef56 Dimitris Aragiorgis
                                (constants.RELEASE_ACTION, address, net_uuid))
358 ad4a9ae7 Dimitris Aragiorgis
359 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
360 9ccacbc8 Dimitris Aragiorgis
  def ReleaseIp(self, net_uuid, address, ec_id):
361 ad4a9ae7 Dimitris Aragiorgis
    """Give a specified IP address back to an IP pool.
362 ad4a9ae7 Dimitris Aragiorgis

363 ad4a9ae7 Dimitris Aragiorgis
    This is just a wrapper around _UnlockedReleaseIp.
364 ad4a9ae7 Dimitris Aragiorgis

365 ad4a9ae7 Dimitris Aragiorgis
    """
366 9ccacbc8 Dimitris Aragiorgis
    if net_uuid:
367 9ccacbc8 Dimitris Aragiorgis
      self._UnlockedReleaseIp(net_uuid, address, ec_id)
368 ad4a9ae7 Dimitris Aragiorgis
369 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
370 9ccacbc8 Dimitris Aragiorgis
  def GenerateIp(self, net_uuid, ec_id):
371 ad4a9ae7 Dimitris Aragiorgis
    """Find a free IPv4 address for an instance.
372 ad4a9ae7 Dimitris Aragiorgis

373 ad4a9ae7 Dimitris Aragiorgis
    """
374 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
375 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
376 ad4a9ae7 Dimitris Aragiorgis
377 ad4a9ae7 Dimitris Aragiorgis
    def gen_one():
378 ad4a9ae7 Dimitris Aragiorgis
      try:
379 1f1d3bf2 Dimitris Aragiorgis
        ip = pool.GenerateFree()
380 1f1d3bf2 Dimitris Aragiorgis
      except errors.AddressPoolError:
381 ad4a9ae7 Dimitris Aragiorgis
        raise errors.ReservationError("Cannot generate IP. Network is full")
382 e81eef56 Dimitris Aragiorgis
      return (constants.RESERVE_ACTION, ip, net_uuid)
383 ad4a9ae7 Dimitris Aragiorgis
384 beb81ea5 Dimitris Aragiorgis
    _, address, _ = self._temporary_ips.Generate([], gen_one, ec_id)
385 ad4a9ae7 Dimitris Aragiorgis
    return address
386 ad4a9ae7 Dimitris Aragiorgis
387 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedReserveIp(self, net_uuid, address, ec_id):
388 ad4a9ae7 Dimitris Aragiorgis
    """Reserve a given IPv4 address for use by an instance.
389 ad4a9ae7 Dimitris Aragiorgis

390 ad4a9ae7 Dimitris Aragiorgis
    """
391 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
392 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
393 ad4a9ae7 Dimitris Aragiorgis
    try:
394 ad4a9ae7 Dimitris Aragiorgis
      isreserved = pool.IsReserved(address)
395 ad4a9ae7 Dimitris Aragiorgis
    except errors.AddressPoolError:
396 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address not in network")
397 ad4a9ae7 Dimitris Aragiorgis
    if isreserved:
398 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address already in use")
399 ad4a9ae7 Dimitris Aragiorgis
400 e81eef56 Dimitris Aragiorgis
    return self._temporary_ips.Reserve(ec_id,
401 e81eef56 Dimitris Aragiorgis
                                       (constants.RESERVE_ACTION,
402 e81eef56 Dimitris Aragiorgis
                                        address, net_uuid))
403 ad4a9ae7 Dimitris Aragiorgis
404 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
405 9ccacbc8 Dimitris Aragiorgis
  def ReserveIp(self, net_uuid, address, ec_id):
406 ad4a9ae7 Dimitris Aragiorgis
    """Reserve a given IPv4 address for use by an instance.
407 ad4a9ae7 Dimitris Aragiorgis

408 ad4a9ae7 Dimitris Aragiorgis
    """
409 9ccacbc8 Dimitris Aragiorgis
    if net_uuid:
410 9ccacbc8 Dimitris Aragiorgis
      return self._UnlockedReserveIp(net_uuid, address, ec_id)
411 ad4a9ae7 Dimitris Aragiorgis
412 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
413 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
414 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
415 d8aee57e Iustin Pop

416 d8aee57e Iustin Pop
    @type lv_name: string
417 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
418 d8aee57e Iustin Pop

419 d8aee57e Iustin Pop
    """
420 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
421 d8aee57e Iustin Pop
    if lv_name in all_lvs:
422 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
423 d8aee57e Iustin Pop
    else:
424 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
425 d8aee57e Iustin Pop
426 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
427 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
428 f9518d38 Iustin Pop
    """Generate a DRBD secret.
429 f9518d38 Iustin Pop

430 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
431 f9518d38 Iustin Pop

432 f9518d38 Iustin Pop
    """
433 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
434 afa1386e Guido Trotter
                                            utils.GenerateSecret,
435 afa1386e Guido Trotter
                                            ec_id)
436 8d9c3bef Michael Hanselmann
437 34e54ebc Iustin Pop
  def _AllLVs(self):
438 923b1523 Iustin Pop
    """Compute the list of all LVs.
439 923b1523 Iustin Pop

440 923b1523 Iustin Pop
    """
441 923b1523 Iustin Pop
    lvnames = set()
442 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
443 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
444 923b1523 Iustin Pop
      for lv_list in node_data.values():
445 923b1523 Iustin Pop
        lvnames.update(lv_list)
446 923b1523 Iustin Pop
    return lvnames
447 923b1523 Iustin Pop
448 b87a9c5f Christos Stavrakakis
  def _AllDisks(self):
449 b87a9c5f Christos Stavrakakis
    """Compute the list of all Disks.
450 b87a9c5f Christos Stavrakakis

451 b87a9c5f Christos Stavrakakis
    """
452 b87a9c5f Christos Stavrakakis
    disks = []
453 b87a9c5f Christos Stavrakakis
    for instance in self._config_data.instances.values():
454 b87a9c5f Christos Stavrakakis
      disks.extend(instance.disks)
455 b87a9c5f Christos Stavrakakis
    return disks
456 b87a9c5f Christos Stavrakakis
457 b87a9c5f Christos Stavrakakis
  def _AllNICs(self):
458 b87a9c5f Christos Stavrakakis
    """Compute the list of all NICs.
459 b87a9c5f Christos Stavrakakis

460 b87a9c5f Christos Stavrakakis
    """
461 b87a9c5f Christos Stavrakakis
    nics = []
462 b87a9c5f Christos Stavrakakis
    for instance in self._config_data.instances.values():
463 b87a9c5f Christos Stavrakakis
      nics.extend(instance.nics)
464 b87a9c5f Christos Stavrakakis
    return nics
465 b87a9c5f Christos Stavrakakis
466 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
467 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
468 34e54ebc Iustin Pop

469 34e54ebc Iustin Pop
    @type include_temporary: boolean
470 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
471 34e54ebc Iustin Pop
    @rtype: set
472 34e54ebc Iustin Pop
    @return: a set of IDs
473 34e54ebc Iustin Pop

474 34e54ebc Iustin Pop
    """
475 34e54ebc Iustin Pop
    existing = set()
476 34e54ebc Iustin Pop
    if include_temporary:
477 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
478 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
479 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
480 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
481 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
482 34e54ebc Iustin Pop
    return existing
483 34e54ebc Iustin Pop
484 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
485 430b923c Iustin Pop
    """Generate an unique UUID.
486 923b1523 Iustin Pop

487 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
488 923b1523 Iustin Pop
    duplicates.
489 923b1523 Iustin Pop

490 c41eea6e Iustin Pop
    @rtype: string
491 c41eea6e Iustin Pop
    @return: the unique id
492 923b1523 Iustin Pop

493 923b1523 Iustin Pop
    """
494 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
495 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
496 923b1523 Iustin Pop
497 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
498 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
499 430b923c Iustin Pop
    """Generate an unique ID.
500 430b923c Iustin Pop

501 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
502 430b923c Iustin Pop

503 4fae38c5 Guido Trotter
    @type ec_id: string
504 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
505 34d657ba Iustin Pop

506 34d657ba Iustin Pop
    """
507 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
508 34d657ba Iustin Pop
509 a8083063 Iustin Pop
  def _AllMACs(self):
510 a8083063 Iustin Pop
    """Return all MACs present in the config.
511 a8083063 Iustin Pop

512 c41eea6e Iustin Pop
    @rtype: list
513 c41eea6e Iustin Pop
    @return: the list of all MACs
514 c41eea6e Iustin Pop

515 a8083063 Iustin Pop
    """
516 a8083063 Iustin Pop
    result = []
517 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
518 a8083063 Iustin Pop
      for nic in instance.nics:
519 a8083063 Iustin Pop
        result.append(nic.mac)
520 a8083063 Iustin Pop
521 a8083063 Iustin Pop
    return result
522 a8083063 Iustin Pop
523 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
524 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
525 f9518d38 Iustin Pop

526 c41eea6e Iustin Pop
    @rtype: list
527 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
528 c41eea6e Iustin Pop

529 f9518d38 Iustin Pop
    """
530 f9518d38 Iustin Pop
    def helper(disk, result):
531 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
532 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
533 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
534 f9518d38 Iustin Pop
      if disk.children:
535 f9518d38 Iustin Pop
        for child in disk.children:
536 f9518d38 Iustin Pop
          helper(child, result)
537 f9518d38 Iustin Pop
538 f9518d38 Iustin Pop
    result = []
539 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
540 f9518d38 Iustin Pop
      for disk in instance.disks:
541 f9518d38 Iustin Pop
        helper(disk, result)
542 f9518d38 Iustin Pop
543 f9518d38 Iustin Pop
    return result
544 f9518d38 Iustin Pop
545 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
546 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
547 4b98ac29 Iustin Pop

548 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
549 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
550 4b98ac29 Iustin Pop
    @type l_ids: list
551 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
552 4b98ac29 Iustin Pop
    @type p_ids: list
553 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
554 4b98ac29 Iustin Pop
    @rtype: list
555 4b98ac29 Iustin Pop
    @return: a list of error messages
556 4b98ac29 Iustin Pop

557 4b98ac29 Iustin Pop
    """
558 4b98ac29 Iustin Pop
    result = []
559 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
560 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
561 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
562 25ae22e4 Iustin Pop
      else:
563 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
564 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
565 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
566 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
567 25ae22e4 Iustin Pop
      else:
568 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
569 4b98ac29 Iustin Pop
570 4b98ac29 Iustin Pop
    if disk.children:
571 4b98ac29 Iustin Pop
      for child in disk.children:
572 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
573 4b98ac29 Iustin Pop
    return result
574 4b98ac29 Iustin Pop
575 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
576 a8efbb40 Iustin Pop
    """Verify function.
577 a8efbb40 Iustin Pop

578 4a89c54a Iustin Pop
    @rtype: list
579 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
580 4a89c54a Iustin Pop
        configuration errors
581 4a89c54a Iustin Pop

582 a8083063 Iustin Pop
    """
583 b459a848 Andrea Spadaccini
    # pylint: disable=R0914
584 a8083063 Iustin Pop
    result = []
585 a8083063 Iustin Pop
    seen_macs = []
586 48ce9fd9 Iustin Pop
    ports = {}
587 a8083063 Iustin Pop
    data = self._config_data
588 7e01d204 Iustin Pop
    cluster = data.cluster
589 4b98ac29 Iustin Pop
    seen_lids = []
590 4b98ac29 Iustin Pop
    seen_pids = []
591 9a5fba23 Guido Trotter
592 9a5fba23 Guido Trotter
    # global cluster checks
593 7e01d204 Iustin Pop
    if not cluster.enabled_hypervisors:
594 9a5fba23 Guido Trotter
      result.append("enabled hypervisors list doesn't have any entries")
595 7e01d204 Iustin Pop
    invalid_hvs = set(cluster.enabled_hypervisors) - constants.HYPER_TYPES
596 9a5fba23 Guido Trotter
    if invalid_hvs:
597 9a5fba23 Guido Trotter
      result.append("enabled hypervisors contains invalid entries: %s" %
598 3da6e141 Helga Velroyen
                    utils.CommaJoin(invalid_hvs))
599 7e01d204 Iustin Pop
    missing_hvp = (set(cluster.enabled_hypervisors) -
600 7e01d204 Iustin Pop
                   set(cluster.hvparams.keys()))
601 9f3ac970 Iustin Pop
    if missing_hvp:
602 9f3ac970 Iustin Pop
      result.append("hypervisor parameters missing for the enabled"
603 9f3ac970 Iustin Pop
                    " hypervisor(s) %s" % utils.CommaJoin(missing_hvp))
604 9a5fba23 Guido Trotter
605 3da6e141 Helga Velroyen
    if not cluster.enabled_disk_templates:
606 3da6e141 Helga Velroyen
      result.append("enabled disk templates list doesn't have any entries")
607 3da6e141 Helga Velroyen
    invalid_disk_templates = set(cluster.enabled_disk_templates) \
608 3da6e141 Helga Velroyen
                               - constants.DISK_TEMPLATES
609 3da6e141 Helga Velroyen
    if invalid_disk_templates:
610 3da6e141 Helga Velroyen
      result.append("enabled disk templates list contains invalid entries:"
611 3da6e141 Helga Velroyen
                    " %s" % utils.CommaJoin(invalid_disk_templates))
612 3da6e141 Helga Velroyen
613 7e01d204 Iustin Pop
    if cluster.master_node not in data.nodes:
614 9a5fba23 Guido Trotter
      result.append("cluster has invalid primary node '%s'" %
615 7e01d204 Iustin Pop
                    cluster.master_node)
616 9a5fba23 Guido Trotter
617 26f2fd8d Iustin Pop
    def _helper(owner, attr, value, template):
618 26f2fd8d Iustin Pop
      try:
619 26f2fd8d Iustin Pop
        utils.ForceDictType(value, template)
620 26f2fd8d Iustin Pop
      except errors.GenericError, err:
621 26f2fd8d Iustin Pop
        result.append("%s has invalid %s: %s" % (owner, attr, err))
622 26f2fd8d Iustin Pop
623 26f2fd8d Iustin Pop
    def _helper_nic(owner, params):
624 26f2fd8d Iustin Pop
      try:
625 26f2fd8d Iustin Pop
        objects.NIC.CheckParameterSyntax(params)
626 26f2fd8d Iustin Pop
      except errors.ConfigurationError, err:
627 26f2fd8d Iustin Pop
        result.append("%s has invalid nicparams: %s" % (owner, err))
628 26f2fd8d Iustin Pop
629 da5f09ef Bernardo Dal Seno
    def _helper_ipolicy(owner, ipolicy, iscluster):
630 918eb80b Agata Murawska
      try:
631 da5f09ef Bernardo Dal Seno
        objects.InstancePolicy.CheckParameterSyntax(ipolicy, iscluster)
632 918eb80b Agata Murawska
      except errors.ConfigurationError, err:
633 918eb80b Agata Murawska
        result.append("%s has invalid instance policy: %s" % (owner, err))
634 da5f09ef Bernardo Dal Seno
      for key, value in ipolicy.items():
635 da5f09ef Bernardo Dal Seno
        if key == constants.ISPECS_MINMAX:
636 41044e04 Bernardo Dal Seno
          for k in range(len(value)):
637 41044e04 Bernardo Dal Seno
            _helper_ispecs(owner, "ipolicy/%s[%s]" % (key, k), value[k])
638 da5f09ef Bernardo Dal Seno
        elif key == constants.ISPECS_STD:
639 da5f09ef Bernardo Dal Seno
          _helper(owner, "ipolicy/" + key, value,
640 da5f09ef Bernardo Dal Seno
                  constants.ISPECS_PARAMETER_TYPES)
641 2cc673a3 Iustin Pop
        else:
642 2cc673a3 Iustin Pop
          # FIXME: assuming list type
643 ff6c5e55 Iustin Pop
          if key in constants.IPOLICY_PARAMETERS:
644 ff6c5e55 Iustin Pop
            exp_type = float
645 ff6c5e55 Iustin Pop
          else:
646 ff6c5e55 Iustin Pop
            exp_type = list
647 ff6c5e55 Iustin Pop
          if not isinstance(value, exp_type):
648 2cc673a3 Iustin Pop
            result.append("%s has invalid instance policy: for %s,"
649 ff6c5e55 Iustin Pop
                          " expecting %s, got %s" %
650 ff6c5e55 Iustin Pop
                          (owner, key, exp_type.__name__, type(value)))
651 918eb80b Agata Murawska
652 da5f09ef Bernardo Dal Seno
    def _helper_ispecs(owner, parentkey, params):
653 da5f09ef Bernardo Dal Seno
      for (key, value) in params.items():
654 da5f09ef Bernardo Dal Seno
        fullkey = "/".join([parentkey, key])
655 da5f09ef Bernardo Dal Seno
        _helper(owner, fullkey, value, constants.ISPECS_PARAMETER_TYPES)
656 da5f09ef Bernardo Dal Seno
657 26f2fd8d Iustin Pop
    # check cluster parameters
658 26f2fd8d Iustin Pop
    _helper("cluster", "beparams", cluster.SimpleFillBE({}),
659 26f2fd8d Iustin Pop
            constants.BES_PARAMETER_TYPES)
660 26f2fd8d Iustin Pop
    _helper("cluster", "nicparams", cluster.SimpleFillNIC({}),
661 26f2fd8d Iustin Pop
            constants.NICS_PARAMETER_TYPES)
662 26f2fd8d Iustin Pop
    _helper_nic("cluster", cluster.SimpleFillNIC({}))
663 26f2fd8d Iustin Pop
    _helper("cluster", "ndparams", cluster.SimpleFillND({}),
664 26f2fd8d Iustin Pop
            constants.NDS_PARAMETER_TYPES)
665 da5f09ef Bernardo Dal Seno
    _helper_ipolicy("cluster", cluster.ipolicy, True)
666 26f2fd8d Iustin Pop
667 9a5fba23 Guido Trotter
    # per-instance checks
668 da4a52a3 Thomas Thrainer
    for instance_uuid in data.instances:
669 da4a52a3 Thomas Thrainer
      instance = data.instances[instance_uuid]
670 da4a52a3 Thomas Thrainer
      if instance.uuid != instance_uuid:
671 da4a52a3 Thomas Thrainer
        result.append("instance '%s' is indexed by wrong UUID '%s'" %
672 da4a52a3 Thomas Thrainer
                      (instance.name, instance_uuid))
673 a8083063 Iustin Pop
      if instance.primary_node not in data.nodes:
674 8522ceeb Iustin Pop
        result.append("instance '%s' has invalid primary node '%s'" %
675 da4a52a3 Thomas Thrainer
                      (instance.name, instance.primary_node))
676 a8083063 Iustin Pop
      for snode in instance.secondary_nodes:
677 a8083063 Iustin Pop
        if snode not in data.nodes:
678 8522ceeb Iustin Pop
          result.append("instance '%s' has invalid secondary node '%s'" %
679 da4a52a3 Thomas Thrainer
                        (instance.name, snode))
680 a8083063 Iustin Pop
      for idx, nic in enumerate(instance.nics):
681 a8083063 Iustin Pop
        if nic.mac in seen_macs:
682 8522ceeb Iustin Pop
          result.append("instance '%s' has NIC %d mac %s duplicate" %
683 da4a52a3 Thomas Thrainer
                        (instance.name, idx, nic.mac))
684 a8083063 Iustin Pop
        else:
685 a8083063 Iustin Pop
          seen_macs.append(nic.mac)
686 26f2fd8d Iustin Pop
        if nic.nicparams:
687 26f2fd8d Iustin Pop
          filled = cluster.SimpleFillNIC(nic.nicparams)
688 26f2fd8d Iustin Pop
          owner = "instance %s nic %d" % (instance.name, idx)
689 26f2fd8d Iustin Pop
          _helper(owner, "nicparams",
690 26f2fd8d Iustin Pop
                  filled, constants.NICS_PARAMETER_TYPES)
691 26f2fd8d Iustin Pop
          _helper_nic(owner, filled)
692 26f2fd8d Iustin Pop
693 3da6e141 Helga Velroyen
      # disk template checks
694 3da6e141 Helga Velroyen
      if not instance.disk_template in data.cluster.enabled_disk_templates:
695 3da6e141 Helga Velroyen
        result.append("instance '%s' uses the disabled disk template '%s'." %
696 da4a52a3 Thomas Thrainer
                      (instance.name, instance.disk_template))
697 3da6e141 Helga Velroyen
698 26f2fd8d Iustin Pop
      # parameter checks
699 26f2fd8d Iustin Pop
      if instance.beparams:
700 26f2fd8d Iustin Pop
        _helper("instance %s" % instance.name, "beparams",
701 26f2fd8d Iustin Pop
                cluster.FillBE(instance), constants.BES_PARAMETER_TYPES)
702 48ce9fd9 Iustin Pop
703 48ce9fd9 Iustin Pop
      # gather the drbd ports for duplicate checks
704 e2569c1d Michael Hanselmann
      for (idx, dsk) in enumerate(instance.disks):
705 48ce9fd9 Iustin Pop
        if dsk.dev_type in constants.LDS_DRBD:
706 48ce9fd9 Iustin Pop
          tcp_port = dsk.logical_id[2]
707 48ce9fd9 Iustin Pop
          if tcp_port not in ports:
708 48ce9fd9 Iustin Pop
            ports[tcp_port] = []
709 e2569c1d Michael Hanselmann
          ports[tcp_port].append((instance.name, "drbd disk %s" % idx))
710 48ce9fd9 Iustin Pop
      # gather network port reservation
711 48ce9fd9 Iustin Pop
      net_port = getattr(instance, "network_port", None)
712 48ce9fd9 Iustin Pop
      if net_port is not None:
713 48ce9fd9 Iustin Pop
        if net_port not in ports:
714 48ce9fd9 Iustin Pop
          ports[net_port] = []
715 48ce9fd9 Iustin Pop
        ports[net_port].append((instance.name, "network port"))
716 48ce9fd9 Iustin Pop
717 332d0e37 Iustin Pop
      # instance disk verify
718 332d0e37 Iustin Pop
      for idx, disk in enumerate(instance.disks):
719 332d0e37 Iustin Pop
        result.extend(["instance '%s' disk %d error: %s" %
720 332d0e37 Iustin Pop
                       (instance.name, idx, msg) for msg in disk.Verify()])
721 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(disk, seen_lids, seen_pids))
722 332d0e37 Iustin Pop
723 82c54b5b Michael Hanselmann
      wrong_names = _CheckInstanceDiskIvNames(instance.disks)
724 82c54b5b Michael Hanselmann
      if wrong_names:
725 82c54b5b Michael Hanselmann
        tmp = "; ".join(("name of disk %s should be '%s', but is '%s'" %
726 82c54b5b Michael Hanselmann
                         (idx, exp_name, actual_name))
727 82c54b5b Michael Hanselmann
                        for (idx, exp_name, actual_name) in wrong_names)
728 82c54b5b Michael Hanselmann
729 82c54b5b Michael Hanselmann
        result.append("Instance '%s' has wrongly named disks: %s" %
730 82c54b5b Michael Hanselmann
                      (instance.name, tmp))
731 82c54b5b Michael Hanselmann
732 48ce9fd9 Iustin Pop
    # cluster-wide pool of free ports
733 7e01d204 Iustin Pop
    for free_port in cluster.tcpudp_port_pool:
734 48ce9fd9 Iustin Pop
      if free_port not in ports:
735 48ce9fd9 Iustin Pop
        ports[free_port] = []
736 48ce9fd9 Iustin Pop
      ports[free_port].append(("cluster", "port marked as free"))
737 48ce9fd9 Iustin Pop
738 48ce9fd9 Iustin Pop
    # compute tcp/udp duplicate ports
739 48ce9fd9 Iustin Pop
    keys = ports.keys()
740 48ce9fd9 Iustin Pop
    keys.sort()
741 48ce9fd9 Iustin Pop
    for pnum in keys:
742 48ce9fd9 Iustin Pop
      pdata = ports[pnum]
743 48ce9fd9 Iustin Pop
      if len(pdata) > 1:
744 1f864b60 Iustin Pop
        txt = utils.CommaJoin(["%s/%s" % val for val in pdata])
745 48ce9fd9 Iustin Pop
        result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
746 48ce9fd9 Iustin Pop
747 48ce9fd9 Iustin Pop
    # highest used tcp port check
748 48ce9fd9 Iustin Pop
    if keys:
749 7e01d204 Iustin Pop
      if keys[-1] > cluster.highest_used_port:
750 48ce9fd9 Iustin Pop
        result.append("Highest used port mismatch, saved %s, computed %s" %
751 7e01d204 Iustin Pop
                      (cluster.highest_used_port, keys[-1]))
752 a8efbb40 Iustin Pop
753 7e01d204 Iustin Pop
    if not data.nodes[cluster.master_node].master_candidate:
754 3a26773f Iustin Pop
      result.append("Master node is not a master candidate")
755 3a26773f Iustin Pop
756 4a89c54a Iustin Pop
    # master candidate checks
757 e623dbe3 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats()
758 ec0292f1 Iustin Pop
    if mc_now < mc_max:
759 ec0292f1 Iustin Pop
      result.append("Not enough master candidates: actual %d, target %d" %
760 ec0292f1 Iustin Pop
                    (mc_now, mc_max))
761 48ce9fd9 Iustin Pop
762 5bf07049 Iustin Pop
    # node checks
763 1c3231aa Thomas Thrainer
    for node_uuid, node in data.nodes.items():
764 1c3231aa Thomas Thrainer
      if node.uuid != node_uuid:
765 1c3231aa Thomas Thrainer
        result.append("Node '%s' is indexed by wrong UUID '%s'" %
766 1c3231aa Thomas Thrainer
                      (node.name, node_uuid))
767 5bf07049 Iustin Pop
      if [node.master_candidate, node.drained, node.offline].count(True) > 1:
768 5bf07049 Iustin Pop
        result.append("Node %s state is invalid: master_candidate=%s,"
769 5bf07049 Iustin Pop
                      " drain=%s, offline=%s" %
770 3d889a7d Michael Hanselmann
                      (node.name, node.master_candidate, node.drained,
771 5bf07049 Iustin Pop
                       node.offline))
772 26f2fd8d Iustin Pop
      if node.group not in data.nodegroups:
773 26f2fd8d Iustin Pop
        result.append("Node '%s' has invalid group '%s'" %
774 26f2fd8d Iustin Pop
                      (node.name, node.group))
775 26f2fd8d Iustin Pop
      else:
776 26f2fd8d Iustin Pop
        _helper("node %s" % node.name, "ndparams",
777 26f2fd8d Iustin Pop
                cluster.FillND(node, data.nodegroups[node.group]),
778 26f2fd8d Iustin Pop
                constants.NDS_PARAMETER_TYPES)
779 3697def0 Bernardo Dal Seno
      used_globals = constants.NDC_GLOBALS.intersection(node.ndparams)
780 3697def0 Bernardo Dal Seno
      if used_globals:
781 3697def0 Bernardo Dal Seno
        result.append("Node '%s' has some global parameters set: %s" %
782 3697def0 Bernardo Dal Seno
                      (node.name, utils.CommaJoin(used_globals)))
783 5bf07049 Iustin Pop
784 6520ba14 Guido Trotter
    # nodegroups checks
785 ace16501 Guido Trotter
    nodegroups_names = set()
786 6520ba14 Guido Trotter
    for nodegroup_uuid in data.nodegroups:
787 6520ba14 Guido Trotter
      nodegroup = data.nodegroups[nodegroup_uuid]
788 6520ba14 Guido Trotter
      if nodegroup.uuid != nodegroup_uuid:
789 913cc25e Adeodato Simo
        result.append("node group '%s' (uuid: '%s') indexed by wrong uuid '%s'"
790 6520ba14 Guido Trotter
                      % (nodegroup.name, nodegroup.uuid, nodegroup_uuid))
791 485ba212 Guido Trotter
      if utils.UUID_RE.match(nodegroup.name.lower()):
792 913cc25e Adeodato Simo
        result.append("node group '%s' (uuid: '%s') has uuid-like name" %
793 485ba212 Guido Trotter
                      (nodegroup.name, nodegroup.uuid))
794 ace16501 Guido Trotter
      if nodegroup.name in nodegroups_names:
795 913cc25e Adeodato Simo
        result.append("duplicate node group name '%s'" % nodegroup.name)
796 ace16501 Guido Trotter
      else:
797 ace16501 Guido Trotter
        nodegroups_names.add(nodegroup.name)
798 81e3ab4f Agata Murawska
      group_name = "group %s" % nodegroup.name
799 8b057218 René Nussbaumer
      _helper_ipolicy(group_name, cluster.SimpleFillIPolicy(nodegroup.ipolicy),
800 8b057218 René Nussbaumer
                      False)
801 26f2fd8d Iustin Pop
      if nodegroup.ndparams:
802 81e3ab4f Agata Murawska
        _helper(group_name, "ndparams",
803 26f2fd8d Iustin Pop
                cluster.SimpleFillND(nodegroup.ndparams),
804 26f2fd8d Iustin Pop
                constants.NDS_PARAMETER_TYPES)
805 26f2fd8d Iustin Pop
806 4a89c54a Iustin Pop
    # drbd minors check
807 1122eb25 Iustin Pop
    _, duplicates = self._UnlockedComputeDRBDMap()
808 4a89c54a Iustin Pop
    for node, minor, instance_a, instance_b in duplicates:
809 4a89c54a Iustin Pop
      result.append("DRBD minor %d on node %s is assigned twice to instances"
810 4a89c54a Iustin Pop
                    " %s and %s" % (minor, node, instance_a, instance_b))
811 4a89c54a Iustin Pop
812 0ce8f948 Iustin Pop
    # IP checks
813 7e01d204 Iustin Pop
    default_nicparams = cluster.nicparams[constants.PP_DEFAULT]
814 b8716596 Michael Hanselmann
    ips = {}
815 b8716596 Michael Hanselmann
816 b8716596 Michael Hanselmann
    def _AddIpAddress(ip, name):
817 b8716596 Michael Hanselmann
      ips.setdefault(ip, []).append(name)
818 b8716596 Michael Hanselmann
819 7e01d204 Iustin Pop
    _AddIpAddress(cluster.master_ip, "cluster_ip")
820 0ce8f948 Iustin Pop
821 0ce8f948 Iustin Pop
    for node in data.nodes.values():
822 b8716596 Michael Hanselmann
      _AddIpAddress(node.primary_ip, "node:%s/primary" % node.name)
823 0ce8f948 Iustin Pop
      if node.secondary_ip != node.primary_ip:
824 b8716596 Michael Hanselmann
        _AddIpAddress(node.secondary_ip, "node:%s/secondary" % node.name)
825 b8716596 Michael Hanselmann
826 b8716596 Michael Hanselmann
    for instance in data.instances.values():
827 b8716596 Michael Hanselmann
      for idx, nic in enumerate(instance.nics):
828 b8716596 Michael Hanselmann
        if nic.ip is None:
829 b8716596 Michael Hanselmann
          continue
830 b8716596 Michael Hanselmann
831 b8716596 Michael Hanselmann
        nicparams = objects.FillDict(default_nicparams, nic.nicparams)
832 b8716596 Michael Hanselmann
        nic_mode = nicparams[constants.NIC_MODE]
833 b8716596 Michael Hanselmann
        nic_link = nicparams[constants.NIC_LINK]
834 b8716596 Michael Hanselmann
835 b8716596 Michael Hanselmann
        if nic_mode == constants.NIC_MODE_BRIDGED:
836 b8716596 Michael Hanselmann
          link = "bridge:%s" % nic_link
837 b8716596 Michael Hanselmann
        elif nic_mode == constants.NIC_MODE_ROUTED:
838 b8716596 Michael Hanselmann
          link = "route:%s" % nic_link
839 b8716596 Michael Hanselmann
        else:
840 b8716596 Michael Hanselmann
          raise errors.ProgrammerError("NIC mode '%s' not handled" % nic_mode)
841 b8716596 Michael Hanselmann
842 ad4a9ae7 Dimitris Aragiorgis
        _AddIpAddress("%s/%s/%s" % (link, nic.ip, nic.network),
843 b8716596 Michael Hanselmann
                      "instance:%s/nic:%d" % (instance.name, idx))
844 0ce8f948 Iustin Pop
845 0ce8f948 Iustin Pop
    for ip, owners in ips.items():
846 0ce8f948 Iustin Pop
      if len(owners) > 1:
847 0ce8f948 Iustin Pop
        result.append("IP address %s is used by multiple owners: %s" %
848 1f864b60 Iustin Pop
                      (ip, utils.CommaJoin(owners)))
849 b8716596 Michael Hanselmann
850 a8083063 Iustin Pop
    return result
851 a8083063 Iustin Pop
852 4a89c54a Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
853 4a89c54a Iustin Pop
  def VerifyConfig(self):
854 4a89c54a Iustin Pop
    """Verify function.
855 4a89c54a Iustin Pop

856 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
857 4a89c54a Iustin Pop

858 4a89c54a Iustin Pop
    @rtype: list
859 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
860 4a89c54a Iustin Pop
        configuration errors
861 4a89c54a Iustin Pop

862 4a89c54a Iustin Pop
    """
863 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
864 4a89c54a Iustin Pop
865 1c3231aa Thomas Thrainer
  def _UnlockedSetDiskID(self, disk, node_uuid):
866 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
867 a8083063 Iustin Pop

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

870 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
871 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
872 a8083063 Iustin Pop
    node.
873 a8083063 Iustin Pop

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

876 a8083063 Iustin Pop
    """
877 a8083063 Iustin Pop
    if disk.children:
878 a8083063 Iustin Pop
      for child in disk.children:
879 1c3231aa Thomas Thrainer
        self._UnlockedSetDiskID(child, node_uuid)
880 a8083063 Iustin Pop
881 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
882 a8083063 Iustin Pop
      return
883 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
884 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
885 1c3231aa Thomas Thrainer
      if node_uuid not in (pnode, snode):
886 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
887 1c3231aa Thomas Thrainer
                                        node_uuid)
888 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
889 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
890 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
891 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
892 a8083063 Iustin Pop
                                        " for %s" % str(disk))
893 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
894 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
895 1c3231aa Thomas Thrainer
      if pnode == node_uuid:
896 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
897 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
898 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
899 a8083063 Iustin Pop
    else:
900 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
901 a8083063 Iustin Pop
    return
902 a8083063 Iustin Pop
903 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
904 1c3231aa Thomas Thrainer
  def SetDiskID(self, disk, node_uuid):
905 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
906 f78ede4e Guido Trotter

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

909 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
910 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
911 f78ede4e Guido Trotter
    node.
912 f78ede4e Guido Trotter

913 f78ede4e Guido Trotter
    """
914 1c3231aa Thomas Thrainer
    return self._UnlockedSetDiskID(disk, node_uuid)
915 f78ede4e Guido Trotter
916 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
917 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
918 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
919 b2fddf63 Iustin Pop

920 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
921 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
922 3b3b1bca Dimitris Aragiorgis
        configuration is stable
923 3b3b1bca Dimitris Aragiorgis

924 b2fddf63 Iustin Pop
    """
925 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
926 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
927 264bb3c5 Michael Hanselmann
928 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
929 264bb3c5 Michael Hanselmann
930 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
931 b2fddf63 Iustin Pop
  def GetPortList(self):
932 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
933 264bb3c5 Michael Hanselmann

934 264bb3c5 Michael Hanselmann
    """
935 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
936 264bb3c5 Michael Hanselmann
937 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
938 a8083063 Iustin Pop
  def AllocatePort(self):
939 a8083063 Iustin Pop
    """Allocate a port.
940 a8083063 Iustin Pop

941 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
942 b2fddf63 Iustin Pop
    default port range (and in this case we increase
943 b2fddf63 Iustin Pop
    highest_used_port).
944 a8083063 Iustin Pop

945 a8083063 Iustin Pop
    """
946 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
947 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
948 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
949 264bb3c5 Michael Hanselmann
    else:
950 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
951 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
952 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
953 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
954 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
955 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
956 a8083063 Iustin Pop
957 a8083063 Iustin Pop
    self._WriteConfig()
958 a8083063 Iustin Pop
    return port
959 a8083063 Iustin Pop
960 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
961 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
962 a81c53c9 Iustin Pop

963 4a89c54a Iustin Pop
    @rtype: (dict, list)
964 da4a52a3 Thomas Thrainer
    @return: dictionary of node_uuid: dict of minor: instance_uuid;
965 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
966 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
967 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
968 4a89c54a Iustin Pop
        should raise an exception
969 a81c53c9 Iustin Pop

970 a81c53c9 Iustin Pop
    """
971 da4a52a3 Thomas Thrainer
    def _AppendUsedMinors(get_node_name_fn, instance, disk, used):
972 4a89c54a Iustin Pop
      duplicates = []
973 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
974 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
975 da4a52a3 Thomas Thrainer
        for node_uuid, minor in ((node_a, minor_a), (node_b, minor_b)):
976 b691385f Thomas Thrainer
          assert node_uuid in used, \
977 b691385f Thomas Thrainer
            ("Node '%s' of instance '%s' not found in node list" %
978 da4a52a3 Thomas Thrainer
             (get_node_name_fn(node_uuid), instance.name))
979 da4a52a3 Thomas Thrainer
          if minor in used[node_uuid]:
980 da4a52a3 Thomas Thrainer
            duplicates.append((node_uuid, minor, instance.uuid,
981 da4a52a3 Thomas Thrainer
                               used[node_uuid][minor]))
982 4a89c54a Iustin Pop
          else:
983 da4a52a3 Thomas Thrainer
            used[node_uuid][minor] = instance.uuid
984 a81c53c9 Iustin Pop
      if disk.children:
985 a81c53c9 Iustin Pop
        for child in disk.children:
986 da4a52a3 Thomas Thrainer
          duplicates.extend(_AppendUsedMinors(get_node_name_fn, instance, child,
987 da4a52a3 Thomas Thrainer
                                              used))
988 4a89c54a Iustin Pop
      return duplicates
989 a81c53c9 Iustin Pop
990 4a89c54a Iustin Pop
    duplicates = []
991 da4a52a3 Thomas Thrainer
    my_dict = dict((node_uuid, {}) for node_uuid in self._config_data.nodes)
992 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
993 79b26a7a Iustin Pop
      for disk in instance.disks:
994 da4a52a3 Thomas Thrainer
        duplicates.extend(_AppendUsedMinors(self._UnlockedGetNodeName,
995 da4a52a3 Thomas Thrainer
                                            instance, disk, my_dict))
996 da4a52a3 Thomas Thrainer
    for (node_uuid, minor), inst_uuid in self._temporary_drbds.iteritems():
997 da4a52a3 Thomas Thrainer
      if minor in my_dict[node_uuid] and my_dict[node_uuid][minor] != inst_uuid:
998 da4a52a3 Thomas Thrainer
        duplicates.append((node_uuid, minor, inst_uuid,
999 da4a52a3 Thomas Thrainer
                           my_dict[node_uuid][minor]))
1000 4a89c54a Iustin Pop
      else:
1001 da4a52a3 Thomas Thrainer
        my_dict[node_uuid][minor] = inst_uuid
1002 4a89c54a Iustin Pop
    return my_dict, duplicates
1003 a81c53c9 Iustin Pop
1004 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
1005 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
1006 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
1007 6d2e83d5 Iustin Pop

1008 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
1009 6d2e83d5 Iustin Pop

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

1014 6d2e83d5 Iustin Pop
    """
1015 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
1016 4a89c54a Iustin Pop
    if duplicates:
1017 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
1018 4a89c54a Iustin Pop
                                      str(duplicates))
1019 4a89c54a Iustin Pop
    return d_map
1020 6d2e83d5 Iustin Pop
1021 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
1022 da4a52a3 Thomas Thrainer
  def AllocateDRBDMinor(self, node_uuids, inst_uuid):
1023 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
1024 a81c53c9 Iustin Pop

1025 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
1026 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
1027 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
1028 a81c53c9 Iustin Pop
    order as the passed nodes.
1029 a81c53c9 Iustin Pop

1030 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1031 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which we allocate minors
1032 32388e6d Iustin Pop

1033 a81c53c9 Iustin Pop
    """
1034 da4a52a3 Thomas Thrainer
    assert isinstance(inst_uuid, basestring), \
1035 da4a52a3 Thomas Thrainer
           "Invalid argument '%s' passed to AllocateDRBDMinor" % inst_uuid
1036 32388e6d Iustin Pop
1037 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
1038 4a89c54a Iustin Pop
    if duplicates:
1039 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
1040 4a89c54a Iustin Pop
                                      str(duplicates))
1041 a81c53c9 Iustin Pop
    result = []
1042 1c3231aa Thomas Thrainer
    for nuuid in node_uuids:
1043 1c3231aa Thomas Thrainer
      ndata = d_map[nuuid]
1044 a81c53c9 Iustin Pop
      if not ndata:
1045 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
1046 a81c53c9 Iustin Pop
        result.append(0)
1047 da4a52a3 Thomas Thrainer
        ndata[0] = inst_uuid
1048 da4a52a3 Thomas Thrainer
        self._temporary_drbds[(nuuid, 0)] = inst_uuid
1049 a81c53c9 Iustin Pop
        continue
1050 a81c53c9 Iustin Pop
      keys = ndata.keys()
1051 a81c53c9 Iustin Pop
      keys.sort()
1052 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
1053 a81c53c9 Iustin Pop
      if ffree is None:
1054 a81c53c9 Iustin Pop
        # return the next minor
1055 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
1056 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
1057 a81c53c9 Iustin Pop
      else:
1058 a81c53c9 Iustin Pop
        minor = ffree
1059 4a89c54a Iustin Pop
      # double-check minor against current instances
1060 1c3231aa Thomas Thrainer
      assert minor not in d_map[nuuid], \
1061 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
1062 4a89c54a Iustin Pop
              " already allocated to instance %s" %
1063 1c3231aa Thomas Thrainer
              (minor, nuuid, d_map[nuuid][minor]))
1064 da4a52a3 Thomas Thrainer
      ndata[minor] = inst_uuid
1065 4a89c54a Iustin Pop
      # double-check minor against reservation
1066 1c3231aa Thomas Thrainer
      r_key = (nuuid, minor)
1067 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
1068 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
1069 4a89c54a Iustin Pop
              " reserved for instance %s" %
1070 1c3231aa Thomas Thrainer
              (minor, nuuid, self._temporary_drbds[r_key]))
1071 da4a52a3 Thomas Thrainer
      self._temporary_drbds[r_key] = inst_uuid
1072 4a89c54a Iustin Pop
      result.append(minor)
1073 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
1074 1c3231aa Thomas Thrainer
                  node_uuids, result)
1075 a81c53c9 Iustin Pop
    return result
1076 a81c53c9 Iustin Pop
1077 da4a52a3 Thomas Thrainer
  def _UnlockedReleaseDRBDMinors(self, inst_uuid):
1078 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
1079 a81c53c9 Iustin Pop

1080 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1081 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1082 da4a52a3 Thomas Thrainer
                      released
1083 a81c53c9 Iustin Pop

1084 a81c53c9 Iustin Pop
    """
1085 da4a52a3 Thomas Thrainer
    assert isinstance(inst_uuid, basestring), \
1086 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
1087 da4a52a3 Thomas Thrainer
    for key, uuid in self._temporary_drbds.items():
1088 da4a52a3 Thomas Thrainer
      if uuid == inst_uuid:
1089 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
1090 a81c53c9 Iustin Pop
1091 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
1092 da4a52a3 Thomas Thrainer
  def ReleaseDRBDMinors(self, inst_uuid):
1093 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
1094 61cf6b5e Iustin Pop

1095 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
1096 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
1097 61cf6b5e Iustin Pop
    functions.
1098 61cf6b5e Iustin Pop

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

1101 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1102 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1103 da4a52a3 Thomas Thrainer
                      released
1104 61cf6b5e Iustin Pop

1105 61cf6b5e Iustin Pop
    """
1106 da4a52a3 Thomas Thrainer
    self._UnlockedReleaseDRBDMinors(inst_uuid)
1107 61cf6b5e Iustin Pop
1108 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1109 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
1110 4a8b186a Michael Hanselmann
    """Get the configuration version.
1111 4a8b186a Michael Hanselmann

1112 4a8b186a Michael Hanselmann
    @return: Config version
1113 4a8b186a Michael Hanselmann

1114 4a8b186a Michael Hanselmann
    """
1115 4a8b186a Michael Hanselmann
    return self._config_data.version
1116 4a8b186a Michael Hanselmann
1117 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1118 4a8b186a Michael Hanselmann
  def GetClusterName(self):
1119 4a8b186a Michael Hanselmann
    """Get cluster name.
1120 4a8b186a Michael Hanselmann

1121 4a8b186a Michael Hanselmann
    @return: Cluster name
1122 4a8b186a Michael Hanselmann

1123 4a8b186a Michael Hanselmann
    """
1124 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
1125 4a8b186a Michael Hanselmann
1126 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1127 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
1128 1c3231aa Thomas Thrainer
    """Get the UUID of the master node for this cluster.
1129 4a8b186a Michael Hanselmann

1130 1c3231aa Thomas Thrainer
    @return: Master node UUID
1131 4a8b186a Michael Hanselmann

1132 4a8b186a Michael Hanselmann
    """
1133 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
1134 4a8b186a Michael Hanselmann
1135 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1136 1c3231aa Thomas Thrainer
  def GetMasterNodeName(self):
1137 1c3231aa Thomas Thrainer
    """Get the hostname of the master node for this cluster.
1138 1c3231aa Thomas Thrainer

1139 1c3231aa Thomas Thrainer
    @return: Master node hostname
1140 1c3231aa Thomas Thrainer

1141 1c3231aa Thomas Thrainer
    """
1142 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(self._config_data.cluster.master_node)
1143 1c3231aa Thomas Thrainer
1144 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1145 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
1146 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
1147 4a8b186a Michael Hanselmann

1148 4a8b186a Michael Hanselmann
    @return: Master IP
1149 4a8b186a Michael Hanselmann

1150 4a8b186a Michael Hanselmann
    """
1151 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
1152 4a8b186a Michael Hanselmann
1153 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1154 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
1155 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
1156 4a8b186a Michael Hanselmann

1157 4a8b186a Michael Hanselmann
    """
1158 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
1159 4a8b186a Michael Hanselmann
1160 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1161 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
1162 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
1163 5a8648eb Andrea Spadaccini

1164 5a8648eb Andrea Spadaccini
    """
1165 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
1166 5a8648eb Andrea Spadaccini
1167 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1168 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
1169 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
1170 33be7576 Andrea Spadaccini

1171 33be7576 Andrea Spadaccini
    """
1172 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
1173 33be7576 Andrea Spadaccini
1174 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1175 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
1176 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
1177 4a8b186a Michael Hanselmann

1178 4a8b186a Michael Hanselmann
    """
1179 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
1180 4a8b186a Michael Hanselmann
1181 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1182 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
1183 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
1184 4b97f902 Apollon Oikonomopoulos

1185 4b97f902 Apollon Oikonomopoulos
    """
1186 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1187 4b97f902 Apollon Oikonomopoulos
1188 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1189 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1190 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1191 4a8b186a Michael Hanselmann

1192 4a8b186a Michael Hanselmann
    """
1193 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
1194 4a8b186a Michael Hanselmann
1195 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1196 a8083063 Iustin Pop
  def GetHostKey(self):
1197 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1198 a8083063 Iustin Pop

1199 c41eea6e Iustin Pop
    @rtype: string
1200 c41eea6e Iustin Pop
    @return: the rsa hostkey
1201 a8083063 Iustin Pop

1202 a8083063 Iustin Pop
    """
1203 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1204 a8083063 Iustin Pop
1205 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1206 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1207 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1208 bf4af505 Apollon Oikonomopoulos

1209 bf4af505 Apollon Oikonomopoulos
    """
1210 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1211 bf4af505 Apollon Oikonomopoulos
1212 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1213 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1214 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1215 868a98ca Manuel Franceschini

1216 868a98ca Manuel Franceschini
    @return: primary ip family
1217 868a98ca Manuel Franceschini

1218 868a98ca Manuel Franceschini
    """
1219 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1220 868a98ca Manuel Franceschini
1221 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1222 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1223 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1224 c9f4b8e6 Andrea Spadaccini

1225 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1226 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1227 c9f4b8e6 Andrea Spadaccini

1228 c9f4b8e6 Andrea Spadaccini
    """
1229 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1230 5ae4945a Iustin Pop
    result = objects.MasterNetworkParameters(
1231 1c3231aa Thomas Thrainer
      uuid=cluster.master_node, ip=cluster.master_ip,
1232 5ae4945a Iustin Pop
      netmask=cluster.master_netmask, netdev=cluster.master_netdev,
1233 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1234 c9f4b8e6 Andrea Spadaccini
1235 f9d20654 Andrea Spadaccini
    return result
1236 f9d20654 Andrea Spadaccini
1237 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1238 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1239 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1240 e11a1b77 Adeodato Simo

1241 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1242 90e99856 Adeodato Simo
    according to their default values.
1243 90e99856 Adeodato Simo

1244 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1245 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1246 e11a1b77 Adeodato Simo
    @type ec_id: string
1247 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1248 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1249 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1250 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1251 e11a1b77 Adeodato Simo
                       configuration already
1252 e11a1b77 Adeodato Simo

1253 e11a1b77 Adeodato Simo
    """
1254 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1255 e11a1b77 Adeodato Simo
    self._WriteConfig()
1256 e11a1b77 Adeodato Simo
1257 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1258 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1259 e11a1b77 Adeodato Simo

1260 e11a1b77 Adeodato Simo
    """
1261 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
1262 e11a1b77 Adeodato Simo
1263 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
1264 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
1265 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
1266 e11a1b77 Adeodato Simo
    if check_uuid:
1267 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
1268 e11a1b77 Adeodato Simo
1269 18ffc0fe Stephen Shirley
    try:
1270 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
1271 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
1272 18ffc0fe Stephen Shirley
      pass
1273 18ffc0fe Stephen Shirley
    else:
1274 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
1275 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
1276 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
1277 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
1278 18ffc0fe Stephen Shirley
1279 e11a1b77 Adeodato Simo
    group.serial_no = 1
1280 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
1281 90e99856 Adeodato Simo
    group.UpgradeConfig()
1282 e11a1b77 Adeodato Simo
1283 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
1284 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1285 e11a1b77 Adeodato Simo
1286 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1287 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
1288 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
1289 e11a1b77 Adeodato Simo

1290 e11a1b77 Adeodato Simo
    @type group_uuid: string
1291 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1292 e11a1b77 Adeodato Simo

1293 e11a1b77 Adeodato Simo
    """
1294 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1295 e11a1b77 Adeodato Simo
1296 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1297 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1298 e11a1b77 Adeodato Simo
1299 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1300 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1301 0389c42a Stephen Shirley
1302 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1303 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1304 e11a1b77 Adeodato Simo
    self._WriteConfig()
1305 e11a1b77 Adeodato Simo
1306 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1307 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1308 eaa98a04 Guido Trotter

1309 eaa98a04 Guido Trotter
    @type target: string or None
1310 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1311 eaa98a04 Guido Trotter
    @rtype: string
1312 412b3531 Guido Trotter
    @return: nodegroup UUID
1313 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1314 eaa98a04 Guido Trotter

1315 eaa98a04 Guido Trotter
    """
1316 eaa98a04 Guido Trotter
    if target is None:
1317 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1318 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1319 2ed0e208 Iustin Pop
                                   " group must be specified explicitly.")
1320 eaa98a04 Guido Trotter
      else:
1321 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1322 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1323 eaa98a04 Guido Trotter
      return target
1324 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1325 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1326 eaa98a04 Guido Trotter
        return nodegroup.uuid
1327 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1328 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1329 eaa98a04 Guido Trotter
1330 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1331 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1332 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1333 e85d8982 Stephen Shirley

1334 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1335 e85d8982 Stephen Shirley

1336 e85d8982 Stephen Shirley
    @type target: string or None
1337 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1338 e85d8982 Stephen Shirley
    @rtype: string
1339 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1340 e85d8982 Stephen Shirley

1341 e85d8982 Stephen Shirley
    """
1342 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1343 e85d8982 Stephen Shirley
1344 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1345 648e4196 Guido Trotter
    """Lookup a node group.
1346 648e4196 Guido Trotter

1347 648e4196 Guido Trotter
    @type uuid: string
1348 648e4196 Guido Trotter
    @param uuid: group UUID
1349 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1350 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1351 648e4196 Guido Trotter

1352 648e4196 Guido Trotter
    """
1353 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1354 648e4196 Guido Trotter
      return None
1355 648e4196 Guido Trotter
1356 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1357 648e4196 Guido Trotter
1358 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1359 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1360 5768e6a6 René Nussbaumer
    """Lookup a node group.
1361 5768e6a6 René Nussbaumer

1362 5768e6a6 René Nussbaumer
    @type uuid: string
1363 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1364 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1365 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1366 5768e6a6 René Nussbaumer

1367 5768e6a6 René Nussbaumer
    """
1368 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1369 5768e6a6 René Nussbaumer
1370 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1371 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1372 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1373 622444e5 Iustin Pop

1374 622444e5 Iustin Pop
    """
1375 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1376 622444e5 Iustin Pop
1377 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1378 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1379 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1380 1ac6f2ad Guido Trotter

1381 1ac6f2ad Guido Trotter
    """
1382 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1383 1ac6f2ad Guido Trotter
1384 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1385 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1386 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1387 dac81741 Michael Hanselmann

1388 dac81741 Michael Hanselmann
    """
1389 1c3231aa Thomas Thrainer
    ngfn = lambda node_uuid: self._UnlockedGetNodeInfo(node_uuid).group
1390 1c3231aa Thomas Thrainer
    return frozenset(member_uuid
1391 1c3231aa Thomas Thrainer
                     for node_uuid in nodes
1392 1c3231aa Thomas Thrainer
                     for member_uuid in
1393 1c3231aa Thomas Thrainer
                       self._UnlockedGetNodeGroup(ngfn(node_uuid)).members)
1394 dac81741 Michael Hanselmann
1395 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1396 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1397 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1398 080fbeea Michael Hanselmann

1399 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1400 080fbeea Michael Hanselmann
    @rtype: list
1401 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1402 080fbeea Michael Hanselmann

1403 080fbeea Michael Hanselmann
    """
1404 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1405 080fbeea Michael Hanselmann
1406 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1407 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1408 a8083063 Iustin Pop
    """Add an instance to the config.
1409 a8083063 Iustin Pop

1410 a8083063 Iustin Pop
    This should be used after creating a new instance.
1411 a8083063 Iustin Pop

1412 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1413 c41eea6e Iustin Pop
    @param instance: the instance object
1414 c41eea6e Iustin Pop

1415 a8083063 Iustin Pop
    """
1416 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1417 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1418 a8083063 Iustin Pop
1419 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1420 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1421 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1422 923b1523 Iustin Pop
1423 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1424 e4640214 Guido Trotter
    for nic in instance.nics:
1425 e4640214 Guido Trotter
      if nic.mac in all_macs:
1426 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1427 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1428 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1429 430b923c Iustin Pop
1430 da4a52a3 Thomas Thrainer
    self._CheckUniqueUUID(instance, include_temporary=False)
1431 e4640214 Guido Trotter
1432 b989e85d Iustin Pop
    instance.serial_no = 1
1433 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1434 da4a52a3 Thomas Thrainer
    self._config_data.instances[instance.uuid] = instance
1435 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1436 da4a52a3 Thomas Thrainer
    self._UnlockedReleaseDRBDMinors(instance.uuid)
1437 e8e079f3 Dimitris Aragiorgis
    self._UnlockedCommitTemporaryIps(ec_id)
1438 a8083063 Iustin Pop
    self._WriteConfig()
1439 a8083063 Iustin Pop
1440 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1441 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1442 430b923c Iustin Pop

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

1446 430b923c Iustin Pop
    """
1447 430b923c Iustin Pop
    if not item.uuid:
1448 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1449 da4a52a3 Thomas Thrainer
    else:
1450 da4a52a3 Thomas Thrainer
      self._CheckUniqueUUID(item, include_temporary=True)
1451 da4a52a3 Thomas Thrainer
1452 da4a52a3 Thomas Thrainer
  def _CheckUniqueUUID(self, item, include_temporary):
1453 da4a52a3 Thomas Thrainer
    """Checks that the UUID of the given object is unique.
1454 da4a52a3 Thomas Thrainer

1455 da4a52a3 Thomas Thrainer
    @param item: the instance or node to be checked
1456 da4a52a3 Thomas Thrainer
    @param include_temporary: whether temporarily generated UUID's should be
1457 da4a52a3 Thomas Thrainer
              included in the check. If the UUID of the item to be checked is
1458 da4a52a3 Thomas Thrainer
              a temporarily generated one, this has to be C{False}.
1459 da4a52a3 Thomas Thrainer

1460 da4a52a3 Thomas Thrainer
    """
1461 da4a52a3 Thomas Thrainer
    if not item.uuid:
1462 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("'%s' must have an UUID" % (item.name,))
1463 da4a52a3 Thomas Thrainer
    if item.uuid in self._AllIDs(include_temporary=include_temporary):
1464 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1465 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1466 430b923c Iustin Pop
1467 da4a52a3 Thomas Thrainer
  def _SetInstanceStatus(self, inst_uuid, status, disks_active):
1468 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1469 a8083063 Iustin Pop

1470 a8083063 Iustin Pop
    """
1471 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1472 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1473 da4a52a3 Thomas Thrainer
                                      inst_uuid)
1474 da4a52a3 Thomas Thrainer
    instance = self._config_data.instances[inst_uuid]
1475 1d4a4b26 Thomas Thrainer
1476 1d4a4b26 Thomas Thrainer
    if status is None:
1477 1d4a4b26 Thomas Thrainer
      status = instance.admin_state
1478 1d4a4b26 Thomas Thrainer
    if disks_active is None:
1479 1d4a4b26 Thomas Thrainer
      disks_active = instance.disks_active
1480 1d4a4b26 Thomas Thrainer
1481 1d4a4b26 Thomas Thrainer
    assert status in constants.ADMINST_ALL, \
1482 1d4a4b26 Thomas Thrainer
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1483 1d4a4b26 Thomas Thrainer
1484 1d4a4b26 Thomas Thrainer
    if instance.admin_state != status or \
1485 1d4a4b26 Thomas Thrainer
       instance.disks_active != disks_active:
1486 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1487 1d4a4b26 Thomas Thrainer
      instance.disks_active = disks_active
1488 b989e85d Iustin Pop
      instance.serial_no += 1
1489 d693c864 Iustin Pop
      instance.mtime = time.time()
1490 455a3445 Iustin Pop
      self._WriteConfig()
1491 a8083063 Iustin Pop
1492 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1493 da4a52a3 Thomas Thrainer
  def MarkInstanceUp(self, inst_uuid):
1494 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1495 6a408fb2 Iustin Pop

1496 1d4a4b26 Thomas Thrainer
    This also sets the instance disks active flag.
1497 1d4a4b26 Thomas Thrainer

1498 6a408fb2 Iustin Pop
    """
1499 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_UP, True)
1500 57de31c0 Agata Murawska
1501 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1502 da4a52a3 Thomas Thrainer
  def MarkInstanceOffline(self, inst_uuid):
1503 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1504 57de31c0 Agata Murawska

1505 1d4a4b26 Thomas Thrainer
    This also clears the instance disks active flag.
1506 1d4a4b26 Thomas Thrainer

1507 57de31c0 Agata Murawska
    """
1508 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_OFFLINE, False)
1509 6a408fb2 Iustin Pop
1510 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1511 da4a52a3 Thomas Thrainer
  def RemoveInstance(self, inst_uuid):
1512 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1513 a8083063 Iustin Pop

1514 a8083063 Iustin Pop
    """
1515 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1516 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1517 f396ad8c Vangelis Koukis
1518 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1519 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1520 da4a52a3 Thomas Thrainer
    inst = self._config_data.instances[inst_uuid]
1521 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1522 f396ad8c Vangelis Koukis
    if network_port is not None:
1523 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1524 f396ad8c Vangelis Koukis
1525 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1526 ced51149 Dimitris Aragiorgis
1527 ced51149 Dimitris Aragiorgis
    for nic in instance.nics:
1528 9394f4d1 Dimitris Aragiorgis
      if nic.network and nic.ip:
1529 1b68f268 Helga Velroyen
        # Return all IP addresses to the respective address pools
1530 9394f4d1 Dimitris Aragiorgis
        self._UnlockedCommitIp(constants.RELEASE_ACTION, nic.network, nic.ip)
1531 ced51149 Dimitris Aragiorgis
1532 da4a52a3 Thomas Thrainer
    del self._config_data.instances[inst_uuid]
1533 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1534 a8083063 Iustin Pop
    self._WriteConfig()
1535 a8083063 Iustin Pop
1536 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1537 da4a52a3 Thomas Thrainer
  def RenameInstance(self, inst_uuid, new_name):
1538 fc95f88f Iustin Pop
    """Rename an instance.
1539 fc95f88f Iustin Pop

1540 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1541 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1542 fc95f88f Iustin Pop
    rename.
1543 fc95f88f Iustin Pop

1544 fc95f88f Iustin Pop
    """
1545 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1546 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1547 ea642319 Michael Hanselmann
1548 da4a52a3 Thomas Thrainer
    inst = self._config_data.instances[inst_uuid]
1549 fc95f88f Iustin Pop
    inst.name = new_name
1550 b23c4333 Manuel Franceschini
1551 ea642319 Michael Hanselmann
    for (idx, disk) in enumerate(inst.disks):
1552 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1553 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1554 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1555 ea642319 Michael Hanselmann
        disk.logical_id = (disk.logical_id[0],
1556 ea642319 Michael Hanselmann
                           utils.PathJoin(file_storage_dir, inst.name,
1557 ea642319 Michael Hanselmann
                                          "disk%s" % idx))
1558 ea642319 Michael Hanselmann
        disk.physical_id = disk.logical_id
1559 ea642319 Michael Hanselmann
1560 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1561 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1562 1fc34c48 Michael Hanselmann
1563 fc95f88f Iustin Pop
    self._WriteConfig()
1564 fc95f88f Iustin Pop
1565 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1566 da4a52a3 Thomas Thrainer
  def MarkInstanceDown(self, inst_uuid):
1567 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1568 a8083063 Iustin Pop

1569 1d4a4b26 Thomas Thrainer
    This does not touch the instance disks active flag, as shut down instances
1570 1d4a4b26 Thomas Thrainer
    can still have active disks.
1571 1d4a4b26 Thomas Thrainer

1572 1d4a4b26 Thomas Thrainer
    """
1573 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None)
1574 1d4a4b26 Thomas Thrainer
1575 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1576 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksActive(self, inst_uuid):
1577 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks active.
1578 1d4a4b26 Thomas Thrainer

1579 1d4a4b26 Thomas Thrainer
    """
1580 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, True)
1581 1d4a4b26 Thomas Thrainer
1582 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1583 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksInactive(self, inst_uuid):
1584 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks inactive.
1585 1d4a4b26 Thomas Thrainer

1586 a8083063 Iustin Pop
    """
1587 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, False)
1588 a8083063 Iustin Pop
1589 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1590 94bbfece Iustin Pop
    """Get the list of instances.
1591 94bbfece Iustin Pop

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

1594 94bbfece Iustin Pop
    """
1595 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1596 94bbfece Iustin Pop
1597 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1598 a8083063 Iustin Pop
  def GetInstanceList(self):
1599 a8083063 Iustin Pop
    """Get the list of instances.
1600 a8083063 Iustin Pop

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

1603 a8083063 Iustin Pop
    """
1604 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1605 a8083063 Iustin Pop
1606 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1607 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1608 a8083063 Iustin Pop

1609 a8083063 Iustin Pop
    """
1610 da4a52a3 Thomas Thrainer
    # Locking is done in L{ConfigWriter.GetAllInstancesInfo}
1611 da4a52a3 Thomas Thrainer
    all_insts = self.GetAllInstancesInfo().values()
1612 da4a52a3 Thomas Thrainer
    expanded_name = _MatchNameComponentIgnoreCase(
1613 da4a52a3 Thomas Thrainer
                      short_name, [inst.name for inst in all_insts])
1614 da4a52a3 Thomas Thrainer
1615 da4a52a3 Thomas Thrainer
    if expanded_name is not None:
1616 da4a52a3 Thomas Thrainer
      # there has to be exactly one instance with that name
1617 da4a52a3 Thomas Thrainer
      inst = (filter(lambda n: n.name == expanded_name, all_insts)[0])
1618 da4a52a3 Thomas Thrainer
      return (inst.uuid, inst.name)
1619 da4a52a3 Thomas Thrainer
    else:
1620 da4a52a3 Thomas Thrainer
      return None
1621 a8083063 Iustin Pop
1622 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceInfo(self, inst_uuid):
1623 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1624 94bbfece Iustin Pop

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

1627 94bbfece Iustin Pop
    """
1628 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1629 94bbfece Iustin Pop
      return None
1630 94bbfece Iustin Pop
1631 da4a52a3 Thomas Thrainer
    return self._config_data.instances[inst_uuid]
1632 94bbfece Iustin Pop
1633 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1634 da4a52a3 Thomas Thrainer
  def GetInstanceInfo(self, inst_uuid):
1635 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1636 a8083063 Iustin Pop

1637 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1638 a8083063 Iustin Pop
    an instance are taken from the live systems.
1639 a8083063 Iustin Pop

1640 da4a52a3 Thomas Thrainer
    @param inst_uuid: UUID of the instance
1641 a8083063 Iustin Pop

1642 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1643 c41eea6e Iustin Pop
    @return: the instance object
1644 a8083063 Iustin Pop

1645 a8083063 Iustin Pop
    """
1646 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfo(inst_uuid)
1647 a8083063 Iustin Pop
1648 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1649 da4a52a3 Thomas Thrainer
  def GetInstanceNodeGroups(self, inst_uuid, primary_only=False):
1650 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1651 2674690b Michael Hanselmann

1652 2674690b Michael Hanselmann
    @rtype: frozenset
1653 2674690b Michael Hanselmann

1654 2674690b Michael Hanselmann
    """
1655 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1656 2674690b Michael Hanselmann
    if not instance:
1657 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1658 2674690b Michael Hanselmann
1659 2674690b Michael Hanselmann
    if primary_only:
1660 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1661 2674690b Michael Hanselmann
    else:
1662 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1663 2674690b Michael Hanselmann
1664 1c3231aa Thomas Thrainer
    return frozenset(self._UnlockedGetNodeInfo(node_uuid).group
1665 1c3231aa Thomas Thrainer
                     for node_uuid in nodes)
1666 2674690b Michael Hanselmann
1667 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1668 da4a52a3 Thomas Thrainer
  def GetInstanceNetworks(self, inst_uuid):
1669 922610c9 Dimitris Aragiorgis
    """Returns set of network UUIDs for instance's nics.
1670 922610c9 Dimitris Aragiorgis

1671 922610c9 Dimitris Aragiorgis
    @rtype: frozenset
1672 922610c9 Dimitris Aragiorgis

1673 922610c9 Dimitris Aragiorgis
    """
1674 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1675 922610c9 Dimitris Aragiorgis
    if not instance:
1676 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1677 922610c9 Dimitris Aragiorgis
1678 922610c9 Dimitris Aragiorgis
    networks = set()
1679 922610c9 Dimitris Aragiorgis
    for nic in instance.nics:
1680 922610c9 Dimitris Aragiorgis
      if nic.network:
1681 922610c9 Dimitris Aragiorgis
        networks.add(nic.network)
1682 922610c9 Dimitris Aragiorgis
1683 922610c9 Dimitris Aragiorgis
    return frozenset(networks)
1684 922610c9 Dimitris Aragiorgis
1685 922610c9 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
1686 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfo(self, inst_uuids):
1687 da4a52a3 Thomas Thrainer
    """Get the configuration of multiple instances.
1688 da4a52a3 Thomas Thrainer

1689 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs
1690 da4a52a3 Thomas Thrainer
    @rtype: list
1691 da4a52a3 Thomas Thrainer
    @return: list of tuples (instance UUID, instance_info), where
1692 da4a52a3 Thomas Thrainer
        instance_info is what would GetInstanceInfo return for the
1693 da4a52a3 Thomas Thrainer
        node, while keeping the original order
1694 da4a52a3 Thomas Thrainer

1695 da4a52a3 Thomas Thrainer
    """
1696 da4a52a3 Thomas Thrainer
    return [(uuid, self._UnlockedGetInstanceInfo(uuid)) for uuid in inst_uuids]
1697 da4a52a3 Thomas Thrainer
1698 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1699 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfoByName(self, inst_names):
1700 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1701 71333cb9 Iustin Pop

1702 da4a52a3 Thomas Thrainer
    @param inst_names: list of instance names
1703 71333cb9 Iustin Pop
    @rtype: list
1704 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1705 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1706 71333cb9 Iustin Pop
        node, while keeping the original order
1707 71333cb9 Iustin Pop

1708 71333cb9 Iustin Pop
    """
1709 da4a52a3 Thomas Thrainer
    result = []
1710 da4a52a3 Thomas Thrainer
    for name in inst_names:
1711 da4a52a3 Thomas Thrainer
      instance = self._UnlockedGetInstanceInfoByName(name)
1712 da4a52a3 Thomas Thrainer
      result.append((instance.uuid, instance))
1713 da4a52a3 Thomas Thrainer
    return result
1714 71333cb9 Iustin Pop
1715 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1716 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1717 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1718 0b2de758 Iustin Pop

1719 0b2de758 Iustin Pop
    @rtype: dict
1720 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1721 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1722 0b2de758 Iustin Pop

1723 0b2de758 Iustin Pop
    """
1724 da4a52a3 Thomas Thrainer
    return self._UnlockedGetAllInstancesInfo()
1725 da4a52a3 Thomas Thrainer
1726 da4a52a3 Thomas Thrainer
  def _UnlockedGetAllInstancesInfo(self):
1727 da4a52a3 Thomas Thrainer
    my_dict = dict([(inst_uuid, self._UnlockedGetInstanceInfo(inst_uuid))
1728 da4a52a3 Thomas Thrainer
                    for inst_uuid in self._UnlockedGetInstanceList()])
1729 0b2de758 Iustin Pop
    return my_dict
1730 0b2de758 Iustin Pop
1731 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1732 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1733 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1734 cc19798f Michael Hanselmann

1735 cc19798f Michael Hanselmann
    @type filter_fn: callable
1736 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1737 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1738 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1739 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1740 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1741 cc19798f Michael Hanselmann

1742 cc19798f Michael Hanselmann
    """
1743 da4a52a3 Thomas Thrainer
    return dict((uuid, inst)
1744 da4a52a3 Thomas Thrainer
                for (uuid, inst) in self._config_data.instances.items()
1745 cc19798f Michael Hanselmann
                if filter_fn(inst))
1746 cc19798f Michael Hanselmann
1747 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1748 da4a52a3 Thomas Thrainer
  def GetInstanceInfoByName(self, inst_name):
1749 da4a52a3 Thomas Thrainer
    """Get the L{objects.Instance} object for a named instance.
1750 da4a52a3 Thomas Thrainer

1751 da4a52a3 Thomas Thrainer
    @param inst_name: name of the instance to get information for
1752 da4a52a3 Thomas Thrainer
    @type inst_name: string
1753 da4a52a3 Thomas Thrainer
    @return: the corresponding L{objects.Instance} instance or None if no
1754 da4a52a3 Thomas Thrainer
          information is available
1755 da4a52a3 Thomas Thrainer

1756 da4a52a3 Thomas Thrainer
    """
1757 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfoByName(inst_name)
1758 da4a52a3 Thomas Thrainer
1759 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceInfoByName(self, inst_name):
1760 da4a52a3 Thomas Thrainer
    for inst in self._UnlockedGetAllInstancesInfo().values():
1761 da4a52a3 Thomas Thrainer
      if inst.name == inst_name:
1762 da4a52a3 Thomas Thrainer
        return inst
1763 da4a52a3 Thomas Thrainer
    return None
1764 da4a52a3 Thomas Thrainer
1765 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceName(self, inst_uuid):
1766 da4a52a3 Thomas Thrainer
    inst_info = self._UnlockedGetInstanceInfo(inst_uuid)
1767 da4a52a3 Thomas Thrainer
    if inst_info is None:
1768 da4a52a3 Thomas Thrainer
      raise errors.OpExecError("Unknown instance: %s" % inst_uuid)
1769 da4a52a3 Thomas Thrainer
    return inst_info.name
1770 da4a52a3 Thomas Thrainer
1771 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1772 da4a52a3 Thomas Thrainer
  def GetInstanceName(self, inst_uuid):
1773 da4a52a3 Thomas Thrainer
    """Gets the instance name for the passed instance.
1774 da4a52a3 Thomas Thrainer

1775 da4a52a3 Thomas Thrainer
    @param inst_uuid: instance UUID to get name for
1776 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1777 da4a52a3 Thomas Thrainer
    @rtype: string
1778 da4a52a3 Thomas Thrainer
    @return: instance name
1779 da4a52a3 Thomas Thrainer

1780 da4a52a3 Thomas Thrainer
    """
1781 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceName(inst_uuid)
1782 da4a52a3 Thomas Thrainer
1783 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1784 da4a52a3 Thomas Thrainer
  def GetInstanceNames(self, inst_uuids):
1785 da4a52a3 Thomas Thrainer
    """Gets the instance names for the passed list of nodes.
1786 da4a52a3 Thomas Thrainer

1787 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs to get names for
1788 da4a52a3 Thomas Thrainer
    @type inst_uuids: list of strings
1789 da4a52a3 Thomas Thrainer
    @rtype: list of strings
1790 da4a52a3 Thomas Thrainer
    @return: list of instance names
1791 da4a52a3 Thomas Thrainer

1792 da4a52a3 Thomas Thrainer
    """
1793 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceNames(inst_uuids)
1794 da4a52a3 Thomas Thrainer
1795 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceNames(self, inst_uuids):
1796 da4a52a3 Thomas Thrainer
    return [self._UnlockedGetInstanceName(uuid) for uuid in inst_uuids]
1797 da4a52a3 Thomas Thrainer
1798 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1799 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1800 a8083063 Iustin Pop
    """Add a node to the configuration.
1801 a8083063 Iustin Pop

1802 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1803 c41eea6e Iustin Pop
    @param node: a Node instance
1804 a8083063 Iustin Pop

1805 a8083063 Iustin Pop
    """
1806 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1807 d8470559 Michael Hanselmann
1808 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1809 430b923c Iustin Pop
1810 b989e85d Iustin Pop
    node.serial_no = 1
1811 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1812 1c3231aa Thomas Thrainer
    self._UnlockedAddNodeToGroup(node.uuid, node.group)
1813 1c3231aa Thomas Thrainer
    self._config_data.nodes[node.uuid] = node
1814 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1815 a8083063 Iustin Pop
    self._WriteConfig()
1816 a8083063 Iustin Pop
1817 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1818 1c3231aa Thomas Thrainer
  def RemoveNode(self, node_uuid):
1819 a8083063 Iustin Pop
    """Remove a node from the configuration.
1820 a8083063 Iustin Pop

1821 a8083063 Iustin Pop
    """
1822 1c3231aa Thomas Thrainer
    logging.info("Removing node %s from configuration", node_uuid)
1823 d8470559 Michael Hanselmann
1824 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
1825 1c3231aa Thomas Thrainer
      raise errors.ConfigurationError("Unknown node '%s'" % node_uuid)
1826 a8083063 Iustin Pop
1827 1c3231aa Thomas Thrainer
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_uuid])
1828 1c3231aa Thomas Thrainer
    del self._config_data.nodes[node_uuid]
1829 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1830 a8083063 Iustin Pop
    self._WriteConfig()
1831 a8083063 Iustin Pop
1832 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1833 1c3231aa Thomas Thrainer
    """Attempt to expand an incomplete node name into a node UUID.
1834 a8083063 Iustin Pop

1835 a8083063 Iustin Pop
    """
1836 1c3231aa Thomas Thrainer
    # Locking is done in L{ConfigWriter.GetAllNodesInfo}
1837 1c3231aa Thomas Thrainer
    all_nodes = self.GetAllNodesInfo().values()
1838 1c3231aa Thomas Thrainer
    expanded_name = _MatchNameComponentIgnoreCase(
1839 1c3231aa Thomas Thrainer
                      short_name, [node.name for node in all_nodes])
1840 a8083063 Iustin Pop
1841 1c3231aa Thomas Thrainer
    if expanded_name is not None:
1842 da4a52a3 Thomas Thrainer
      # there has to be exactly one node with that name
1843 1c3231aa Thomas Thrainer
      node = (filter(lambda n: n.name == expanded_name, all_nodes)[0])
1844 1c3231aa Thomas Thrainer
      return (node.uuid, node.name)
1845 1c3231aa Thomas Thrainer
    else:
1846 1c3231aa Thomas Thrainer
      return None
1847 1c3231aa Thomas Thrainer
1848 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfo(self, node_uuid):
1849 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1850 a8083063 Iustin Pop

1851 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1852 c41eea6e Iustin Pop
    held.
1853 f78ede4e Guido Trotter

1854 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1855 a8083063 Iustin Pop

1856 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1857 c41eea6e Iustin Pop
    @return: the node object
1858 a8083063 Iustin Pop

1859 a8083063 Iustin Pop
    """
1860 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
1861 a8083063 Iustin Pop
      return None
1862 a8083063 Iustin Pop
1863 1c3231aa Thomas Thrainer
    return self._config_data.nodes[node_uuid]
1864 a8083063 Iustin Pop
1865 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1866 1c3231aa Thomas Thrainer
  def GetNodeInfo(self, node_uuid):
1867 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1868 f78ede4e Guido Trotter

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

1871 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1872 c41eea6e Iustin Pop

1873 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1874 c41eea6e Iustin Pop
    @return: the node object
1875 f78ede4e Guido Trotter

1876 f78ede4e Guido Trotter
    """
1877 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfo(node_uuid)
1878 f78ede4e Guido Trotter
1879 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1880 1c3231aa Thomas Thrainer
  def GetNodeInstances(self, node_uuid):
1881 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1882 8bf9e9a5 Iustin Pop

1883 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1884 8bf9e9a5 Iustin Pop

1885 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1886 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1887 8bf9e9a5 Iustin Pop

1888 8bf9e9a5 Iustin Pop
    """
1889 8bf9e9a5 Iustin Pop
    pri = []
1890 8bf9e9a5 Iustin Pop
    sec = []
1891 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1892 1c3231aa Thomas Thrainer
      if inst.primary_node == node_uuid:
1893 da4a52a3 Thomas Thrainer
        pri.append(inst.uuid)
1894 1c3231aa Thomas Thrainer
      if node_uuid in inst.secondary_nodes:
1895 da4a52a3 Thomas Thrainer
        sec.append(inst.uuid)
1896 8bf9e9a5 Iustin Pop
    return (pri, sec)
1897 8bf9e9a5 Iustin Pop
1898 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1899 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1900 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1901 c71b049c Michael Hanselmann

1902 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1903 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1904 c71b049c Michael Hanselmann
    @rtype: frozenset
1905 da4a52a3 Thomas Thrainer
    @return: List of instance UUIDs in node group
1906 c71b049c Michael Hanselmann

1907 c71b049c Michael Hanselmann
    """
1908 c71b049c Michael Hanselmann
    if primary_only:
1909 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1910 c71b049c Michael Hanselmann
    else:
1911 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1912 c71b049c Michael Hanselmann
1913 da4a52a3 Thomas Thrainer
    return frozenset(inst.uuid
1914 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1915 1c3231aa Thomas Thrainer
                     for node_uuid in nodes_fn(inst)
1916 1c3231aa Thomas Thrainer
                     if self._UnlockedGetNodeInfo(node_uuid).group == uuid)
1917 c71b049c Michael Hanselmann
1918 def6577f Helga Velroyen
  def _UnlockedGetHvparamsString(self, hvname):
1919 def6577f Helga Velroyen
    """Return the string representation of the list of hyervisor parameters of
1920 def6577f Helga Velroyen
    the given hypervisor.
1921 def6577f Helga Velroyen

1922 def6577f Helga Velroyen
    @see: C{GetHvparams}
1923 def6577f Helga Velroyen

1924 def6577f Helga Velroyen
    """
1925 def6577f Helga Velroyen
    result = ""
1926 def6577f Helga Velroyen
    hvparams = self._config_data.cluster.hvparams[hvname]
1927 def6577f Helga Velroyen
    for key in hvparams:
1928 def6577f Helga Velroyen
      result += "%s=%s\n" % (key, hvparams[key])
1929 def6577f Helga Velroyen
    return result
1930 def6577f Helga Velroyen
1931 def6577f Helga Velroyen
  @locking.ssynchronized(_config_lock, shared=1)
1932 def6577f Helga Velroyen
  def GetHvparamsString(self, hvname):
1933 def6577f Helga Velroyen
    """Return the hypervisor parameters of the given hypervisor.
1934 def6577f Helga Velroyen

1935 def6577f Helga Velroyen
    @type hvname: string
1936 def6577f Helga Velroyen
    @param hvname: name of a hypervisor
1937 def6577f Helga Velroyen
    @rtype: string
1938 def6577f Helga Velroyen
    @return: string containing key-value-pairs, one pair on each line;
1939 def6577f Helga Velroyen
      format: KEY=VALUE
1940 def6577f Helga Velroyen

1941 def6577f Helga Velroyen
    """
1942 def6577f Helga Velroyen
    return self._UnlockedGetHvparamsString(hvname)
1943 def6577f Helga Velroyen
1944 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1945 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1946 a8083063 Iustin Pop

1947 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1948 c41eea6e Iustin Pop
    held.
1949 c41eea6e Iustin Pop

1950 c41eea6e Iustin Pop
    @rtype: list
1951 f78ede4e Guido Trotter

1952 a8083063 Iustin Pop
    """
1953 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1954 a8083063 Iustin Pop
1955 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1956 f78ede4e Guido Trotter
  def GetNodeList(self):
1957 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1958 f78ede4e Guido Trotter

1959 f78ede4e Guido Trotter
    """
1960 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1961 f78ede4e Guido Trotter
1962 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1963 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1964 94a02bb5 Iustin Pop

1965 94a02bb5 Iustin Pop
    """
1966 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1967 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1968 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.offline]
1969 94a02bb5 Iustin Pop
1970 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1971 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1972 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1973 6819dc49 Iustin Pop

1974 6819dc49 Iustin Pop
    """
1975 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1976 6819dc49 Iustin Pop
1977 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1978 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1979 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1980 075b62ca Iustin Pop

1981 075b62ca Iustin Pop
    """
1982 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1983 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1984 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if node.vm_capable]
1985 075b62ca Iustin Pop
1986 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1987 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1988 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1989 8bf9e9a5 Iustin Pop

1990 8bf9e9a5 Iustin Pop
    """
1991 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1992 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1993 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.vm_capable]
1994 8bf9e9a5 Iustin Pop
1995 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1996 1c3231aa Thomas Thrainer
  def GetMultiNodeInfo(self, node_uuids):
1997 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1998 f5eaa3c1 Iustin Pop

1999 1c3231aa Thomas Thrainer
    @param node_uuids: list of node UUIDs
2000 f5eaa3c1 Iustin Pop
    @rtype: list
2001 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
2002 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
2003 f5eaa3c1 Iustin Pop
        order
2004 f5eaa3c1 Iustin Pop

2005 f5eaa3c1 Iustin Pop
    """
2006 1c3231aa Thomas Thrainer
    return [(uuid, self._UnlockedGetNodeInfo(uuid)) for uuid in node_uuids]
2007 1c3231aa Thomas Thrainer
2008 1c3231aa Thomas Thrainer
  def _UnlockedGetAllNodesInfo(self):
2009 1c3231aa Thomas Thrainer
    """Gets configuration of all nodes.
2010 1c3231aa Thomas Thrainer

2011 1c3231aa Thomas Thrainer
    @note: See L{GetAllNodesInfo}
2012 1c3231aa Thomas Thrainer

2013 1c3231aa Thomas Thrainer
    """
2014 1c3231aa Thomas Thrainer
    return dict([(node_uuid, self._UnlockedGetNodeInfo(node_uuid))
2015 1c3231aa Thomas Thrainer
                 for node_uuid in self._UnlockedGetNodeList()])
2016 f5eaa3c1 Iustin Pop
2017 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2018 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
2019 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
2020 d65e5776 Iustin Pop

2021 d65e5776 Iustin Pop
    @rtype: dict
2022 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
2023 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
2024 d65e5776 Iustin Pop

2025 d65e5776 Iustin Pop
    """
2026 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
2027 ee14d800 Michael Hanselmann
2028 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfoByName(self, node_name):
2029 1c3231aa Thomas Thrainer
    for node in self._UnlockedGetAllNodesInfo().values():
2030 1c3231aa Thomas Thrainer
      if node.name == node_name:
2031 1c3231aa Thomas Thrainer
        return node
2032 1c3231aa Thomas Thrainer
    return None
2033 ee14d800 Michael Hanselmann
2034 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2035 1c3231aa Thomas Thrainer
  def GetNodeInfoByName(self, node_name):
2036 1c3231aa Thomas Thrainer
    """Get the L{objects.Node} object for a named node.
2037 1c3231aa Thomas Thrainer

2038 1c3231aa Thomas Thrainer
    @param node_name: name of the node to get information for
2039 1c3231aa Thomas Thrainer
    @type node_name: string
2040 1c3231aa Thomas Thrainer
    @return: the corresponding L{objects.Node} instance or None if no
2041 1c3231aa Thomas Thrainer
          information is available
2042 1c3231aa Thomas Thrainer

2043 1c3231aa Thomas Thrainer
    """
2044 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfoByName(node_name)
2045 1c3231aa Thomas Thrainer
2046 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeName(self, node_spec):
2047 1c3231aa Thomas Thrainer
    if isinstance(node_spec, objects.Node):
2048 1c3231aa Thomas Thrainer
      return node_spec.name
2049 1c3231aa Thomas Thrainer
    elif isinstance(node_spec, basestring):
2050 1c3231aa Thomas Thrainer
      node_info = self._UnlockedGetNodeInfo(node_spec)
2051 1c3231aa Thomas Thrainer
      if node_info is None:
2052 1c3231aa Thomas Thrainer
        raise errors.OpExecError("Unknown node: %s" % node_spec)
2053 1c3231aa Thomas Thrainer
      return node_info.name
2054 1c3231aa Thomas Thrainer
    else:
2055 1c3231aa Thomas Thrainer
      raise errors.ProgrammerError("Can't handle node spec '%s'" % node_spec)
2056 1c3231aa Thomas Thrainer
2057 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2058 1c3231aa Thomas Thrainer
  def GetNodeName(self, node_spec):
2059 1c3231aa Thomas Thrainer
    """Gets the node name for the passed node.
2060 1c3231aa Thomas Thrainer

2061 1c3231aa Thomas Thrainer
    @param node_spec: node to get names for
2062 1c3231aa Thomas Thrainer
    @type node_spec: either node UUID or a L{objects.Node} object
2063 1c3231aa Thomas Thrainer
    @rtype: string
2064 1c3231aa Thomas Thrainer
    @return: node name
2065 1c3231aa Thomas Thrainer

2066 1c3231aa Thomas Thrainer
    """
2067 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(node_spec)
2068 1c3231aa Thomas Thrainer
2069 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeNames(self, node_specs):
2070 1c3231aa Thomas Thrainer
    return [self._UnlockedGetNodeName(node_spec) for node_spec in node_specs]
2071 1c3231aa Thomas Thrainer
2072 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2073 1c3231aa Thomas Thrainer
  def GetNodeNames(self, node_specs):
2074 1c3231aa Thomas Thrainer
    """Gets the node names for the passed list of nodes.
2075 1c3231aa Thomas Thrainer

2076 1c3231aa Thomas Thrainer
    @param node_specs: list of nodes to get names for
2077 1c3231aa Thomas Thrainer
    @type node_specs: list of either node UUIDs or L{objects.Node} objects
2078 1c3231aa Thomas Thrainer
    @rtype: list of strings
2079 1c3231aa Thomas Thrainer
    @return: list of node names
2080 ee14d800 Michael Hanselmann

2081 ee14d800 Michael Hanselmann
    """
2082 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeNames(node_specs)
2083 d65e5776 Iustin Pop
2084 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
2085 1c3231aa Thomas Thrainer
  def GetNodeGroupsFromNodes(self, node_uuids):
2086 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
2087 9d5b1371 Michael Hanselmann

2088 1c3231aa Thomas Thrainer
    @type node_uuids: list of string
2089 1c3231aa Thomas Thrainer
    @param node_uuids: List of node UUIDs
2090 9d5b1371 Michael Hanselmann
    @rtype: frozenset
2091 9d5b1371 Michael Hanselmann

2092 9d5b1371 Michael Hanselmann
    """
2093 1c3231aa Thomas Thrainer
    return frozenset(self._UnlockedGetNodeInfo(uuid).group
2094 1c3231aa Thomas Thrainer
                     for uuid in node_uuids)
2095 9d5b1371 Michael Hanselmann
2096 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
2097 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
2098 ec0292f1 Iustin Pop

2099 23f06b2b Iustin Pop
    @type exceptions: list
2100 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
2101 ec0292f1 Iustin Pop
    @rtype: tuple
2102 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
2103 ec0292f1 Iustin Pop

2104 ec0292f1 Iustin Pop
    """
2105 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
2106 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
2107 1c3231aa Thomas Thrainer
      if exceptions and node.uuid in exceptions:
2108 23f06b2b Iustin Pop
        continue
2109 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
2110 ec0292f1 Iustin Pop
        mc_max += 1
2111 ec0292f1 Iustin Pop
      if node.master_candidate:
2112 ec0292f1 Iustin Pop
        mc_now += 1
2113 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
2114 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
2115 ec0292f1 Iustin Pop
2116 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2117 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
2118 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
2119 ec0292f1 Iustin Pop

2120 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
2121 ec0292f1 Iustin Pop

2122 23f06b2b Iustin Pop
    @type exceptions: list
2123 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
2124 ec0292f1 Iustin Pop
    @rtype: tuple
2125 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
2126 ec0292f1 Iustin Pop

2127 ec0292f1 Iustin Pop
    """
2128 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
2129 ec0292f1 Iustin Pop
2130 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
2131 1c3231aa Thomas Thrainer
  def MaintainCandidatePool(self, exception_node_uuids):
2132 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
2133 ec0292f1 Iustin Pop

2134 1c3231aa Thomas Thrainer
    @type exception_node_uuids: list
2135 1c3231aa Thomas Thrainer
    @param exception_node_uuids: if passed, list of nodes that should be ignored
2136 ec0292f1 Iustin Pop
    @rtype: list
2137 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
2138 ec0292f1 Iustin Pop

2139 ec0292f1 Iustin Pop
    """
2140 1c3231aa Thomas Thrainer
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(
2141 1c3231aa Thomas Thrainer
                          exception_node_uuids)
2142 ec0292f1 Iustin Pop
    mod_list = []
2143 ec0292f1 Iustin Pop
    if mc_now < mc_max:
2144 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
2145 ec0292f1 Iustin Pop
      random.shuffle(node_list)
2146 1c3231aa Thomas Thrainer
      for uuid in node_list:
2147 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
2148 ec0292f1 Iustin Pop
          break
2149 1c3231aa Thomas Thrainer
        node = self._config_data.nodes[uuid]
2150 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
2151 1c3231aa Thomas Thrainer
            node.uuid in exception_node_uuids or not node.master_capable):
2152 ec0292f1 Iustin Pop
          continue
2153 ee513a66 Iustin Pop
        mod_list.append(node)
2154 ec0292f1 Iustin Pop
        node.master_candidate = True
2155 ec0292f1 Iustin Pop
        node.serial_no += 1
2156 ec0292f1 Iustin Pop
        mc_now += 1
2157 ec0292f1 Iustin Pop
      if mc_now != mc_max:
2158 ec0292f1 Iustin Pop
        # this should not happen
2159 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
2160 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
2161 ec0292f1 Iustin Pop
      if mod_list:
2162 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
2163 ec0292f1 Iustin Pop
        self._WriteConfig()
2164 ec0292f1 Iustin Pop
2165 ec0292f1 Iustin Pop
    return mod_list
2166 ec0292f1 Iustin Pop
2167 1c3231aa Thomas Thrainer
  def _UnlockedAddNodeToGroup(self, node_uuid, nodegroup_uuid):
2168 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
2169 190e3cb6 Guido Trotter

2170 190e3cb6 Guido Trotter
    """
2171 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
2172 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
2173 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
2174 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
2175 190e3cb6 Guido Trotter
      # is not found anymore.
2176 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
2177 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodegroups[nodegroup_uuid].members:
2178 1c3231aa Thomas Thrainer
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_uuid)
2179 190e3cb6 Guido Trotter
2180 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
2181 190e3cb6 Guido Trotter
    """Remove a given node from its group.
2182 190e3cb6 Guido Trotter

2183 190e3cb6 Guido Trotter
    """
2184 f936c153 Iustin Pop
    nodegroup = node.group
2185 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
2186 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
2187 1c3231aa Thomas Thrainer
                      " (while being removed from it)", node.uuid, nodegroup)
2188 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
2189 1c3231aa Thomas Thrainer
    if node.uuid not in nodegroup_obj.members:
2190 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
2191 1c3231aa Thomas Thrainer
                      " (while being removed from it)", node.uuid, nodegroup)
2192 190e3cb6 Guido Trotter
    else:
2193 1c3231aa Thomas Thrainer
      nodegroup_obj.members.remove(node.uuid)
2194 190e3cb6 Guido Trotter
2195 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
2196 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
2197 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
2198 54c31fd3 Michael Hanselmann

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

2202 54c31fd3 Michael Hanselmann
    """
2203 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
2204 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
2205 54c31fd3 Michael Hanselmann
2206 54c31fd3 Michael Hanselmann
    resmod = []
2207 54c31fd3 Michael Hanselmann
2208 1c3231aa Thomas Thrainer
    # Try to resolve UUIDs first
2209 1c3231aa Thomas Thrainer
    for (node_uuid, new_group_uuid) in mods:
2210 54c31fd3 Michael Hanselmann
      try:
2211 1c3231aa Thomas Thrainer
        node = nodes[node_uuid]
2212 54c31fd3 Michael Hanselmann
      except KeyError:
2213 1c3231aa Thomas Thrainer
        raise errors.ConfigurationError("Unable to find node '%s'" % node_uuid)
2214 54c31fd3 Michael Hanselmann
2215 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
2216 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
2217 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
2218 1c3231aa Thomas Thrainer
                      node_uuid, node.group)
2219 54c31fd3 Michael Hanselmann
        continue
2220 54c31fd3 Michael Hanselmann
2221 54c31fd3 Michael Hanselmann
      # Try to find current group of node
2222 54c31fd3 Michael Hanselmann
      try:
2223 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
2224 54c31fd3 Michael Hanselmann
      except KeyError:
2225 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
2226 54c31fd3 Michael Hanselmann
                                        node.group)
2227 54c31fd3 Michael Hanselmann
2228 54c31fd3 Michael Hanselmann
      # Try to find new group for node
2229 54c31fd3 Michael Hanselmann
      try:
2230 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
2231 54c31fd3 Michael Hanselmann
      except KeyError:
2232 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
2233 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
2234 54c31fd3 Michael Hanselmann
2235 1c3231aa Thomas Thrainer
      assert node.uuid in old_group.members, \
2236 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
2237 1c3231aa Thomas Thrainer
         " old group '%s'" % (node.uuid, old_group.uuid))
2238 1c3231aa Thomas Thrainer
      assert node.uuid not in new_group.members, \
2239 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
2240 1c3231aa Thomas Thrainer
         " its new group '%s'" % (node.uuid, new_group.uuid))
2241 54c31fd3 Michael Hanselmann
2242 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
2243 54c31fd3 Michael Hanselmann
2244 54c31fd3 Michael Hanselmann
    # Apply changes
2245 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
2246 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
2247 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
2248 54c31fd3 Michael Hanselmann
2249 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
2250 54c31fd3 Michael Hanselmann
2251 54c31fd3 Michael Hanselmann
      # Update members of involved groups
2252 1c3231aa Thomas Thrainer
      if node.uuid in old_group.members:
2253 1c3231aa Thomas Thrainer
        old_group.members.remove(node.uuid)
2254 1c3231aa Thomas Thrainer
      if node.uuid not in new_group.members:
2255 1c3231aa Thomas Thrainer
        new_group.members.append(node.uuid)
2256 54c31fd3 Michael Hanselmann
2257 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
2258 54c31fd3 Michael Hanselmann
    now = time.time()
2259 75191077 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142
2260 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
2261 54c31fd3 Michael Hanselmann
      obj.mtime = now
2262 54c31fd3 Michael Hanselmann
2263 54c31fd3 Michael Hanselmann
    # Force ssconf update
2264 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
2265 54c31fd3 Michael Hanselmann
2266 54c31fd3 Michael Hanselmann
    self._WriteConfig()
2267 54c31fd3 Michael Hanselmann
2268 a8083063 Iustin Pop
  def _BumpSerialNo(self):
2269 a8083063 Iustin Pop
    """Bump up the serial number of the config.
2270 a8083063 Iustin Pop

2271 a8083063 Iustin Pop
    """
2272 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
2273 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
2274 a8083063 Iustin Pop
2275 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
2276 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
2277 76d5d3a3 Iustin Pop

2278 76d5d3a3 Iustin Pop
    """
2279 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
2280 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
2281 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
2282 e0519c34 Dimitris Aragiorgis
            self._config_data.networks.values() +
2283 b87a9c5f Christos Stavrakakis
            self._AllDisks() +
2284 b87a9c5f Christos Stavrakakis
            self._AllNICs() +
2285 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
2286 76d5d3a3 Iustin Pop
2287 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
2288 a8083063 Iustin Pop
    """Read the config data from disk.
2289 a8083063 Iustin Pop

2290 a8083063 Iustin Pop
    """
2291 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
2292 13998ef2 Michael Hanselmann
2293 a8083063 Iustin Pop
    try:
2294 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
2295 13998ef2 Michael Hanselmann
    except Exception, err:
2296 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
2297 5b263ed7 Michael Hanselmann
2298 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
2299 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
2300 5b263ed7 Michael Hanselmann
2301 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
2302 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
2303 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
2304 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
2305 90d726a8 Iustin Pop
2306 1c3231aa Thomas Thrainer
    if not data.cluster.master_node in data.nodes:
2307 1c3231aa Thomas Thrainer
      msg = ("The configuration denotes node %s as master, but does not"
2308 1c3231aa Thomas Thrainer
             " contain information about this node" %
2309 1c3231aa Thomas Thrainer
             data.cluster.master_node)
2310 1c3231aa Thomas Thrainer
      raise errors.ConfigurationError(msg)
2311 1c3231aa Thomas Thrainer
2312 1c3231aa Thomas Thrainer
    master_info = data.nodes[data.cluster.master_node]
2313 1c3231aa Thomas Thrainer
    if master_info.name != self._my_hostname and not accept_foreign:
2314 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
2315 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
2316 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
2317 1c3231aa Thomas Thrainer
             (master_info.name, self._my_hostname))
2318 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
2319 eb180fe2 Iustin Pop
2320 a8083063 Iustin Pop
    self._config_data = data
2321 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
2322 0779e3aa Iustin Pop
    # ssconf update
2323 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
2324 a8083063 Iustin Pop
2325 45f62156 Bernardo Dal Seno
    # Upgrade configuration if needed
2326 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
2327 76d5d3a3 Iustin Pop
2328 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
2329 bd407597 Iustin Pop
2330 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
2331 45f62156 Bernardo Dal Seno
    """Run any upgrade steps.
2332 76d5d3a3 Iustin Pop

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

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

2343 76d5d3a3 Iustin Pop
    """
2344 45f62156 Bernardo Dal Seno
    # Keep a copy of the persistent part of _config_data to check for changes
2345 45f62156 Bernardo Dal Seno
    # Serialization doesn't guarantee order in dictionaries
2346 45f62156 Bernardo Dal Seno
    oldconf = copy.deepcopy(self._config_data.ToDict())
2347 45f62156 Bernardo Dal Seno
2348 45f62156 Bernardo Dal Seno
    # In-object upgrades
2349 45f62156 Bernardo Dal Seno
    self._config_data.UpgradeConfig()
2350 45f62156 Bernardo Dal Seno
2351 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
2352 76d5d3a3 Iustin Pop
      if item.uuid is None:
2353 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
2354 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
2355 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
2356 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
2357 75cf411a Adeodato Simo
                                            members=[])
2358 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
2359 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
2360 f936c153 Iustin Pop
      if not node.group:
2361 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
2362 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
2363 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
2364 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
2365 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
2366 1c3231aa Thomas Thrainer
      self._UnlockedAddNodeToGroup(node.uuid, node.group)
2367 45f62156 Bernardo Dal Seno
2368 45f62156 Bernardo Dal Seno
    modified = (oldconf != self._config_data.ToDict())
2369 76d5d3a3 Iustin Pop
    if modified:
2370 76d5d3a3 Iustin Pop
      self._WriteConfig()
2371 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
2372 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
2373 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
2374 04db1880 Bernardo Dal Seno
    else:
2375 04db1880 Bernardo Dal Seno
      config_errors = self._UnlockedVerifyConfig()
2376 04db1880 Bernardo Dal Seno
      if config_errors:
2377 04db1880 Bernardo Dal Seno
        errmsg = ("Loaded configuration data is not consistent: %s" %
2378 04db1880 Bernardo Dal Seno
                  (utils.CommaJoin(config_errors)))
2379 04db1880 Bernardo Dal Seno
        logging.critical(errmsg)
2380 4fae38c5 Guido Trotter
2381 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
2382 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
2383 a8083063 Iustin Pop

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

2387 a8083063 Iustin Pop
    """
2388 a8083063 Iustin Pop
    if self._offline:
2389 a8083063 Iustin Pop
      return True
2390 a4eae71f Michael Hanselmann
2391 a8083063 Iustin Pop
    bad = False
2392 a8083063 Iustin Pop
2393 6a5b8b4b Iustin Pop
    node_list = []
2394 6a5b8b4b Iustin Pop
    addr_list = []
2395 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
2396 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
2397 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
2398 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
2399 6b294c53 Iustin Pop
    # in between
2400 1c3231aa Thomas Thrainer
    for node_uuid in self._UnlockedGetNodeList():
2401 1c3231aa Thomas Thrainer
      node_info = self._UnlockedGetNodeInfo(node_uuid)
2402 1c3231aa Thomas Thrainer
      if node_info.name == myhostname or not node_info.master_candidate:
2403 6a5b8b4b Iustin Pop
        continue
2404 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
2405 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
2406 6b294c53 Iustin Pop
2407 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
2408 415a7304 Michael Hanselmann
    result = \
2409 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
2410 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
2411 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
2412 1b54fc6c Guido Trotter
      if msg:
2413 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
2414 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
2415 1b54fc6c Guido Trotter
        logging.error(msg)
2416 a4eae71f Michael Hanselmann
2417 a4eae71f Michael Hanselmann
        if feedback_fn:
2418 a4eae71f Michael Hanselmann
          feedback_fn(msg)
2419 a4eae71f Michael Hanselmann
2420 a8083063 Iustin Pop
        bad = True
2421 a4eae71f Michael Hanselmann
2422 a8083063 Iustin Pop
    return not bad
2423 a8083063 Iustin Pop
2424 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
2425 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
2426 a8083063 Iustin Pop

2427 a8083063 Iustin Pop
    """
2428 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
2429 a4eae71f Michael Hanselmann
2430 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
2431 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
2432 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
2433 d2231b8c Iustin Pop
    # recovery to the user
2434 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
2435 4a89c54a Iustin Pop
    if config_errors:
2436 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
2437 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
2438 d2231b8c Iustin Pop
      logging.critical(errmsg)
2439 d2231b8c Iustin Pop
      if feedback_fn:
2440 d2231b8c Iustin Pop
        feedback_fn(errmsg)
2441 d2231b8c Iustin Pop
2442 a8083063 Iustin Pop
    if destination is None:
2443 a8083063 Iustin Pop
      destination = self._cfg_file
2444 a8083063 Iustin Pop
    self._BumpSerialNo()
2445 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
2446 13998ef2 Michael Hanselmann
2447 e60c73a1 René Nussbaumer
    getents = self._getents()
2448 bd407597 Iustin Pop
    try:
2449 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
2450 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
2451 bd407597 Iustin Pop
    except errors.LockError:
2452 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
2453 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
2454 bd407597 Iustin Pop
                                      " update")
2455 bd407597 Iustin Pop
    try:
2456 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
2457 bd407597 Iustin Pop
    finally:
2458 bd407597 Iustin Pop
      os.close(fd)
2459 13998ef2 Michael Hanselmann
2460 14e15659 Iustin Pop
    self.write_count += 1
2461 3d3a04bc Iustin Pop
2462 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
2463 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
2464 a8083063 Iustin Pop
2465 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
2466 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
2467 d9a855f1 Michael Hanselmann
      if not self._offline:
2468 b2acdbdc Michael Hanselmann
        result = self._GetRpc(None).call_write_ssconf_files(
2469 1c3231aa Thomas Thrainer
          self._UnlockedGetNodeNames(self._UnlockedGetOnlineNodeList()),
2470 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
2471 a4eae71f Michael Hanselmann
2472 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
2473 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
2474 e1e75d00 Iustin Pop
          if msg:
2475 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
2476 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
2477 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
2478 a4eae71f Michael Hanselmann
2479 a4eae71f Michael Hanselmann
            if feedback_fn:
2480 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
2481 a4eae71f Michael Hanselmann
2482 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
2483 54d1a06e Michael Hanselmann
2484 def6577f Helga Velroyen
  def _GetAllHvparamsStrings(self, hypervisors):
2485 def6577f Helga Velroyen
    """Get the hvparams of all given hypervisors from the config.
2486 def6577f Helga Velroyen

2487 def6577f Helga Velroyen
    @type hypervisors: list of string
2488 def6577f Helga Velroyen
    @param hypervisors: list of hypervisor names
2489 def6577f Helga Velroyen
    @rtype: dict of strings
2490 def6577f Helga Velroyen
    @returns: dictionary mapping the hypervisor name to a string representation
2491 def6577f Helga Velroyen
      of the hypervisor's hvparams
2492 def6577f Helga Velroyen

2493 def6577f Helga Velroyen
    """
2494 def6577f Helga Velroyen
    hvparams = {}
2495 def6577f Helga Velroyen
    for hv in hypervisors:
2496 def6577f Helga Velroyen
      hvparams[hv] = self._UnlockedGetHvparamsString(hv)
2497 def6577f Helga Velroyen
    return hvparams
2498 def6577f Helga Velroyen
2499 def6577f Helga Velroyen
  @staticmethod
2500 def6577f Helga Velroyen
  def _ExtendByAllHvparamsStrings(ssconf_values, all_hvparams):
2501 def6577f Helga Velroyen
    """Extends the ssconf_values dictionary by hvparams.
2502 def6577f Helga Velroyen

2503 def6577f Helga Velroyen
    @type ssconf_values: dict of strings
2504 def6577f Helga Velroyen
    @param ssconf_values: dictionary mapping ssconf_keys to strings
2505 def6577f Helga Velroyen
      representing the content of ssconf files
2506 def6577f Helga Velroyen
    @type all_hvparams: dict of strings
2507 def6577f Helga Velroyen
    @param all_hvparams: dictionary mapping hypervisor names to a string
2508 def6577f Helga Velroyen
      representation of their hvparams
2509 def6577f Helga Velroyen
    @rtype: same as ssconf_values
2510 def6577f Helga Velroyen
    @returns: the ssconf_values dictionary extended by hvparams
2511 def6577f Helga Velroyen

2512 def6577f Helga Velroyen
    """
2513 def6577f Helga Velroyen
    for hv in all_hvparams:
2514 def6577f Helga Velroyen
      ssconf_key = constants.SS_HVPARAMS_PREF + hv
2515 def6577f Helga Velroyen
      ssconf_values[ssconf_key] = all_hvparams[hv]
2516 def6577f Helga Velroyen
    return ssconf_values
2517 def6577f Helga Velroyen
2518 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
2519 054596f0 Iustin Pop
    """Return the values needed by ssconf.
2520 054596f0 Iustin Pop

2521 054596f0 Iustin Pop
    @rtype: dict
2522 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2523 054596f0 Iustin Pop
        associated value
2524 054596f0 Iustin Pop

2525 054596f0 Iustin Pop
    """
2526 a3316e4a Iustin Pop
    fn = "\n".join
2527 da4a52a3 Thomas Thrainer
    instance_names = utils.NiceSort(
2528 da4a52a3 Thomas Thrainer
                       [inst.name for inst in
2529 da4a52a3 Thomas Thrainer
                        self._UnlockedGetAllInstancesInfo().values()])
2530 1c3231aa Thomas Thrainer
    node_infos = self._UnlockedGetAllNodesInfo().values()
2531 1c3231aa Thomas Thrainer
    node_names = [node.name for node in node_infos]
2532 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
2533 1c3231aa Thomas Thrainer
                    for ninfo in node_infos]
2534 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
2535 1c3231aa Thomas Thrainer
                    for ninfo in node_infos]
2536 a3316e4a Iustin Pop
2537 81a49123 Iustin Pop
    instance_data = fn(instance_names)
2538 1c3231aa Thomas Thrainer
    off_data = fn(node.name for node in node_infos if node.offline)
2539 1c3231aa Thomas Thrainer
    on_data = fn(node.name for node in node_infos if not node.offline)
2540 1c3231aa Thomas Thrainer
    mc_data = fn(node.name for node in node_infos if node.master_candidate)
2541 1c3231aa Thomas Thrainer
    mc_ips_data = fn(node.primary_ip for node in node_infos
2542 8113a52e Luca Bigliardi
                     if node.master_candidate)
2543 a3316e4a Iustin Pop
    node_data = fn(node_names)
2544 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
2545 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
2546 f56618e0 Iustin Pop
2547 054596f0 Iustin Pop
    cluster = self._config_data.cluster
2548 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
2549 4f7a6a10 Iustin Pop
2550 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
2551 def6577f Helga Velroyen
    all_hvparams = self._GetAllHvparamsStrings(constants.HYPER_TYPES)
2552 4f7a6a10 Iustin Pop
2553 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
2554 0fbae49a Balazs Lecz
2555 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
2556 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
2557 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
2558 6c0a75db Dimitris Aragiorgis
    networks = ["%s %s" % (net.uuid, net.name) for net in
2559 6c0a75db Dimitris Aragiorgis
                self._config_data.networks.values()]
2560 6c0a75db Dimitris Aragiorgis
    networks_data = fn(utils.NiceSort(networks))
2561 6f076453 Guido Trotter
2562 2afc9238 Iustin Pop
    ssconf_values = {
2563 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
2564 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
2565 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
2566 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
2567 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
2568 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
2569 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
2570 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
2571 5a8648eb Andrea Spadaccini
      constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
2572 1c3231aa Thomas Thrainer
      constants.SS_MASTER_NODE: self._UnlockedGetNodeName(cluster.master_node),
2573 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
2574 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
2575 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
2576 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
2577 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
2578 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
2579 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
2580 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
2581 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
2582 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
2583 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
2584 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
2585 6c0a75db Dimitris Aragiorgis
      constants.SS_NETWORKS: networks_data,
2586 03d1dba2 Michael Hanselmann
      }
2587 def6577f Helga Velroyen
    ssconf_values = self._ExtendByAllHvparamsStrings(ssconf_values,
2588 def6577f Helga Velroyen
                                                     all_hvparams)
2589 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
2590 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
2591 2afc9238 Iustin Pop
    if bad_values:
2592 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
2593 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
2594 2afc9238 Iustin Pop
                                      " values: %s" % err)
2595 2afc9238 Iustin Pop
    return ssconf_values
2596 03d1dba2 Michael Hanselmann
2597 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2598 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
2599 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
2600 d367b66c Manuel Franceschini

2601 d367b66c Manuel Franceschini
    """
2602 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2603 d367b66c Manuel Franceschini
2604 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2605 a8083063 Iustin Pop
  def GetVGName(self):
2606 a8083063 Iustin Pop
    """Return the volume group name.
2607 a8083063 Iustin Pop

2608 a8083063 Iustin Pop
    """
2609 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2610 a8083063 Iustin Pop
2611 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2612 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2613 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2614 89ff8e15 Manuel Franceschini

2615 89ff8e15 Manuel Franceschini
    """
2616 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2617 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2618 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2619 89ff8e15 Manuel Franceschini
2620 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2621 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2622 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2623 9e33896b Luca Bigliardi

2624 9e33896b Luca Bigliardi
    """
2625 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2626 9e33896b Luca Bigliardi
2627 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2628 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2629 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2630 9e33896b Luca Bigliardi

2631 9e33896b Luca Bigliardi
    """
2632 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2633 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2634 9e33896b Luca Bigliardi
    self._WriteConfig()
2635 9e33896b Luca Bigliardi
2636 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2637 a8083063 Iustin Pop
  def GetMACPrefix(self):
2638 a8083063 Iustin Pop
    """Return the mac prefix.
2639 a8083063 Iustin Pop

2640 a8083063 Iustin Pop
    """
2641 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2642 62779dd0 Iustin Pop
2643 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2644 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2645 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2646 62779dd0 Iustin Pop

2647 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2648 c41eea6e Iustin Pop
    @return: the cluster object
2649 62779dd0 Iustin Pop

2650 62779dd0 Iustin Pop
    """
2651 62779dd0 Iustin Pop
    return self._config_data.cluster
2652 e00fb268 Iustin Pop
2653 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2654 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2655 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2656 51cb1581 Luca Bigliardi

2657 51cb1581 Luca Bigliardi
    """
2658 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2659 51cb1581 Luca Bigliardi
2660 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2661 d1547283 Dimitris Aragiorgis
  def Update(self, target, feedback_fn, ec_id=None):
2662 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2663 e00fb268 Iustin Pop

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

2670 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2671 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2672 c41eea6e Iustin Pop
        the cluster
2673 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2674 c41eea6e Iustin Pop

2675 e00fb268 Iustin Pop
    """
2676 e00fb268 Iustin Pop
    if self._config_data is None:
2677 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2678 3ecf6786 Iustin Pop
                                   " cannot save.")
2679 f34901f8 Iustin Pop
    update_serial = False
2680 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2681 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2682 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2683 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2684 f34901f8 Iustin Pop
      update_serial = True
2685 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2686 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2687 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2688 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2689 1e0d3321 Dimitris Aragiorgis
    elif isinstance(target, objects.Network):
2690 1e0d3321 Dimitris Aragiorgis
      test = target in self._config_data.networks.values()
2691 e00fb268 Iustin Pop
    else:
2692 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2693 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2694 e00fb268 Iustin Pop
    if not test:
2695 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2696 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2697 f34901f8 Iustin Pop
    target.serial_no += 1
2698 d693c864 Iustin Pop
    target.mtime = now = time.time()
2699 f34901f8 Iustin Pop
2700 cff4c037 Iustin Pop
    if update_serial:
2701 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2702 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2703 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2704 b989e85d Iustin Pop
2705 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2706 da4a52a3 Thomas Thrainer
      self._UnlockedReleaseDRBDMinors(target.uuid)
2707 61cf6b5e Iustin Pop
2708 d1547283 Dimitris Aragiorgis
    if ec_id is not None:
2709 d1547283 Dimitris Aragiorgis
      # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams
2710 d1547283 Dimitris Aragiorgis
      self._UnlockedCommitTemporaryIps(ec_id)
2711 d1547283 Dimitris Aragiorgis
2712 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2713 73064714 Guido Trotter
2714 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2715 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2716 73064714 Guido Trotter
    """Drop per-execution-context reservations
2717 73064714 Guido Trotter

2718 73064714 Guido Trotter
    """
2719 d8aee57e Iustin Pop
    for rm in self._all_rms:
2720 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)
2721 6c0a75db Dimitris Aragiorgis
2722 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2723 6c0a75db Dimitris Aragiorgis
  def GetAllNetworksInfo(self):
2724 6a94d553 Dimitris Aragiorgis
    """Get configuration info of all the networks.
2725 6c0a75db Dimitris Aragiorgis

2726 6c0a75db Dimitris Aragiorgis
    """
2727 6c0a75db Dimitris Aragiorgis
    return dict(self._config_data.networks)
2728 6c0a75db Dimitris Aragiorgis
2729 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetworkList(self):
2730 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2731 6c0a75db Dimitris Aragiorgis

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

2734 6c0a75db Dimitris Aragiorgis
    """
2735 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks.keys()
2736 6c0a75db Dimitris Aragiorgis
2737 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2738 6c0a75db Dimitris Aragiorgis
  def GetNetworkList(self):
2739 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2740 6c0a75db Dimitris Aragiorgis

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

2743 6c0a75db Dimitris Aragiorgis
    """
2744 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetworkList()
2745 6c0a75db Dimitris Aragiorgis
2746 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2747 6c0a75db Dimitris Aragiorgis
  def GetNetworkNames(self):
2748 6c0a75db Dimitris Aragiorgis
    """Get a list of network names
2749 6c0a75db Dimitris Aragiorgis

2750 6c0a75db Dimitris Aragiorgis
    """
2751 beb81ea5 Dimitris Aragiorgis
    names = [net.name
2752 beb81ea5 Dimitris Aragiorgis
             for net in self._config_data.networks.values()]
2753 6c0a75db Dimitris Aragiorgis
    return names
2754 6c0a75db Dimitris Aragiorgis
2755 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetwork(self, uuid):
2756 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2757 6c0a75db Dimitris Aragiorgis

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

2760 6c0a75db Dimitris Aragiorgis
    """
2761 6c0a75db Dimitris Aragiorgis
    if uuid not in self._config_data.networks:
2762 6c0a75db Dimitris Aragiorgis
      return None
2763 6c0a75db Dimitris Aragiorgis
2764 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks[uuid]
2765 6c0a75db Dimitris Aragiorgis
2766 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2767 6c0a75db Dimitris Aragiorgis
  def GetNetwork(self, uuid):
2768 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2769 6c0a75db Dimitris Aragiorgis

2770 6c0a75db Dimitris Aragiorgis
    It takes the information from the configuration file.
2771 6c0a75db Dimitris Aragiorgis

2772 6c0a75db Dimitris Aragiorgis
    @param uuid: UUID of the network
2773 6c0a75db Dimitris Aragiorgis

2774 6c0a75db Dimitris Aragiorgis
    @rtype: L{objects.Network}
2775 6c0a75db Dimitris Aragiorgis
    @return: the network object
2776 6c0a75db Dimitris Aragiorgis

2777 6c0a75db Dimitris Aragiorgis
    """
2778 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetwork(uuid)
2779 6c0a75db Dimitris Aragiorgis
2780 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2781 6c0a75db Dimitris Aragiorgis
  def AddNetwork(self, net, ec_id, check_uuid=True):
2782 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2783 6c0a75db Dimitris Aragiorgis

2784 6c0a75db Dimitris Aragiorgis
    @type net: L{objects.Network}
2785 6c0a75db Dimitris Aragiorgis
    @param net: the Network object to add
2786 6c0a75db Dimitris Aragiorgis
    @type ec_id: string
2787 6c0a75db Dimitris Aragiorgis
    @param ec_id: unique id for the job to use when creating a missing UUID
2788 6c0a75db Dimitris Aragiorgis

2789 6c0a75db Dimitris Aragiorgis
    """
2790 6c0a75db Dimitris Aragiorgis
    self._UnlockedAddNetwork(net, ec_id, check_uuid)
2791 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2792 6c0a75db Dimitris Aragiorgis
2793 6c0a75db Dimitris Aragiorgis
  def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
2794 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2795 6c0a75db Dimitris Aragiorgis

2796 6c0a75db Dimitris Aragiorgis
    """
2797 6c0a75db Dimitris Aragiorgis
    logging.info("Adding network %s to configuration", net.name)
2798 6c0a75db Dimitris Aragiorgis
2799 6c0a75db Dimitris Aragiorgis
    if check_uuid:
2800 6c0a75db Dimitris Aragiorgis
      self._EnsureUUID(net, ec_id)
2801 6c0a75db Dimitris Aragiorgis
2802 6c0a75db Dimitris Aragiorgis
    net.serial_no = 1
2803 6c0a75db Dimitris Aragiorgis
    self._config_data.networks[net.uuid] = net
2804 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2805 6c0a75db Dimitris Aragiorgis
2806 6c0a75db Dimitris Aragiorgis
  def _UnlockedLookupNetwork(self, target):
2807 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2808 6c0a75db Dimitris Aragiorgis

2809 6c0a75db Dimitris Aragiorgis
    @type target: string
2810 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2811 6c0a75db Dimitris Aragiorgis
    @rtype: string
2812 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2813 6c0a75db Dimitris Aragiorgis
    @raises errors.OpPrereqError: when the target network cannot be found
2814 6c0a75db Dimitris Aragiorgis

2815 6c0a75db Dimitris Aragiorgis
    """
2816 9394f4d1 Dimitris Aragiorgis
    if target is None:
2817 9394f4d1 Dimitris Aragiorgis
      return None
2818 6c0a75db Dimitris Aragiorgis
    if target in self._config_data.networks:
2819 6c0a75db Dimitris Aragiorgis
      return target
2820 6c0a75db Dimitris Aragiorgis
    for net in self._config_data.networks.values():
2821 6c0a75db Dimitris Aragiorgis
      if net.name == target:
2822 6c0a75db Dimitris Aragiorgis
        return net.uuid
2823 1b68f268 Helga Velroyen
    raise errors.OpPrereqError("Network '%s' not found" % target,
2824 1b68f268 Helga Velroyen
                               errors.ECODE_NOENT)
2825 6c0a75db Dimitris Aragiorgis
2826 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2827 6c0a75db Dimitris Aragiorgis
  def LookupNetwork(self, target):
2828 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2829 6c0a75db Dimitris Aragiorgis

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

2832 6c0a75db Dimitris Aragiorgis
    @type target: string
2833 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2834 6c0a75db Dimitris Aragiorgis
    @rtype: string
2835 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2836 6c0a75db Dimitris Aragiorgis

2837 6c0a75db Dimitris Aragiorgis
    """
2838 6c0a75db Dimitris Aragiorgis
    return self._UnlockedLookupNetwork(target)
2839 6c0a75db Dimitris Aragiorgis
2840 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2841 6c0a75db Dimitris Aragiorgis
  def RemoveNetwork(self, network_uuid):
2842 6c0a75db Dimitris Aragiorgis
    """Remove a network from the configuration.
2843 6c0a75db Dimitris Aragiorgis

2844 6c0a75db Dimitris Aragiorgis
    @type network_uuid: string
2845 6c0a75db Dimitris Aragiorgis
    @param network_uuid: the UUID of the network to remove
2846 6c0a75db Dimitris Aragiorgis

2847 6c0a75db Dimitris Aragiorgis
    """
2848 6c0a75db Dimitris Aragiorgis
    logging.info("Removing network %s from configuration", network_uuid)
2849 6c0a75db Dimitris Aragiorgis
2850 6c0a75db Dimitris Aragiorgis
    if network_uuid not in self._config_data.networks:
2851 6c0a75db Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
2852 6c0a75db Dimitris Aragiorgis
2853 6c0a75db Dimitris Aragiorgis
    del self._config_data.networks[network_uuid]
2854 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2855 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2856 ad4a9ae7 Dimitris Aragiorgis
2857 1c3231aa Thomas Thrainer
  def _UnlockedGetGroupNetParams(self, net_uuid, node_uuid):
2858 ad4a9ae7 Dimitris Aragiorgis
    """Get the netparams (mode, link) of a network.
2859 ad4a9ae7 Dimitris Aragiorgis

2860 ad4a9ae7 Dimitris Aragiorgis
    Get a network's netparams for a given node.
2861 ad4a9ae7 Dimitris Aragiorgis

2862 9ccacbc8 Dimitris Aragiorgis
    @type net_uuid: string
2863 9ccacbc8 Dimitris Aragiorgis
    @param net_uuid: network uuid
2864 1c3231aa Thomas Thrainer
    @type node_uuid: string
2865 1c3231aa Thomas Thrainer
    @param node_uuid: node UUID
2866 ad4a9ae7 Dimitris Aragiorgis
    @rtype: dict or None
2867 ad4a9ae7 Dimitris Aragiorgis
    @return: netparams
2868 ad4a9ae7 Dimitris Aragiorgis

2869 ad4a9ae7 Dimitris Aragiorgis
    """
2870 1c3231aa Thomas Thrainer
    node_info = self._UnlockedGetNodeInfo(node_uuid)
2871 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2872 ad4a9ae7 Dimitris Aragiorgis
    netparams = nodegroup_info.networks.get(net_uuid, None)
2873 ad4a9ae7 Dimitris Aragiorgis
2874 ad4a9ae7 Dimitris Aragiorgis
    return netparams
2875 ad4a9ae7 Dimitris Aragiorgis
2876 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2877 1c3231aa Thomas Thrainer
  def GetGroupNetParams(self, net_uuid, node_uuid):
2878 ad4a9ae7 Dimitris Aragiorgis
    """Locking wrapper of _UnlockedGetGroupNetParams()
2879 ad4a9ae7 Dimitris Aragiorgis

2880 ad4a9ae7 Dimitris Aragiorgis
    """
2881 1c3231aa Thomas Thrainer
    return self._UnlockedGetGroupNetParams(net_uuid, node_uuid)
2882 ad4a9ae7 Dimitris Aragiorgis
2883 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2884 1c3231aa Thomas Thrainer
  def CheckIPInNodeGroup(self, ip, node_uuid):
2885 6a94d553 Dimitris Aragiorgis
    """Check IP uniqueness in nodegroup.
2886 6a94d553 Dimitris Aragiorgis

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

2891 ad4a9ae7 Dimitris Aragiorgis
    @type ip: string
2892 ad4a9ae7 Dimitris Aragiorgis
    @param ip: ip address
2893 1c3231aa Thomas Thrainer
    @type node_uuid: string
2894 1c3231aa Thomas Thrainer
    @param node_uuid: node UUID
2895 ad4a9ae7 Dimitris Aragiorgis
    @rtype: (string, dict) or (None, None)
2896 ad4a9ae7 Dimitris Aragiorgis
    @return: (network name, netparams)
2897 ad4a9ae7 Dimitris Aragiorgis

2898 ad4a9ae7 Dimitris Aragiorgis
    """
2899 ad4a9ae7 Dimitris Aragiorgis
    if ip is None:
2900 ad4a9ae7 Dimitris Aragiorgis
      return (None, None)
2901 1c3231aa Thomas Thrainer
    node_info = self._UnlockedGetNodeInfo(node_uuid)
2902 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2903 ad4a9ae7 Dimitris Aragiorgis
    for net_uuid in nodegroup_info.networks.keys():
2904 ad4a9ae7 Dimitris Aragiorgis
      net_info = self._UnlockedGetNetwork(net_uuid)
2905 ad4a9ae7 Dimitris Aragiorgis
      pool = network.AddressPool(net_info)
2906 beb81ea5 Dimitris Aragiorgis
      if pool.Contains(ip):
2907 ad4a9ae7 Dimitris Aragiorgis
        return (net_info.name, nodegroup_info.networks[net_uuid])
2908 ad4a9ae7 Dimitris Aragiorgis
2909 ad4a9ae7 Dimitris Aragiorgis
    return (None, None)