Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 25cf4130

History | View | Annotate | Download (99.1 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 9a94cee3 Jose A. Lopes
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Configuration management for Ganeti
23 a8083063 Iustin Pop

24 319856a9 Michael Hanselmann
This module provides the interface to the Ganeti cluster configuration.
25 a8083063 Iustin Pop

26 319856a9 Michael Hanselmann
The configuration data is stored on every node but is updated on the master
27 319856a9 Michael Hanselmann
only. After each update, the master distributes the data to the other nodes.
28 a8083063 Iustin Pop

29 319856a9 Michael Hanselmann
Currently, the data storage format is JSON. YAML was slow and consuming too
30 319856a9 Michael Hanselmann
much memory.
31 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

260 8a147bba René Nussbaumer
    """
261 8a147bba René Nussbaumer
    node = self._UnlockedGetNodeInfo(instance.primary_node)
262 8a147bba René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
263 99ccf8b9 René Nussbaumer
    return self._UnlockedGetGroupDiskParams(nodegroup)
264 99ccf8b9 René Nussbaumer
265 99ccf8b9 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
266 99ccf8b9 René Nussbaumer
  def GetGroupDiskParams(self, group):
267 99ccf8b9 René Nussbaumer
    """Get the disk params populated with inherit chain.
268 99ccf8b9 René Nussbaumer

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

273 99ccf8b9 René Nussbaumer
    """
274 99ccf8b9 René Nussbaumer
    return self._UnlockedGetGroupDiskParams(group)
275 99ccf8b9 René Nussbaumer
276 99ccf8b9 René Nussbaumer
  def _UnlockedGetGroupDiskParams(self, group):
277 99ccf8b9 René Nussbaumer
    """Get the disk params populated with inherit chain down to node-group.
278 99ccf8b9 René Nussbaumer

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

283 99ccf8b9 René Nussbaumer
    """
284 99ccf8b9 René Nussbaumer
    return self._config_data.cluster.SimpleFillDP(group.diskparams)
285 8a147bba René Nussbaumer
286 9ccacbc8 Dimitris Aragiorgis
  def _UnlockedGetNetworkMACPrefix(self, net_uuid):
287 032a7d71 Dimitris Aragiorgis
    """Return the network mac prefix if it exists or the cluster level default.
288 032a7d71 Dimitris Aragiorgis

289 032a7d71 Dimitris Aragiorgis
    """
290 032a7d71 Dimitris Aragiorgis
    prefix = None
291 9ccacbc8 Dimitris Aragiorgis
    if net_uuid:
292 1b68f268 Helga Velroyen
      nobj = self._UnlockedGetNetwork(net_uuid)
293 1b68f268 Helga Velroyen
      if nobj.mac_prefix:
294 1b68f268 Helga Velroyen
        prefix = nobj.mac_prefix
295 032a7d71 Dimitris Aragiorgis
296 032a7d71 Dimitris Aragiorgis
    return prefix
297 032a7d71 Dimitris Aragiorgis
298 032a7d71 Dimitris Aragiorgis
  def _GenerateOneMAC(self, prefix=None):
299 032a7d71 Dimitris Aragiorgis
    """Return a function that randomly generates a MAC suffic
300 032a7d71 Dimitris Aragiorgis
       and appends it to the given prefix. If prefix is not given get
301 032a7d71 Dimitris Aragiorgis
       the cluster level default.
302 032a7d71 Dimitris Aragiorgis

303 032a7d71 Dimitris Aragiorgis
    """
304 032a7d71 Dimitris Aragiorgis
    if not prefix:
305 032a7d71 Dimitris Aragiorgis
      prefix = self._config_data.cluster.mac_prefix
306 032a7d71 Dimitris Aragiorgis
307 032a7d71 Dimitris Aragiorgis
    def GenMac():
308 032a7d71 Dimitris Aragiorgis
      byte1 = random.randrange(0, 256)
309 032a7d71 Dimitris Aragiorgis
      byte2 = random.randrange(0, 256)
310 032a7d71 Dimitris Aragiorgis
      byte3 = random.randrange(0, 256)
311 032a7d71 Dimitris Aragiorgis
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
312 032a7d71 Dimitris Aragiorgis
      return mac
313 032a7d71 Dimitris Aragiorgis
314 032a7d71 Dimitris Aragiorgis
    return GenMac
315 032a7d71 Dimitris Aragiorgis
316 8a147bba René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
317 9ccacbc8 Dimitris Aragiorgis
  def GenerateMAC(self, net_uuid, ec_id):
318 a8083063 Iustin Pop
    """Generate a MAC for an instance.
319 a8083063 Iustin Pop

320 a8083063 Iustin Pop
    This should check the current instances for duplicates.
321 a8083063 Iustin Pop

322 a8083063 Iustin Pop
    """
323 36b66e6e Guido Trotter
    existing = self._AllMACs()
324 9ccacbc8 Dimitris Aragiorgis
    prefix = self._UnlockedGetNetworkMACPrefix(net_uuid)
325 032a7d71 Dimitris Aragiorgis
    gen_mac = self._GenerateOneMAC(prefix)
326 a0af6c80 Dimitris Aragiorgis
    return self._temporary_ids.Generate(existing, gen_mac, ec_id)
327 a8083063 Iustin Pop
328 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
329 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
330 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
331 1862d460 Alexander Schreiber

332 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
333 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
334 1862d460 Alexander Schreiber

335 1862d460 Alexander Schreiber
    """
336 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
337 36b66e6e Guido Trotter
    if mac in all_macs:
338 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
339 36b66e6e Guido Trotter
    else:
340 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
341 1862d460 Alexander Schreiber
342 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedCommitTemporaryIps(self, ec_id):
343 ad4a9ae7 Dimitris Aragiorgis
    """Commit all reserved IP address to their respective pools
344 ad4a9ae7 Dimitris Aragiorgis

345 ad4a9ae7 Dimitris Aragiorgis
    """
346 ad4a9ae7 Dimitris Aragiorgis
    for action, address, net_uuid in self._temporary_ips.GetECReserved(ec_id):
347 ad4a9ae7 Dimitris Aragiorgis
      self._UnlockedCommitIp(action, net_uuid, address)
348 ad4a9ae7 Dimitris Aragiorgis
349 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedCommitIp(self, action, net_uuid, address):
350 ad4a9ae7 Dimitris Aragiorgis
    """Commit a reserved IP address to an IP pool.
351 ad4a9ae7 Dimitris Aragiorgis

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

354 ad4a9ae7 Dimitris Aragiorgis
    """
355 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
356 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
357 e81eef56 Dimitris Aragiorgis
    if action == constants.RESERVE_ACTION:
358 ad4a9ae7 Dimitris Aragiorgis
      pool.Reserve(address)
359 e81eef56 Dimitris Aragiorgis
    elif action == constants.RELEASE_ACTION:
360 ad4a9ae7 Dimitris Aragiorgis
      pool.Release(address)
361 ad4a9ae7 Dimitris Aragiorgis
362 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedReleaseIp(self, net_uuid, address, ec_id):
363 ad4a9ae7 Dimitris Aragiorgis
    """Give a specific IP address back to an IP pool.
364 ad4a9ae7 Dimitris Aragiorgis

365 ad4a9ae7 Dimitris Aragiorgis
    The IP address is returned to the IP pool designated by pool_id and marked
366 ad4a9ae7 Dimitris Aragiorgis
    as reserved.
367 ad4a9ae7 Dimitris Aragiorgis

368 ad4a9ae7 Dimitris Aragiorgis
    """
369 e81eef56 Dimitris Aragiorgis
    self._temporary_ips.Reserve(ec_id,
370 e81eef56 Dimitris Aragiorgis
                                (constants.RELEASE_ACTION, address, net_uuid))
371 ad4a9ae7 Dimitris Aragiorgis
372 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
373 9ccacbc8 Dimitris Aragiorgis
  def ReleaseIp(self, net_uuid, address, ec_id):
374 ad4a9ae7 Dimitris Aragiorgis
    """Give a specified IP address back to an IP pool.
375 ad4a9ae7 Dimitris Aragiorgis

376 ad4a9ae7 Dimitris Aragiorgis
    This is just a wrapper around _UnlockedReleaseIp.
377 ad4a9ae7 Dimitris Aragiorgis

378 ad4a9ae7 Dimitris Aragiorgis
    """
379 9ccacbc8 Dimitris Aragiorgis
    if net_uuid:
380 9ccacbc8 Dimitris Aragiorgis
      self._UnlockedReleaseIp(net_uuid, address, ec_id)
381 ad4a9ae7 Dimitris Aragiorgis
382 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
383 9ccacbc8 Dimitris Aragiorgis
  def GenerateIp(self, net_uuid, ec_id):
384 ad4a9ae7 Dimitris Aragiorgis
    """Find a free IPv4 address for an instance.
385 ad4a9ae7 Dimitris Aragiorgis

386 ad4a9ae7 Dimitris Aragiorgis
    """
387 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
388 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
389 ad4a9ae7 Dimitris Aragiorgis
390 ad4a9ae7 Dimitris Aragiorgis
    def gen_one():
391 ad4a9ae7 Dimitris Aragiorgis
      try:
392 1f1d3bf2 Dimitris Aragiorgis
        ip = pool.GenerateFree()
393 1f1d3bf2 Dimitris Aragiorgis
      except errors.AddressPoolError:
394 ad4a9ae7 Dimitris Aragiorgis
        raise errors.ReservationError("Cannot generate IP. Network is full")
395 e81eef56 Dimitris Aragiorgis
      return (constants.RESERVE_ACTION, ip, net_uuid)
396 ad4a9ae7 Dimitris Aragiorgis
397 beb81ea5 Dimitris Aragiorgis
    _, address, _ = self._temporary_ips.Generate([], gen_one, ec_id)
398 ad4a9ae7 Dimitris Aragiorgis
    return address
399 ad4a9ae7 Dimitris Aragiorgis
400 031d2db1 Dimitris Aragiorgis
  def _UnlockedReserveIp(self, net_uuid, address, ec_id, check=True):
401 ad4a9ae7 Dimitris Aragiorgis
    """Reserve a given IPv4 address for use by an instance.
402 ad4a9ae7 Dimitris Aragiorgis

403 ad4a9ae7 Dimitris Aragiorgis
    """
404 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
405 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
406 ad4a9ae7 Dimitris Aragiorgis
    try:
407 ad4a9ae7 Dimitris Aragiorgis
      isreserved = pool.IsReserved(address)
408 031d2db1 Dimitris Aragiorgis
      isextreserved = pool.IsReserved(address, external=True)
409 ad4a9ae7 Dimitris Aragiorgis
    except errors.AddressPoolError:
410 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address not in network")
411 ad4a9ae7 Dimitris Aragiorgis
    if isreserved:
412 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address already in use")
413 031d2db1 Dimitris Aragiorgis
    if check and isextreserved:
414 031d2db1 Dimitris Aragiorgis
      raise errors.ReservationError("IP is externally reserved")
415 ad4a9ae7 Dimitris Aragiorgis
416 e81eef56 Dimitris Aragiorgis
    return self._temporary_ips.Reserve(ec_id,
417 e81eef56 Dimitris Aragiorgis
                                       (constants.RESERVE_ACTION,
418 e81eef56 Dimitris Aragiorgis
                                        address, net_uuid))
419 ad4a9ae7 Dimitris Aragiorgis
420 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
421 031d2db1 Dimitris Aragiorgis
  def ReserveIp(self, net_uuid, address, ec_id, check=True):
422 ad4a9ae7 Dimitris Aragiorgis
    """Reserve a given IPv4 address for use by an instance.
423 ad4a9ae7 Dimitris Aragiorgis

424 ad4a9ae7 Dimitris Aragiorgis
    """
425 9ccacbc8 Dimitris Aragiorgis
    if net_uuid:
426 031d2db1 Dimitris Aragiorgis
      return self._UnlockedReserveIp(net_uuid, address, ec_id, check)
427 ad4a9ae7 Dimitris Aragiorgis
428 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
429 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
430 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
431 d8aee57e Iustin Pop

432 d8aee57e Iustin Pop
    @type lv_name: string
433 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
434 d8aee57e Iustin Pop

435 d8aee57e Iustin Pop
    """
436 d8aee57e Iustin Pop
    all_lvs = self._AllLVs()
437 d8aee57e Iustin Pop
    if lv_name in all_lvs:
438 d8aee57e Iustin Pop
      raise errors.ReservationError("LV already in use")
439 d8aee57e Iustin Pop
    else:
440 8785b71b Apollon Oikonomopoulos
      self._temporary_lvs.Reserve(ec_id, lv_name)
441 d8aee57e Iustin Pop
442 d8aee57e Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
443 afa1386e Guido Trotter
  def GenerateDRBDSecret(self, ec_id):
444 f9518d38 Iustin Pop
    """Generate a DRBD secret.
445 f9518d38 Iustin Pop

446 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
447 f9518d38 Iustin Pop

448 f9518d38 Iustin Pop
    """
449 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
450 afa1386e Guido Trotter
                                            utils.GenerateSecret,
451 afa1386e Guido Trotter
                                            ec_id)
452 8d9c3bef Michael Hanselmann
453 34e54ebc Iustin Pop
  def _AllLVs(self):
454 923b1523 Iustin Pop
    """Compute the list of all LVs.
455 923b1523 Iustin Pop

456 923b1523 Iustin Pop
    """
457 923b1523 Iustin Pop
    lvnames = set()
458 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
459 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
460 923b1523 Iustin Pop
      for lv_list in node_data.values():
461 923b1523 Iustin Pop
        lvnames.update(lv_list)
462 923b1523 Iustin Pop
    return lvnames
463 923b1523 Iustin Pop
464 b87a9c5f Christos Stavrakakis
  def _AllDisks(self):
465 79780863 Michele Tartara
    """Compute the list of all Disks (recursively, including children).
466 b87a9c5f Christos Stavrakakis

467 b87a9c5f Christos Stavrakakis
    """
468 79780863 Michele Tartara
    def DiskAndAllChildren(disk):
469 79780863 Michele Tartara
      """Returns a list containing the given disk and all of his children.
470 79780863 Michele Tartara

471 79780863 Michele Tartara
      """
472 79780863 Michele Tartara
      disks = [disk]
473 79780863 Michele Tartara
      if disk.children:
474 79780863 Michele Tartara
        for child_disk in disk.children:
475 79780863 Michele Tartara
          disks.extend(DiskAndAllChildren(child_disk))
476 79780863 Michele Tartara
      return disks
477 79780863 Michele Tartara
478 b87a9c5f Christos Stavrakakis
    disks = []
479 b87a9c5f Christos Stavrakakis
    for instance in self._config_data.instances.values():
480 79780863 Michele Tartara
      for disk in instance.disks:
481 79780863 Michele Tartara
        disks.extend(DiskAndAllChildren(disk))
482 b87a9c5f Christos Stavrakakis
    return disks
483 b87a9c5f Christos Stavrakakis
484 b87a9c5f Christos Stavrakakis
  def _AllNICs(self):
485 b87a9c5f Christos Stavrakakis
    """Compute the list of all NICs.
486 b87a9c5f Christos Stavrakakis

487 b87a9c5f Christos Stavrakakis
    """
488 b87a9c5f Christos Stavrakakis
    nics = []
489 b87a9c5f Christos Stavrakakis
    for instance in self._config_data.instances.values():
490 b87a9c5f Christos Stavrakakis
      nics.extend(instance.nics)
491 b87a9c5f Christos Stavrakakis
    return nics
492 b87a9c5f Christos Stavrakakis
493 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
494 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
495 34e54ebc Iustin Pop

496 34e54ebc Iustin Pop
    @type include_temporary: boolean
497 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
498 34e54ebc Iustin Pop
    @rtype: set
499 34e54ebc Iustin Pop
    @return: a set of IDs
500 34e54ebc Iustin Pop

501 34e54ebc Iustin Pop
    """
502 34e54ebc Iustin Pop
    existing = set()
503 34e54ebc Iustin Pop
    if include_temporary:
504 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
505 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
506 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
507 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
508 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
509 34e54ebc Iustin Pop
    return existing
510 34e54ebc Iustin Pop
511 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
512 430b923c Iustin Pop
    """Generate an unique UUID.
513 923b1523 Iustin Pop

514 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
515 923b1523 Iustin Pop
    duplicates.
516 923b1523 Iustin Pop

517 c41eea6e Iustin Pop
    @rtype: string
518 c41eea6e Iustin Pop
    @return: the unique id
519 923b1523 Iustin Pop

520 923b1523 Iustin Pop
    """
521 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
522 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
523 923b1523 Iustin Pop
524 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
525 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
526 430b923c Iustin Pop
    """Generate an unique ID.
527 430b923c Iustin Pop

528 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
529 430b923c Iustin Pop

530 4fae38c5 Guido Trotter
    @type ec_id: string
531 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
532 34d657ba Iustin Pop

533 34d657ba Iustin Pop
    """
534 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
535 34d657ba Iustin Pop
536 a8083063 Iustin Pop
  def _AllMACs(self):
537 a8083063 Iustin Pop
    """Return all MACs present in the config.
538 a8083063 Iustin Pop

539 c41eea6e Iustin Pop
    @rtype: list
540 c41eea6e Iustin Pop
    @return: the list of all MACs
541 c41eea6e Iustin Pop

542 a8083063 Iustin Pop
    """
543 a8083063 Iustin Pop
    result = []
544 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
545 a8083063 Iustin Pop
      for nic in instance.nics:
546 a8083063 Iustin Pop
        result.append(nic.mac)
547 a8083063 Iustin Pop
548 a8083063 Iustin Pop
    return result
549 a8083063 Iustin Pop
550 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
551 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
552 f9518d38 Iustin Pop

553 c41eea6e Iustin Pop
    @rtype: list
554 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
555 c41eea6e Iustin Pop

556 f9518d38 Iustin Pop
    """
557 f9518d38 Iustin Pop
    def helper(disk, result):
558 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
559 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
560 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
561 f9518d38 Iustin Pop
      if disk.children:
562 f9518d38 Iustin Pop
        for child in disk.children:
563 f9518d38 Iustin Pop
          helper(child, result)
564 f9518d38 Iustin Pop
565 f9518d38 Iustin Pop
    result = []
566 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
567 f9518d38 Iustin Pop
      for disk in instance.disks:
568 f9518d38 Iustin Pop
        helper(disk, result)
569 f9518d38 Iustin Pop
570 f9518d38 Iustin Pop
    return result
571 f9518d38 Iustin Pop
572 a57e502a Thomas Thrainer
  def _CheckDiskIDs(self, disk, l_ids):
573 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
574 4b98ac29 Iustin Pop

575 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
576 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
577 4b98ac29 Iustin Pop
    @type l_ids: list
578 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
579 4b98ac29 Iustin Pop
    @rtype: list
580 4b98ac29 Iustin Pop
    @return: a list of error messages
581 4b98ac29 Iustin Pop

582 4b98ac29 Iustin Pop
    """
583 4b98ac29 Iustin Pop
    result = []
584 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
585 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
586 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
587 25ae22e4 Iustin Pop
      else:
588 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
589 4b98ac29 Iustin Pop
590 4b98ac29 Iustin Pop
    if disk.children:
591 4b98ac29 Iustin Pop
      for child in disk.children:
592 a57e502a Thomas Thrainer
        result.extend(self._CheckDiskIDs(child, l_ids))
593 4b98ac29 Iustin Pop
    return result
594 4b98ac29 Iustin Pop
595 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
596 a8efbb40 Iustin Pop
    """Verify function.
597 a8efbb40 Iustin Pop

598 4a89c54a Iustin Pop
    @rtype: list
599 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
600 4a89c54a Iustin Pop
        configuration errors
601 4a89c54a Iustin Pop

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

899 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
900 4a89c54a Iustin Pop

901 4a89c54a Iustin Pop
    @rtype: list
902 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
903 4a89c54a Iustin Pop
        configuration errors
904 4a89c54a Iustin Pop

905 4a89c54a Iustin Pop
    """
906 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
907 4a89c54a Iustin Pop
908 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
909 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
910 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
911 b2fddf63 Iustin Pop

912 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
913 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
914 3b3b1bca Dimitris Aragiorgis
        configuration is stable
915 3b3b1bca Dimitris Aragiorgis

916 b2fddf63 Iustin Pop
    """
917 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
918 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
919 264bb3c5 Michael Hanselmann
920 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
921 264bb3c5 Michael Hanselmann
922 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
923 b2fddf63 Iustin Pop
  def GetPortList(self):
924 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
925 264bb3c5 Michael Hanselmann

926 264bb3c5 Michael Hanselmann
    """
927 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
928 264bb3c5 Michael Hanselmann
929 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
930 a8083063 Iustin Pop
  def AllocatePort(self):
931 a8083063 Iustin Pop
    """Allocate a port.
932 a8083063 Iustin Pop

933 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
934 b2fddf63 Iustin Pop
    default port range (and in this case we increase
935 b2fddf63 Iustin Pop
    highest_used_port).
936 a8083063 Iustin Pop

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

955 4a89c54a Iustin Pop
    @rtype: (dict, list)
956 da4a52a3 Thomas Thrainer
    @return: dictionary of node_uuid: dict of minor: instance_uuid;
957 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
958 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
959 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
960 4a89c54a Iustin Pop
        should raise an exception
961 a81c53c9 Iustin Pop

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

1000 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
1001 6d2e83d5 Iustin Pop

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

1006 6d2e83d5 Iustin Pop
    """
1007 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
1008 4a89c54a Iustin Pop
    if duplicates:
1009 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
1010 4a89c54a Iustin Pop
                                      str(duplicates))
1011 4a89c54a Iustin Pop
    return d_map
1012 6d2e83d5 Iustin Pop
1013 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
1014 da4a52a3 Thomas Thrainer
  def AllocateDRBDMinor(self, node_uuids, inst_uuid):
1015 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
1016 a81c53c9 Iustin Pop

1017 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
1018 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
1019 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
1020 a81c53c9 Iustin Pop
    order as the passed nodes.
1021 a81c53c9 Iustin Pop

1022 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1023 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which we allocate minors
1024 32388e6d Iustin Pop

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

1072 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1073 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1074 da4a52a3 Thomas Thrainer
                      released
1075 a81c53c9 Iustin Pop

1076 a81c53c9 Iustin Pop
    """
1077 da4a52a3 Thomas Thrainer
    assert isinstance(inst_uuid, basestring), \
1078 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
1079 da4a52a3 Thomas Thrainer
    for key, uuid in self._temporary_drbds.items():
1080 da4a52a3 Thomas Thrainer
      if uuid == inst_uuid:
1081 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
1082 a81c53c9 Iustin Pop
1083 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
1084 da4a52a3 Thomas Thrainer
  def ReleaseDRBDMinors(self, inst_uuid):
1085 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
1086 61cf6b5e Iustin Pop

1087 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
1088 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
1089 61cf6b5e Iustin Pop
    functions.
1090 61cf6b5e Iustin Pop

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

1093 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1094 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1095 da4a52a3 Thomas Thrainer
                      released
1096 61cf6b5e Iustin Pop

1097 61cf6b5e Iustin Pop
    """
1098 da4a52a3 Thomas Thrainer
    self._UnlockedReleaseDRBDMinors(inst_uuid)
1099 61cf6b5e Iustin Pop
1100 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1101 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
1102 4a8b186a Michael Hanselmann
    """Get the configuration version.
1103 4a8b186a Michael Hanselmann

1104 4a8b186a Michael Hanselmann
    @return: Config version
1105 4a8b186a Michael Hanselmann

1106 4a8b186a Michael Hanselmann
    """
1107 4a8b186a Michael Hanselmann
    return self._config_data.version
1108 4a8b186a Michael Hanselmann
1109 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1110 4a8b186a Michael Hanselmann
  def GetClusterName(self):
1111 4a8b186a Michael Hanselmann
    """Get cluster name.
1112 4a8b186a Michael Hanselmann

1113 4a8b186a Michael Hanselmann
    @return: Cluster name
1114 4a8b186a Michael Hanselmann

1115 4a8b186a Michael Hanselmann
    """
1116 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
1117 4a8b186a Michael Hanselmann
1118 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1119 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
1120 1c3231aa Thomas Thrainer
    """Get the UUID of the master node for this cluster.
1121 4a8b186a Michael Hanselmann

1122 1c3231aa Thomas Thrainer
    @return: Master node UUID
1123 4a8b186a Michael Hanselmann

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

1131 1c3231aa Thomas Thrainer
    @return: Master node hostname
1132 1c3231aa Thomas Thrainer

1133 1c3231aa Thomas Thrainer
    """
1134 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(self._config_data.cluster.master_node)
1135 1c3231aa Thomas Thrainer
1136 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1137 b730e2a7 Thomas Thrainer
  def GetMasterNodeInfo(self):
1138 b730e2a7 Thomas Thrainer
    """Get the master node information for this cluster.
1139 b730e2a7 Thomas Thrainer

1140 b730e2a7 Thomas Thrainer
    @rtype: objects.Node
1141 b730e2a7 Thomas Thrainer
    @return: Master node L{objects.Node} object
1142 b730e2a7 Thomas Thrainer

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

1150 4a8b186a Michael Hanselmann
    @return: Master IP
1151 4a8b186a Michael Hanselmann

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

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

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

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

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

1187 4b97f902 Apollon Oikonomopoulos
    """
1188 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1189 4b97f902 Apollon Oikonomopoulos
1190 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1191 d3e6fd0e Santi Raffa
  def GetGlusterStorageDir(self):
1192 d3e6fd0e Santi Raffa
    """Get the Gluster storage dir for this cluster.
1193 d3e6fd0e Santi Raffa

1194 d3e6fd0e Santi Raffa
    """
1195 d3e6fd0e Santi Raffa
    return self._config_data.cluster.gluster_storage_dir
1196 d3e6fd0e Santi Raffa
1197 d3e6fd0e Santi Raffa
  @locking.ssynchronized(_config_lock, shared=1)
1198 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1199 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1200 4a8b186a Michael Hanselmann

1201 4a8b186a Michael Hanselmann
    """
1202 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
1203 4a8b186a Michael Hanselmann
1204 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1205 a9542a4f Thomas Thrainer
  def GetRsaHostKey(self):
1206 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1207 a8083063 Iustin Pop

1208 c41eea6e Iustin Pop
    @rtype: string
1209 c41eea6e Iustin Pop
    @return: the rsa hostkey
1210 a8083063 Iustin Pop

1211 a8083063 Iustin Pop
    """
1212 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1213 a8083063 Iustin Pop
1214 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1215 a9542a4f Thomas Thrainer
  def GetDsaHostKey(self):
1216 a9542a4f Thomas Thrainer
    """Return the dsa hostkey from the config.
1217 a9542a4f Thomas Thrainer

1218 a9542a4f Thomas Thrainer
    @rtype: string
1219 a9542a4f Thomas Thrainer
    @return: the dsa hostkey
1220 a9542a4f Thomas Thrainer

1221 a9542a4f Thomas Thrainer
    """
1222 a9542a4f Thomas Thrainer
    return self._config_data.cluster.dsahostkeypub
1223 a9542a4f Thomas Thrainer
1224 a9542a4f Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1225 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1226 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1227 bf4af505 Apollon Oikonomopoulos

1228 bf4af505 Apollon Oikonomopoulos
    """
1229 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1230 bf4af505 Apollon Oikonomopoulos
1231 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1232 0359e5d0 Spyros Trigazis
  def GetDefaultIAllocatorParameters(self):
1233 0359e5d0 Spyros Trigazis
    """Get the default instance allocator parameters for this cluster.
1234 0359e5d0 Spyros Trigazis

1235 0359e5d0 Spyros Trigazis
    @rtype: dict
1236 0359e5d0 Spyros Trigazis
    @return: dict of iallocator parameters
1237 0359e5d0 Spyros Trigazis

1238 0359e5d0 Spyros Trigazis
    """
1239 0359e5d0 Spyros Trigazis
    return self._config_data.cluster.default_iallocator_params
1240 0359e5d0 Spyros Trigazis
1241 0359e5d0 Spyros Trigazis
  @locking.ssynchronized(_config_lock, shared=1)
1242 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1243 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1244 868a98ca Manuel Franceschini

1245 868a98ca Manuel Franceschini
    @return: primary ip family
1246 868a98ca Manuel Franceschini

1247 868a98ca Manuel Franceschini
    """
1248 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1249 868a98ca Manuel Franceschini
1250 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1251 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1252 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1253 c9f4b8e6 Andrea Spadaccini

1254 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1255 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1256 c9f4b8e6 Andrea Spadaccini

1257 c9f4b8e6 Andrea Spadaccini
    """
1258 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1259 5ae4945a Iustin Pop
    result = objects.MasterNetworkParameters(
1260 1c3231aa Thomas Thrainer
      uuid=cluster.master_node, ip=cluster.master_ip,
1261 5ae4945a Iustin Pop
      netmask=cluster.master_netmask, netdev=cluster.master_netdev,
1262 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1263 c9f4b8e6 Andrea Spadaccini
1264 f9d20654 Andrea Spadaccini
    return result
1265 f9d20654 Andrea Spadaccini
1266 9a94cee3 Jose A. Lopes
  @locking.ssynchronized(_config_lock, shared=1)
1267 9a94cee3 Jose A. Lopes
  def GetInstanceCommunicationNetwork(self):
1268 9a94cee3 Jose A. Lopes
    """Get cluster instance communication network
1269 9a94cee3 Jose A. Lopes

1270 9a94cee3 Jose A. Lopes
    @rtype: string
1271 9a94cee3 Jose A. Lopes
    @return: instance communication network, which is the name of the
1272 9a94cee3 Jose A. Lopes
             network used for instance communication
1273 9a94cee3 Jose A. Lopes

1274 9a94cee3 Jose A. Lopes
    """
1275 9a94cee3 Jose A. Lopes
    return self._config_data.cluster.instance_communication_network
1276 9a94cee3 Jose A. Lopes
1277 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1278 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1279 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1280 e11a1b77 Adeodato Simo

1281 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1282 90e99856 Adeodato Simo
    according to their default values.
1283 90e99856 Adeodato Simo

1284 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1285 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1286 e11a1b77 Adeodato Simo
    @type ec_id: string
1287 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1288 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1289 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1290 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1291 e11a1b77 Adeodato Simo
                       configuration already
1292 e11a1b77 Adeodato Simo

1293 e11a1b77 Adeodato Simo
    """
1294 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1295 e11a1b77 Adeodato Simo
    self._WriteConfig()
1296 e11a1b77 Adeodato Simo
1297 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1298 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1299 e11a1b77 Adeodato Simo

1300 e11a1b77 Adeodato Simo
    """
1301 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
1302 e11a1b77 Adeodato Simo
1303 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
1304 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
1305 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
1306 e11a1b77 Adeodato Simo
    if check_uuid:
1307 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
1308 e11a1b77 Adeodato Simo
1309 18ffc0fe Stephen Shirley
    try:
1310 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
1311 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
1312 18ffc0fe Stephen Shirley
      pass
1313 18ffc0fe Stephen Shirley
    else:
1314 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
1315 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
1316 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
1317 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
1318 18ffc0fe Stephen Shirley
1319 e11a1b77 Adeodato Simo
    group.serial_no = 1
1320 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
1321 90e99856 Adeodato Simo
    group.UpgradeConfig()
1322 e11a1b77 Adeodato Simo
1323 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
1324 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1325 e11a1b77 Adeodato Simo
1326 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1327 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
1328 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
1329 e11a1b77 Adeodato Simo

1330 e11a1b77 Adeodato Simo
    @type group_uuid: string
1331 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1332 e11a1b77 Adeodato Simo

1333 e11a1b77 Adeodato Simo
    """
1334 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1335 e11a1b77 Adeodato Simo
1336 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1337 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1338 e11a1b77 Adeodato Simo
1339 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1340 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1341 0389c42a Stephen Shirley
1342 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1343 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1344 e11a1b77 Adeodato Simo
    self._WriteConfig()
1345 e11a1b77 Adeodato Simo
1346 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1347 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1348 eaa98a04 Guido Trotter

1349 eaa98a04 Guido Trotter
    @type target: string or None
1350 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1351 eaa98a04 Guido Trotter
    @rtype: string
1352 412b3531 Guido Trotter
    @return: nodegroup UUID
1353 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1354 eaa98a04 Guido Trotter

1355 eaa98a04 Guido Trotter
    """
1356 eaa98a04 Guido Trotter
    if target is None:
1357 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1358 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1359 2ed0e208 Iustin Pop
                                   " group must be specified explicitly.")
1360 eaa98a04 Guido Trotter
      else:
1361 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1362 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1363 eaa98a04 Guido Trotter
      return target
1364 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1365 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1366 eaa98a04 Guido Trotter
        return nodegroup.uuid
1367 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1368 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1369 eaa98a04 Guido Trotter
1370 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1371 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1372 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1373 e85d8982 Stephen Shirley

1374 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1375 e85d8982 Stephen Shirley

1376 e85d8982 Stephen Shirley
    @type target: string or None
1377 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1378 e85d8982 Stephen Shirley
    @rtype: string
1379 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1380 e85d8982 Stephen Shirley

1381 e85d8982 Stephen Shirley
    """
1382 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1383 e85d8982 Stephen Shirley
1384 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1385 648e4196 Guido Trotter
    """Lookup a node group.
1386 648e4196 Guido Trotter

1387 648e4196 Guido Trotter
    @type uuid: string
1388 648e4196 Guido Trotter
    @param uuid: group UUID
1389 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1390 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1391 648e4196 Guido Trotter

1392 648e4196 Guido Trotter
    """
1393 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1394 648e4196 Guido Trotter
      return None
1395 648e4196 Guido Trotter
1396 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1397 648e4196 Guido Trotter
1398 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1399 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1400 5768e6a6 René Nussbaumer
    """Lookup a node group.
1401 5768e6a6 René Nussbaumer

1402 5768e6a6 René Nussbaumer
    @type uuid: string
1403 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1404 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1405 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1406 5768e6a6 René Nussbaumer

1407 5768e6a6 René Nussbaumer
    """
1408 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1409 5768e6a6 René Nussbaumer
1410 6b2a2942 Petr Pudlak
  def _UnlockedGetAllNodeGroupsInfo(self):
1411 6b2a2942 Petr Pudlak
    """Get the configuration of all node groups.
1412 6b2a2942 Petr Pudlak

1413 6b2a2942 Petr Pudlak
    """
1414 6b2a2942 Petr Pudlak
    return dict(self._config_data.nodegroups)
1415 6b2a2942 Petr Pudlak
1416 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1417 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1418 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1419 622444e5 Iustin Pop

1420 622444e5 Iustin Pop
    """
1421 6b2a2942 Petr Pudlak
    return self._UnlockedGetAllNodeGroupsInfo()
1422 622444e5 Iustin Pop
1423 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1424 a9f33339 Petr Pudlak
  def GetAllNodeGroupsInfoDict(self):
1425 a9f33339 Petr Pudlak
    """Get the configuration of all node groups expressed as a dictionary of
1426 a9f33339 Petr Pudlak
    dictionaries.
1427 a9f33339 Petr Pudlak

1428 a9f33339 Petr Pudlak
    """
1429 a9f33339 Petr Pudlak
    return dict(map(lambda (uuid, ng): (uuid, ng.ToDict()),
1430 a9f33339 Petr Pudlak
                    self._UnlockedGetAllNodeGroupsInfo().items()))
1431 a9f33339 Petr Pudlak
1432 a9f33339 Petr Pudlak
  @locking.ssynchronized(_config_lock, shared=1)
1433 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1434 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1435 1ac6f2ad Guido Trotter

1436 1ac6f2ad Guido Trotter
    """
1437 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1438 1ac6f2ad Guido Trotter
1439 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1440 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1441 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1442 dac81741 Michael Hanselmann

1443 dac81741 Michael Hanselmann
    """
1444 1c3231aa Thomas Thrainer
    ngfn = lambda node_uuid: self._UnlockedGetNodeInfo(node_uuid).group
1445 1c3231aa Thomas Thrainer
    return frozenset(member_uuid
1446 1c3231aa Thomas Thrainer
                     for node_uuid in nodes
1447 1c3231aa Thomas Thrainer
                     for member_uuid in
1448 1c3231aa Thomas Thrainer
                       self._UnlockedGetNodeGroup(ngfn(node_uuid)).members)
1449 dac81741 Michael Hanselmann
1450 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1451 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1452 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1453 080fbeea Michael Hanselmann

1454 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1455 080fbeea Michael Hanselmann
    @rtype: list
1456 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1457 080fbeea Michael Hanselmann

1458 080fbeea Michael Hanselmann
    """
1459 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1460 080fbeea Michael Hanselmann
1461 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1462 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1463 a8083063 Iustin Pop
    """Add an instance to the config.
1464 a8083063 Iustin Pop

1465 a8083063 Iustin Pop
    This should be used after creating a new instance.
1466 a8083063 Iustin Pop

1467 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1468 c41eea6e Iustin Pop
    @param instance: the instance object
1469 c41eea6e Iustin Pop

1470 a8083063 Iustin Pop
    """
1471 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1472 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1473 a8083063 Iustin Pop
1474 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1475 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1476 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1477 923b1523 Iustin Pop
1478 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1479 e4640214 Guido Trotter
    for nic in instance.nics:
1480 e4640214 Guido Trotter
      if nic.mac in all_macs:
1481 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1482 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1483 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1484 430b923c Iustin Pop
1485 da4a52a3 Thomas Thrainer
    self._CheckUniqueUUID(instance, include_temporary=False)
1486 e4640214 Guido Trotter
1487 b989e85d Iustin Pop
    instance.serial_no = 1
1488 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1489 da4a52a3 Thomas Thrainer
    self._config_data.instances[instance.uuid] = instance
1490 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1491 da4a52a3 Thomas Thrainer
    self._UnlockedReleaseDRBDMinors(instance.uuid)
1492 e8e079f3 Dimitris Aragiorgis
    self._UnlockedCommitTemporaryIps(ec_id)
1493 a8083063 Iustin Pop
    self._WriteConfig()
1494 a8083063 Iustin Pop
1495 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1496 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1497 430b923c Iustin Pop

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

1501 430b923c Iustin Pop
    """
1502 430b923c Iustin Pop
    if not item.uuid:
1503 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1504 da4a52a3 Thomas Thrainer
    else:
1505 da4a52a3 Thomas Thrainer
      self._CheckUniqueUUID(item, include_temporary=True)
1506 da4a52a3 Thomas Thrainer
1507 da4a52a3 Thomas Thrainer
  def _CheckUniqueUUID(self, item, include_temporary):
1508 da4a52a3 Thomas Thrainer
    """Checks that the UUID of the given object is unique.
1509 da4a52a3 Thomas Thrainer

1510 da4a52a3 Thomas Thrainer
    @param item: the instance or node to be checked
1511 da4a52a3 Thomas Thrainer
    @param include_temporary: whether temporarily generated UUID's should be
1512 da4a52a3 Thomas Thrainer
              included in the check. If the UUID of the item to be checked is
1513 da4a52a3 Thomas Thrainer
              a temporarily generated one, this has to be C{False}.
1514 da4a52a3 Thomas Thrainer

1515 da4a52a3 Thomas Thrainer
    """
1516 da4a52a3 Thomas Thrainer
    if not item.uuid:
1517 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("'%s' must have an UUID" % (item.name,))
1518 da4a52a3 Thomas Thrainer
    if item.uuid in self._AllIDs(include_temporary=include_temporary):
1519 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1520 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1521 430b923c Iustin Pop
1522 da4a52a3 Thomas Thrainer
  def _SetInstanceStatus(self, inst_uuid, status, disks_active):
1523 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1524 a8083063 Iustin Pop

1525 a8083063 Iustin Pop
    """
1526 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1527 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1528 da4a52a3 Thomas Thrainer
                                      inst_uuid)
1529 da4a52a3 Thomas Thrainer
    instance = self._config_data.instances[inst_uuid]
1530 1d4a4b26 Thomas Thrainer
1531 1d4a4b26 Thomas Thrainer
    if status is None:
1532 1d4a4b26 Thomas Thrainer
      status = instance.admin_state
1533 1d4a4b26 Thomas Thrainer
    if disks_active is None:
1534 1d4a4b26 Thomas Thrainer
      disks_active = instance.disks_active
1535 1d4a4b26 Thomas Thrainer
1536 1d4a4b26 Thomas Thrainer
    assert status in constants.ADMINST_ALL, \
1537 1d4a4b26 Thomas Thrainer
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1538 1d4a4b26 Thomas Thrainer
1539 1d4a4b26 Thomas Thrainer
    if instance.admin_state != status or \
1540 1d4a4b26 Thomas Thrainer
       instance.disks_active != disks_active:
1541 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1542 1d4a4b26 Thomas Thrainer
      instance.disks_active = disks_active
1543 b989e85d Iustin Pop
      instance.serial_no += 1
1544 d693c864 Iustin Pop
      instance.mtime = time.time()
1545 455a3445 Iustin Pop
      self._WriteConfig()
1546 a8083063 Iustin Pop
1547 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1548 da4a52a3 Thomas Thrainer
  def MarkInstanceUp(self, inst_uuid):
1549 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1550 6a408fb2 Iustin Pop

1551 1d4a4b26 Thomas Thrainer
    This also sets the instance disks active flag.
1552 1d4a4b26 Thomas Thrainer

1553 6a408fb2 Iustin Pop
    """
1554 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_UP, True)
1555 57de31c0 Agata Murawska
1556 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1557 da4a52a3 Thomas Thrainer
  def MarkInstanceOffline(self, inst_uuid):
1558 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1559 57de31c0 Agata Murawska

1560 1d4a4b26 Thomas Thrainer
    This also clears the instance disks active flag.
1561 1d4a4b26 Thomas Thrainer

1562 57de31c0 Agata Murawska
    """
1563 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_OFFLINE, False)
1564 6a408fb2 Iustin Pop
1565 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1566 da4a52a3 Thomas Thrainer
  def RemoveInstance(self, inst_uuid):
1567 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1568 a8083063 Iustin Pop

1569 a8083063 Iustin Pop
    """
1570 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1571 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1572 f396ad8c Vangelis Koukis
1573 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1574 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1575 da4a52a3 Thomas Thrainer
    inst = self._config_data.instances[inst_uuid]
1576 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1577 f396ad8c Vangelis Koukis
    if network_port is not None:
1578 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1579 f396ad8c Vangelis Koukis
1580 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1581 ced51149 Dimitris Aragiorgis
1582 ced51149 Dimitris Aragiorgis
    for nic in instance.nics:
1583 9394f4d1 Dimitris Aragiorgis
      if nic.network and nic.ip:
1584 1b68f268 Helga Velroyen
        # Return all IP addresses to the respective address pools
1585 9394f4d1 Dimitris Aragiorgis
        self._UnlockedCommitIp(constants.RELEASE_ACTION, nic.network, nic.ip)
1586 ced51149 Dimitris Aragiorgis
1587 da4a52a3 Thomas Thrainer
    del self._config_data.instances[inst_uuid]
1588 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1589 a8083063 Iustin Pop
    self._WriteConfig()
1590 a8083063 Iustin Pop
1591 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1592 da4a52a3 Thomas Thrainer
  def RenameInstance(self, inst_uuid, new_name):
1593 fc95f88f Iustin Pop
    """Rename an instance.
1594 fc95f88f Iustin Pop

1595 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1596 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1597 fc95f88f Iustin Pop
    rename.
1598 fc95f88f Iustin Pop

1599 fc95f88f Iustin Pop
    """
1600 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1601 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1602 ea642319 Michael Hanselmann
1603 da4a52a3 Thomas Thrainer
    inst = self._config_data.instances[inst_uuid]
1604 fc95f88f Iustin Pop
    inst.name = new_name
1605 b23c4333 Manuel Franceschini
1606 9e14897d Klaus Aehlig
    for (_, disk) in enumerate(inst.disks):
1607 cd3b4ff4 Helga Velroyen
      if disk.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1608 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1609 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1610 ea642319 Michael Hanselmann
        disk.logical_id = (disk.logical_id[0],
1611 ea642319 Michael Hanselmann
                           utils.PathJoin(file_storage_dir, inst.name,
1612 9e14897d Klaus Aehlig
                                          os.path.basename(disk.logical_id[1])))
1613 ea642319 Michael Hanselmann
1614 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1615 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1616 1fc34c48 Michael Hanselmann
1617 fc95f88f Iustin Pop
    self._WriteConfig()
1618 fc95f88f Iustin Pop
1619 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1620 da4a52a3 Thomas Thrainer
  def MarkInstanceDown(self, inst_uuid):
1621 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1622 a8083063 Iustin Pop

1623 1d4a4b26 Thomas Thrainer
    This does not touch the instance disks active flag, as shut down instances
1624 1d4a4b26 Thomas Thrainer
    can still have active disks.
1625 1d4a4b26 Thomas Thrainer

1626 1d4a4b26 Thomas Thrainer
    """
1627 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None)
1628 1d4a4b26 Thomas Thrainer
1629 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1630 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksActive(self, inst_uuid):
1631 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks active.
1632 1d4a4b26 Thomas Thrainer

1633 1d4a4b26 Thomas Thrainer
    """
1634 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, True)
1635 1d4a4b26 Thomas Thrainer
1636 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1637 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksInactive(self, inst_uuid):
1638 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks inactive.
1639 1d4a4b26 Thomas Thrainer

1640 a8083063 Iustin Pop
    """
1641 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, False)
1642 a8083063 Iustin Pop
1643 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1644 94bbfece Iustin Pop
    """Get the list of instances.
1645 94bbfece Iustin Pop

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

1648 94bbfece Iustin Pop
    """
1649 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1650 94bbfece Iustin Pop
1651 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1652 a8083063 Iustin Pop
  def GetInstanceList(self):
1653 a8083063 Iustin Pop
    """Get the list of instances.
1654 a8083063 Iustin Pop

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

1657 a8083063 Iustin Pop
    """
1658 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1659 a8083063 Iustin Pop
1660 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1661 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1662 a8083063 Iustin Pop

1663 a8083063 Iustin Pop
    """
1664 da4a52a3 Thomas Thrainer
    # Locking is done in L{ConfigWriter.GetAllInstancesInfo}
1665 da4a52a3 Thomas Thrainer
    all_insts = self.GetAllInstancesInfo().values()
1666 da4a52a3 Thomas Thrainer
    expanded_name = _MatchNameComponentIgnoreCase(
1667 da4a52a3 Thomas Thrainer
                      short_name, [inst.name for inst in all_insts])
1668 da4a52a3 Thomas Thrainer
1669 da4a52a3 Thomas Thrainer
    if expanded_name is not None:
1670 da4a52a3 Thomas Thrainer
      # there has to be exactly one instance with that name
1671 da4a52a3 Thomas Thrainer
      inst = (filter(lambda n: n.name == expanded_name, all_insts)[0])
1672 da4a52a3 Thomas Thrainer
      return (inst.uuid, inst.name)
1673 da4a52a3 Thomas Thrainer
    else:
1674 738436bf Thomas Thrainer
      return (None, None)
1675 a8083063 Iustin Pop
1676 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceInfo(self, inst_uuid):
1677 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1678 94bbfece Iustin Pop

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

1681 94bbfece Iustin Pop
    """
1682 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1683 94bbfece Iustin Pop
      return None
1684 94bbfece Iustin Pop
1685 da4a52a3 Thomas Thrainer
    return self._config_data.instances[inst_uuid]
1686 94bbfece Iustin Pop
1687 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1688 da4a52a3 Thomas Thrainer
  def GetInstanceInfo(self, inst_uuid):
1689 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1690 a8083063 Iustin Pop

1691 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1692 a8083063 Iustin Pop
    an instance are taken from the live systems.
1693 a8083063 Iustin Pop

1694 da4a52a3 Thomas Thrainer
    @param inst_uuid: UUID of the instance
1695 a8083063 Iustin Pop

1696 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1697 c41eea6e Iustin Pop
    @return: the instance object
1698 a8083063 Iustin Pop

1699 a8083063 Iustin Pop
    """
1700 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfo(inst_uuid)
1701 a8083063 Iustin Pop
1702 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1703 da4a52a3 Thomas Thrainer
  def GetInstanceNodeGroups(self, inst_uuid, primary_only=False):
1704 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1705 2674690b Michael Hanselmann

1706 2674690b Michael Hanselmann
    @rtype: frozenset
1707 2674690b Michael Hanselmann

1708 2674690b Michael Hanselmann
    """
1709 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1710 2674690b Michael Hanselmann
    if not instance:
1711 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1712 2674690b Michael Hanselmann
1713 2674690b Michael Hanselmann
    if primary_only:
1714 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1715 2674690b Michael Hanselmann
    else:
1716 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1717 2674690b Michael Hanselmann
1718 1c3231aa Thomas Thrainer
    return frozenset(self._UnlockedGetNodeInfo(node_uuid).group
1719 1c3231aa Thomas Thrainer
                     for node_uuid in nodes)
1720 2674690b Michael Hanselmann
1721 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1722 da4a52a3 Thomas Thrainer
  def GetInstanceNetworks(self, inst_uuid):
1723 922610c9 Dimitris Aragiorgis
    """Returns set of network UUIDs for instance's nics.
1724 922610c9 Dimitris Aragiorgis

1725 922610c9 Dimitris Aragiorgis
    @rtype: frozenset
1726 922610c9 Dimitris Aragiorgis

1727 922610c9 Dimitris Aragiorgis
    """
1728 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1729 922610c9 Dimitris Aragiorgis
    if not instance:
1730 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1731 922610c9 Dimitris Aragiorgis
1732 922610c9 Dimitris Aragiorgis
    networks = set()
1733 922610c9 Dimitris Aragiorgis
    for nic in instance.nics:
1734 922610c9 Dimitris Aragiorgis
      if nic.network:
1735 922610c9 Dimitris Aragiorgis
        networks.add(nic.network)
1736 922610c9 Dimitris Aragiorgis
1737 922610c9 Dimitris Aragiorgis
    return frozenset(networks)
1738 922610c9 Dimitris Aragiorgis
1739 922610c9 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
1740 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfo(self, inst_uuids):
1741 da4a52a3 Thomas Thrainer
    """Get the configuration of multiple instances.
1742 da4a52a3 Thomas Thrainer

1743 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs
1744 da4a52a3 Thomas Thrainer
    @rtype: list
1745 da4a52a3 Thomas Thrainer
    @return: list of tuples (instance UUID, instance_info), where
1746 da4a52a3 Thomas Thrainer
        instance_info is what would GetInstanceInfo return for the
1747 da4a52a3 Thomas Thrainer
        node, while keeping the original order
1748 da4a52a3 Thomas Thrainer

1749 da4a52a3 Thomas Thrainer
    """
1750 da4a52a3 Thomas Thrainer
    return [(uuid, self._UnlockedGetInstanceInfo(uuid)) for uuid in inst_uuids]
1751 da4a52a3 Thomas Thrainer
1752 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1753 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfoByName(self, inst_names):
1754 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1755 71333cb9 Iustin Pop

1756 da4a52a3 Thomas Thrainer
    @param inst_names: list of instance names
1757 71333cb9 Iustin Pop
    @rtype: list
1758 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1759 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1760 71333cb9 Iustin Pop
        node, while keeping the original order
1761 71333cb9 Iustin Pop

1762 71333cb9 Iustin Pop
    """
1763 da4a52a3 Thomas Thrainer
    result = []
1764 da4a52a3 Thomas Thrainer
    for name in inst_names:
1765 da4a52a3 Thomas Thrainer
      instance = self._UnlockedGetInstanceInfoByName(name)
1766 da4a52a3 Thomas Thrainer
      result.append((instance.uuid, instance))
1767 da4a52a3 Thomas Thrainer
    return result
1768 71333cb9 Iustin Pop
1769 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1770 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1771 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1772 0b2de758 Iustin Pop

1773 0b2de758 Iustin Pop
    @rtype: dict
1774 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1775 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1776 0b2de758 Iustin Pop

1777 0b2de758 Iustin Pop
    """
1778 da4a52a3 Thomas Thrainer
    return self._UnlockedGetAllInstancesInfo()
1779 da4a52a3 Thomas Thrainer
1780 da4a52a3 Thomas Thrainer
  def _UnlockedGetAllInstancesInfo(self):
1781 da4a52a3 Thomas Thrainer
    my_dict = dict([(inst_uuid, self._UnlockedGetInstanceInfo(inst_uuid))
1782 da4a52a3 Thomas Thrainer
                    for inst_uuid in self._UnlockedGetInstanceList()])
1783 0b2de758 Iustin Pop
    return my_dict
1784 0b2de758 Iustin Pop
1785 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1786 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1787 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1788 cc19798f Michael Hanselmann

1789 cc19798f Michael Hanselmann
    @type filter_fn: callable
1790 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1791 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1792 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1793 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1794 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1795 cc19798f Michael Hanselmann

1796 cc19798f Michael Hanselmann
    """
1797 da4a52a3 Thomas Thrainer
    return dict((uuid, inst)
1798 da4a52a3 Thomas Thrainer
                for (uuid, inst) in self._config_data.instances.items()
1799 cc19798f Michael Hanselmann
                if filter_fn(inst))
1800 cc19798f Michael Hanselmann
1801 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1802 da4a52a3 Thomas Thrainer
  def GetInstanceInfoByName(self, inst_name):
1803 da4a52a3 Thomas Thrainer
    """Get the L{objects.Instance} object for a named instance.
1804 da4a52a3 Thomas Thrainer

1805 da4a52a3 Thomas Thrainer
    @param inst_name: name of the instance to get information for
1806 da4a52a3 Thomas Thrainer
    @type inst_name: string
1807 da4a52a3 Thomas Thrainer
    @return: the corresponding L{objects.Instance} instance or None if no
1808 da4a52a3 Thomas Thrainer
          information is available
1809 da4a52a3 Thomas Thrainer

1810 da4a52a3 Thomas Thrainer
    """
1811 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfoByName(inst_name)
1812 da4a52a3 Thomas Thrainer
1813 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceInfoByName(self, inst_name):
1814 da4a52a3 Thomas Thrainer
    for inst in self._UnlockedGetAllInstancesInfo().values():
1815 da4a52a3 Thomas Thrainer
      if inst.name == inst_name:
1816 da4a52a3 Thomas Thrainer
        return inst
1817 da4a52a3 Thomas Thrainer
    return None
1818 da4a52a3 Thomas Thrainer
1819 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceName(self, inst_uuid):
1820 da4a52a3 Thomas Thrainer
    inst_info = self._UnlockedGetInstanceInfo(inst_uuid)
1821 da4a52a3 Thomas Thrainer
    if inst_info is None:
1822 da4a52a3 Thomas Thrainer
      raise errors.OpExecError("Unknown instance: %s" % inst_uuid)
1823 da4a52a3 Thomas Thrainer
    return inst_info.name
1824 da4a52a3 Thomas Thrainer
1825 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1826 da4a52a3 Thomas Thrainer
  def GetInstanceName(self, inst_uuid):
1827 da4a52a3 Thomas Thrainer
    """Gets the instance name for the passed instance.
1828 da4a52a3 Thomas Thrainer

1829 da4a52a3 Thomas Thrainer
    @param inst_uuid: instance UUID to get name for
1830 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1831 da4a52a3 Thomas Thrainer
    @rtype: string
1832 da4a52a3 Thomas Thrainer
    @return: instance name
1833 da4a52a3 Thomas Thrainer

1834 da4a52a3 Thomas Thrainer
    """
1835 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceName(inst_uuid)
1836 da4a52a3 Thomas Thrainer
1837 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1838 da4a52a3 Thomas Thrainer
  def GetInstanceNames(self, inst_uuids):
1839 da4a52a3 Thomas Thrainer
    """Gets the instance names for the passed list of nodes.
1840 da4a52a3 Thomas Thrainer

1841 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs to get names for
1842 da4a52a3 Thomas Thrainer
    @type inst_uuids: list of strings
1843 da4a52a3 Thomas Thrainer
    @rtype: list of strings
1844 da4a52a3 Thomas Thrainer
    @return: list of instance names
1845 da4a52a3 Thomas Thrainer

1846 da4a52a3 Thomas Thrainer
    """
1847 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceNames(inst_uuids)
1848 da4a52a3 Thomas Thrainer
1849 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceNames(self, inst_uuids):
1850 da4a52a3 Thomas Thrainer
    return [self._UnlockedGetInstanceName(uuid) for uuid in inst_uuids]
1851 da4a52a3 Thomas Thrainer
1852 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1853 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1854 a8083063 Iustin Pop
    """Add a node to the configuration.
1855 a8083063 Iustin Pop

1856 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1857 c41eea6e Iustin Pop
    @param node: a Node instance
1858 a8083063 Iustin Pop

1859 a8083063 Iustin Pop
    """
1860 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1861 d8470559 Michael Hanselmann
1862 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1863 430b923c Iustin Pop
1864 b989e85d Iustin Pop
    node.serial_no = 1
1865 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1866 1c3231aa Thomas Thrainer
    self._UnlockedAddNodeToGroup(node.uuid, node.group)
1867 1c3231aa Thomas Thrainer
    self._config_data.nodes[node.uuid] = node
1868 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1869 a8083063 Iustin Pop
    self._WriteConfig()
1870 a8083063 Iustin Pop
1871 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1872 1c3231aa Thomas Thrainer
  def RemoveNode(self, node_uuid):
1873 a8083063 Iustin Pop
    """Remove a node from the configuration.
1874 a8083063 Iustin Pop

1875 a8083063 Iustin Pop
    """
1876 1c3231aa Thomas Thrainer
    logging.info("Removing node %s from configuration", node_uuid)
1877 d8470559 Michael Hanselmann
1878 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
1879 1c3231aa Thomas Thrainer
      raise errors.ConfigurationError("Unknown node '%s'" % node_uuid)
1880 a8083063 Iustin Pop
1881 1c3231aa Thomas Thrainer
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_uuid])
1882 1c3231aa Thomas Thrainer
    del self._config_data.nodes[node_uuid]
1883 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1884 a8083063 Iustin Pop
    self._WriteConfig()
1885 a8083063 Iustin Pop
1886 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1887 1c3231aa Thomas Thrainer
    """Attempt to expand an incomplete node name into a node UUID.
1888 a8083063 Iustin Pop

1889 a8083063 Iustin Pop
    """
1890 1c3231aa Thomas Thrainer
    # Locking is done in L{ConfigWriter.GetAllNodesInfo}
1891 1c3231aa Thomas Thrainer
    all_nodes = self.GetAllNodesInfo().values()
1892 1c3231aa Thomas Thrainer
    expanded_name = _MatchNameComponentIgnoreCase(
1893 1c3231aa Thomas Thrainer
                      short_name, [node.name for node in all_nodes])
1894 a8083063 Iustin Pop
1895 1c3231aa Thomas Thrainer
    if expanded_name is not None:
1896 da4a52a3 Thomas Thrainer
      # there has to be exactly one node with that name
1897 1c3231aa Thomas Thrainer
      node = (filter(lambda n: n.name == expanded_name, all_nodes)[0])
1898 1c3231aa Thomas Thrainer
      return (node.uuid, node.name)
1899 1c3231aa Thomas Thrainer
    else:
1900 738436bf Thomas Thrainer
      return (None, None)
1901 1c3231aa Thomas Thrainer
1902 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfo(self, node_uuid):
1903 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1904 a8083063 Iustin Pop

1905 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1906 c41eea6e Iustin Pop
    held.
1907 f78ede4e Guido Trotter

1908 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1909 a8083063 Iustin Pop

1910 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1911 c41eea6e Iustin Pop
    @return: the node object
1912 a8083063 Iustin Pop

1913 a8083063 Iustin Pop
    """
1914 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
1915 a8083063 Iustin Pop
      return None
1916 a8083063 Iustin Pop
1917 1c3231aa Thomas Thrainer
    return self._config_data.nodes[node_uuid]
1918 a8083063 Iustin Pop
1919 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1920 1c3231aa Thomas Thrainer
  def GetNodeInfo(self, node_uuid):
1921 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1922 f78ede4e Guido Trotter

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

1925 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1926 c41eea6e Iustin Pop

1927 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1928 c41eea6e Iustin Pop
    @return: the node object
1929 f78ede4e Guido Trotter

1930 f78ede4e Guido Trotter
    """
1931 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfo(node_uuid)
1932 f78ede4e Guido Trotter
1933 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1934 1c3231aa Thomas Thrainer
  def GetNodeInstances(self, node_uuid):
1935 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1936 8bf9e9a5 Iustin Pop

1937 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1938 8bf9e9a5 Iustin Pop

1939 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1940 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1941 8bf9e9a5 Iustin Pop

1942 8bf9e9a5 Iustin Pop
    """
1943 8bf9e9a5 Iustin Pop
    pri = []
1944 8bf9e9a5 Iustin Pop
    sec = []
1945 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1946 1c3231aa Thomas Thrainer
      if inst.primary_node == node_uuid:
1947 da4a52a3 Thomas Thrainer
        pri.append(inst.uuid)
1948 1c3231aa Thomas Thrainer
      if node_uuid in inst.secondary_nodes:
1949 da4a52a3 Thomas Thrainer
        sec.append(inst.uuid)
1950 8bf9e9a5 Iustin Pop
    return (pri, sec)
1951 8bf9e9a5 Iustin Pop
1952 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1953 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1954 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1955 c71b049c Michael Hanselmann

1956 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1957 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1958 c71b049c Michael Hanselmann
    @rtype: frozenset
1959 da4a52a3 Thomas Thrainer
    @return: List of instance UUIDs in node group
1960 c71b049c Michael Hanselmann

1961 c71b049c Michael Hanselmann
    """
1962 c71b049c Michael Hanselmann
    if primary_only:
1963 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1964 c71b049c Michael Hanselmann
    else:
1965 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1966 c71b049c Michael Hanselmann
1967 da4a52a3 Thomas Thrainer
    return frozenset(inst.uuid
1968 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1969 1c3231aa Thomas Thrainer
                     for node_uuid in nodes_fn(inst)
1970 1c3231aa Thomas Thrainer
                     if self._UnlockedGetNodeInfo(node_uuid).group == uuid)
1971 c71b049c Michael Hanselmann
1972 def6577f Helga Velroyen
  def _UnlockedGetHvparamsString(self, hvname):
1973 def6577f Helga Velroyen
    """Return the string representation of the list of hyervisor parameters of
1974 def6577f Helga Velroyen
    the given hypervisor.
1975 def6577f Helga Velroyen

1976 def6577f Helga Velroyen
    @see: C{GetHvparams}
1977 def6577f Helga Velroyen

1978 def6577f Helga Velroyen
    """
1979 def6577f Helga Velroyen
    result = ""
1980 def6577f Helga Velroyen
    hvparams = self._config_data.cluster.hvparams[hvname]
1981 def6577f Helga Velroyen
    for key in hvparams:
1982 def6577f Helga Velroyen
      result += "%s=%s\n" % (key, hvparams[key])
1983 def6577f Helga Velroyen
    return result
1984 def6577f Helga Velroyen
1985 def6577f Helga Velroyen
  @locking.ssynchronized(_config_lock, shared=1)
1986 def6577f Helga Velroyen
  def GetHvparamsString(self, hvname):
1987 def6577f Helga Velroyen
    """Return the hypervisor parameters of the given hypervisor.
1988 def6577f Helga Velroyen

1989 def6577f Helga Velroyen
    @type hvname: string
1990 def6577f Helga Velroyen
    @param hvname: name of a hypervisor
1991 def6577f Helga Velroyen
    @rtype: string
1992 def6577f Helga Velroyen
    @return: string containing key-value-pairs, one pair on each line;
1993 def6577f Helga Velroyen
      format: KEY=VALUE
1994 def6577f Helga Velroyen

1995 def6577f Helga Velroyen
    """
1996 def6577f Helga Velroyen
    return self._UnlockedGetHvparamsString(hvname)
1997 def6577f Helga Velroyen
1998 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1999 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
2000 a8083063 Iustin Pop

2001 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
2002 c41eea6e Iustin Pop
    held.
2003 c41eea6e Iustin Pop

2004 c41eea6e Iustin Pop
    @rtype: list
2005 f78ede4e Guido Trotter

2006 a8083063 Iustin Pop
    """
2007 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
2008 a8083063 Iustin Pop
2009 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2010 f78ede4e Guido Trotter
  def GetNodeList(self):
2011 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
2012 f78ede4e Guido Trotter

2013 f78ede4e Guido Trotter
    """
2014 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
2015 f78ede4e Guido Trotter
2016 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
2017 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
2018 94a02bb5 Iustin Pop

2019 94a02bb5 Iustin Pop
    """
2020 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
2021 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
2022 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.offline]
2023 94a02bb5 Iustin Pop
2024 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2025 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
2026 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
2027 6819dc49 Iustin Pop

2028 6819dc49 Iustin Pop
    """
2029 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
2030 6819dc49 Iustin Pop
2031 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2032 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
2033 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
2034 075b62ca Iustin Pop

2035 075b62ca Iustin Pop
    """
2036 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
2037 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
2038 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if node.vm_capable]
2039 075b62ca Iustin Pop
2040 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2041 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
2042 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
2043 8bf9e9a5 Iustin Pop

2044 8bf9e9a5 Iustin Pop
    """
2045 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
2046 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
2047 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.vm_capable]
2048 8bf9e9a5 Iustin Pop
2049 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2050 1c3231aa Thomas Thrainer
  def GetMultiNodeInfo(self, node_uuids):
2051 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
2052 f5eaa3c1 Iustin Pop

2053 1c3231aa Thomas Thrainer
    @param node_uuids: list of node UUIDs
2054 f5eaa3c1 Iustin Pop
    @rtype: list
2055 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
2056 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
2057 f5eaa3c1 Iustin Pop
        order
2058 f5eaa3c1 Iustin Pop

2059 f5eaa3c1 Iustin Pop
    """
2060 1c3231aa Thomas Thrainer
    return [(uuid, self._UnlockedGetNodeInfo(uuid)) for uuid in node_uuids]
2061 1c3231aa Thomas Thrainer
2062 1c3231aa Thomas Thrainer
  def _UnlockedGetAllNodesInfo(self):
2063 1c3231aa Thomas Thrainer
    """Gets configuration of all nodes.
2064 1c3231aa Thomas Thrainer

2065 1c3231aa Thomas Thrainer
    @note: See L{GetAllNodesInfo}
2066 1c3231aa Thomas Thrainer

2067 1c3231aa Thomas Thrainer
    """
2068 1c3231aa Thomas Thrainer
    return dict([(node_uuid, self._UnlockedGetNodeInfo(node_uuid))
2069 1c3231aa Thomas Thrainer
                 for node_uuid in self._UnlockedGetNodeList()])
2070 f5eaa3c1 Iustin Pop
2071 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2072 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
2073 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
2074 d65e5776 Iustin Pop

2075 d65e5776 Iustin Pop
    @rtype: dict
2076 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
2077 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
2078 d65e5776 Iustin Pop

2079 d65e5776 Iustin Pop
    """
2080 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
2081 ee14d800 Michael Hanselmann
2082 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfoByName(self, node_name):
2083 1c3231aa Thomas Thrainer
    for node in self._UnlockedGetAllNodesInfo().values():
2084 1c3231aa Thomas Thrainer
      if node.name == node_name:
2085 1c3231aa Thomas Thrainer
        return node
2086 1c3231aa Thomas Thrainer
    return None
2087 ee14d800 Michael Hanselmann
2088 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2089 1c3231aa Thomas Thrainer
  def GetNodeInfoByName(self, node_name):
2090 1c3231aa Thomas Thrainer
    """Get the L{objects.Node} object for a named node.
2091 1c3231aa Thomas Thrainer

2092 1c3231aa Thomas Thrainer
    @param node_name: name of the node to get information for
2093 1c3231aa Thomas Thrainer
    @type node_name: string
2094 1c3231aa Thomas Thrainer
    @return: the corresponding L{objects.Node} instance or None if no
2095 1c3231aa Thomas Thrainer
          information is available
2096 1c3231aa Thomas Thrainer

2097 1c3231aa Thomas Thrainer
    """
2098 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfoByName(node_name)
2099 1c3231aa Thomas Thrainer
2100 6b2a2942 Petr Pudlak
  @locking.ssynchronized(_config_lock, shared=1)
2101 6b2a2942 Petr Pudlak
  def GetNodeGroupInfoByName(self, nodegroup_name):
2102 6b2a2942 Petr Pudlak
    """Get the L{objects.NodeGroup} object for a named node group.
2103 6b2a2942 Petr Pudlak

2104 6b2a2942 Petr Pudlak
    @param nodegroup_name: name of the node group to get information for
2105 6b2a2942 Petr Pudlak
    @type nodegroup_name: string
2106 6b2a2942 Petr Pudlak
    @return: the corresponding L{objects.NodeGroup} instance or None if no
2107 6b2a2942 Petr Pudlak
          information is available
2108 6b2a2942 Petr Pudlak

2109 6b2a2942 Petr Pudlak
    """
2110 6b2a2942 Petr Pudlak
    for nodegroup in self._UnlockedGetAllNodeGroupsInfo().values():
2111 6b2a2942 Petr Pudlak
      if nodegroup.name == nodegroup_name:
2112 6b2a2942 Petr Pudlak
        return nodegroup
2113 6b2a2942 Petr Pudlak
    return None
2114 6b2a2942 Petr Pudlak
2115 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeName(self, node_spec):
2116 1c3231aa Thomas Thrainer
    if isinstance(node_spec, objects.Node):
2117 1c3231aa Thomas Thrainer
      return node_spec.name
2118 1c3231aa Thomas Thrainer
    elif isinstance(node_spec, basestring):
2119 1c3231aa Thomas Thrainer
      node_info = self._UnlockedGetNodeInfo(node_spec)
2120 1c3231aa Thomas Thrainer
      if node_info is None:
2121 1c3231aa Thomas Thrainer
        raise errors.OpExecError("Unknown node: %s" % node_spec)
2122 1c3231aa Thomas Thrainer
      return node_info.name
2123 1c3231aa Thomas Thrainer
    else:
2124 1c3231aa Thomas Thrainer
      raise errors.ProgrammerError("Can't handle node spec '%s'" % node_spec)
2125 1c3231aa Thomas Thrainer
2126 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2127 1c3231aa Thomas Thrainer
  def GetNodeName(self, node_spec):
2128 1c3231aa Thomas Thrainer
    """Gets the node name for the passed node.
2129 1c3231aa Thomas Thrainer

2130 1c3231aa Thomas Thrainer
    @param node_spec: node to get names for
2131 1c3231aa Thomas Thrainer
    @type node_spec: either node UUID or a L{objects.Node} object
2132 1c3231aa Thomas Thrainer
    @rtype: string
2133 1c3231aa Thomas Thrainer
    @return: node name
2134 1c3231aa Thomas Thrainer

2135 1c3231aa Thomas Thrainer
    """
2136 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(node_spec)
2137 1c3231aa Thomas Thrainer
2138 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeNames(self, node_specs):
2139 1c3231aa Thomas Thrainer
    return [self._UnlockedGetNodeName(node_spec) for node_spec in node_specs]
2140 1c3231aa Thomas Thrainer
2141 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2142 1c3231aa Thomas Thrainer
  def GetNodeNames(self, node_specs):
2143 1c3231aa Thomas Thrainer
    """Gets the node names for the passed list of nodes.
2144 1c3231aa Thomas Thrainer

2145 1c3231aa Thomas Thrainer
    @param node_specs: list of nodes to get names for
2146 1c3231aa Thomas Thrainer
    @type node_specs: list of either node UUIDs or L{objects.Node} objects
2147 1c3231aa Thomas Thrainer
    @rtype: list of strings
2148 1c3231aa Thomas Thrainer
    @return: list of node names
2149 ee14d800 Michael Hanselmann

2150 ee14d800 Michael Hanselmann
    """
2151 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeNames(node_specs)
2152 d65e5776 Iustin Pop
2153 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
2154 1c3231aa Thomas Thrainer
  def GetNodeGroupsFromNodes(self, node_uuids):
2155 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
2156 9d5b1371 Michael Hanselmann

2157 1c3231aa Thomas Thrainer
    @type node_uuids: list of string
2158 1c3231aa Thomas Thrainer
    @param node_uuids: List of node UUIDs
2159 9d5b1371 Michael Hanselmann
    @rtype: frozenset
2160 9d5b1371 Michael Hanselmann

2161 9d5b1371 Michael Hanselmann
    """
2162 1c3231aa Thomas Thrainer
    return frozenset(self._UnlockedGetNodeInfo(uuid).group
2163 1c3231aa Thomas Thrainer
                     for uuid in node_uuids)
2164 9d5b1371 Michael Hanselmann
2165 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
2166 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
2167 ec0292f1 Iustin Pop

2168 23f06b2b Iustin Pop
    @type exceptions: list
2169 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
2170 ec0292f1 Iustin Pop
    @rtype: tuple
2171 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
2172 ec0292f1 Iustin Pop

2173 ec0292f1 Iustin Pop
    """
2174 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
2175 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
2176 1c3231aa Thomas Thrainer
      if exceptions and node.uuid in exceptions:
2177 23f06b2b Iustin Pop
        continue
2178 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
2179 ec0292f1 Iustin Pop
        mc_max += 1
2180 ec0292f1 Iustin Pop
      if node.master_candidate:
2181 ec0292f1 Iustin Pop
        mc_now += 1
2182 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
2183 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
2184 ec0292f1 Iustin Pop
2185 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2186 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
2187 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
2188 ec0292f1 Iustin Pop

2189 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
2190 ec0292f1 Iustin Pop

2191 23f06b2b Iustin Pop
    @type exceptions: list
2192 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
2193 ec0292f1 Iustin Pop
    @rtype: tuple
2194 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
2195 ec0292f1 Iustin Pop

2196 ec0292f1 Iustin Pop
    """
2197 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
2198 ec0292f1 Iustin Pop
2199 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
2200 1c3231aa Thomas Thrainer
  def MaintainCandidatePool(self, exception_node_uuids):
2201 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
2202 ec0292f1 Iustin Pop

2203 1c3231aa Thomas Thrainer
    @type exception_node_uuids: list
2204 1c3231aa Thomas Thrainer
    @param exception_node_uuids: if passed, list of nodes that should be ignored
2205 ec0292f1 Iustin Pop
    @rtype: list
2206 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
2207 ec0292f1 Iustin Pop

2208 ec0292f1 Iustin Pop
    """
2209 1c3231aa Thomas Thrainer
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(
2210 1c3231aa Thomas Thrainer
                          exception_node_uuids)
2211 ec0292f1 Iustin Pop
    mod_list = []
2212 ec0292f1 Iustin Pop
    if mc_now < mc_max:
2213 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
2214 ec0292f1 Iustin Pop
      random.shuffle(node_list)
2215 1c3231aa Thomas Thrainer
      for uuid in node_list:
2216 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
2217 ec0292f1 Iustin Pop
          break
2218 1c3231aa Thomas Thrainer
        node = self._config_data.nodes[uuid]
2219 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
2220 1c3231aa Thomas Thrainer
            node.uuid in exception_node_uuids or not node.master_capable):
2221 ec0292f1 Iustin Pop
          continue
2222 ee513a66 Iustin Pop
        mod_list.append(node)
2223 ec0292f1 Iustin Pop
        node.master_candidate = True
2224 ec0292f1 Iustin Pop
        node.serial_no += 1
2225 ec0292f1 Iustin Pop
        mc_now += 1
2226 ec0292f1 Iustin Pop
      if mc_now != mc_max:
2227 ec0292f1 Iustin Pop
        # this should not happen
2228 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
2229 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
2230 ec0292f1 Iustin Pop
      if mod_list:
2231 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
2232 ec0292f1 Iustin Pop
        self._WriteConfig()
2233 ec0292f1 Iustin Pop
2234 ec0292f1 Iustin Pop
    return mod_list
2235 ec0292f1 Iustin Pop
2236 1c3231aa Thomas Thrainer
  def _UnlockedAddNodeToGroup(self, node_uuid, nodegroup_uuid):
2237 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
2238 190e3cb6 Guido Trotter

2239 190e3cb6 Guido Trotter
    """
2240 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
2241 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
2242 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
2243 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
2244 190e3cb6 Guido Trotter
      # is not found anymore.
2245 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
2246 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodegroups[nodegroup_uuid].members:
2247 1c3231aa Thomas Thrainer
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_uuid)
2248 190e3cb6 Guido Trotter
2249 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
2250 190e3cb6 Guido Trotter
    """Remove a given node from its group.
2251 190e3cb6 Guido Trotter

2252 190e3cb6 Guido Trotter
    """
2253 f936c153 Iustin Pop
    nodegroup = node.group
2254 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
2255 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
2256 1c3231aa Thomas Thrainer
                      " (while being removed from it)", node.uuid, nodegroup)
2257 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
2258 1c3231aa Thomas Thrainer
    if node.uuid not in nodegroup_obj.members:
2259 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
2260 1c3231aa Thomas Thrainer
                      " (while being removed from it)", node.uuid, nodegroup)
2261 190e3cb6 Guido Trotter
    else:
2262 1c3231aa Thomas Thrainer
      nodegroup_obj.members.remove(node.uuid)
2263 190e3cb6 Guido Trotter
2264 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
2265 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
2266 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
2267 54c31fd3 Michael Hanselmann

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

2271 54c31fd3 Michael Hanselmann
    """
2272 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
2273 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
2274 54c31fd3 Michael Hanselmann
2275 54c31fd3 Michael Hanselmann
    resmod = []
2276 54c31fd3 Michael Hanselmann
2277 1c3231aa Thomas Thrainer
    # Try to resolve UUIDs first
2278 1c3231aa Thomas Thrainer
    for (node_uuid, new_group_uuid) in mods:
2279 54c31fd3 Michael Hanselmann
      try:
2280 1c3231aa Thomas Thrainer
        node = nodes[node_uuid]
2281 54c31fd3 Michael Hanselmann
      except KeyError:
2282 1c3231aa Thomas Thrainer
        raise errors.ConfigurationError("Unable to find node '%s'" % node_uuid)
2283 54c31fd3 Michael Hanselmann
2284 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
2285 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
2286 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
2287 1c3231aa Thomas Thrainer
                      node_uuid, node.group)
2288 54c31fd3 Michael Hanselmann
        continue
2289 54c31fd3 Michael Hanselmann
2290 54c31fd3 Michael Hanselmann
      # Try to find current group of node
2291 54c31fd3 Michael Hanselmann
      try:
2292 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
2293 54c31fd3 Michael Hanselmann
      except KeyError:
2294 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
2295 54c31fd3 Michael Hanselmann
                                        node.group)
2296 54c31fd3 Michael Hanselmann
2297 54c31fd3 Michael Hanselmann
      # Try to find new group for node
2298 54c31fd3 Michael Hanselmann
      try:
2299 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
2300 54c31fd3 Michael Hanselmann
      except KeyError:
2301 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
2302 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
2303 54c31fd3 Michael Hanselmann
2304 1c3231aa Thomas Thrainer
      assert node.uuid in old_group.members, \
2305 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
2306 1c3231aa Thomas Thrainer
         " old group '%s'" % (node.uuid, old_group.uuid))
2307 1c3231aa Thomas Thrainer
      assert node.uuid not in new_group.members, \
2308 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
2309 1c3231aa Thomas Thrainer
         " its new group '%s'" % (node.uuid, new_group.uuid))
2310 54c31fd3 Michael Hanselmann
2311 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
2312 54c31fd3 Michael Hanselmann
2313 54c31fd3 Michael Hanselmann
    # Apply changes
2314 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
2315 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
2316 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
2317 54c31fd3 Michael Hanselmann
2318 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
2319 54c31fd3 Michael Hanselmann
2320 54c31fd3 Michael Hanselmann
      # Update members of involved groups
2321 1c3231aa Thomas Thrainer
      if node.uuid in old_group.members:
2322 1c3231aa Thomas Thrainer
        old_group.members.remove(node.uuid)
2323 1c3231aa Thomas Thrainer
      if node.uuid not in new_group.members:
2324 1c3231aa Thomas Thrainer
        new_group.members.append(node.uuid)
2325 54c31fd3 Michael Hanselmann
2326 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
2327 54c31fd3 Michael Hanselmann
    now = time.time()
2328 75191077 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142
2329 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
2330 54c31fd3 Michael Hanselmann
      obj.mtime = now
2331 54c31fd3 Michael Hanselmann
2332 54c31fd3 Michael Hanselmann
    # Force ssconf update
2333 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
2334 54c31fd3 Michael Hanselmann
2335 54c31fd3 Michael Hanselmann
    self._WriteConfig()
2336 54c31fd3 Michael Hanselmann
2337 a8083063 Iustin Pop
  def _BumpSerialNo(self):
2338 a8083063 Iustin Pop
    """Bump up the serial number of the config.
2339 a8083063 Iustin Pop

2340 a8083063 Iustin Pop
    """
2341 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
2342 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
2343 a8083063 Iustin Pop
2344 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
2345 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
2346 76d5d3a3 Iustin Pop

2347 76d5d3a3 Iustin Pop
    """
2348 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
2349 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
2350 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
2351 e0519c34 Dimitris Aragiorgis
            self._config_data.networks.values() +
2352 b87a9c5f Christos Stavrakakis
            self._AllDisks() +
2353 b87a9c5f Christos Stavrakakis
            self._AllNICs() +
2354 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
2355 76d5d3a3 Iustin Pop
2356 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
2357 a8083063 Iustin Pop
    """Read the config data from disk.
2358 a8083063 Iustin Pop

2359 a8083063 Iustin Pop
    """
2360 25cf4130 Petr Pudlak
    # Read the configuration data. If offline, read the file directly.
2361 25cf4130 Petr Pudlak
    # If online, call WConfd.
2362 25cf4130 Petr Pudlak
    if self._offline:
2363 25cf4130 Petr Pudlak
      raw_data = utils.ReadFile(self._cfg_file)
2364 25cf4130 Petr Pudlak
      try:
2365 25cf4130 Petr Pudlak
        dict_data = serializer.Load(raw_data)
2366 25cf4130 Petr Pudlak
      except Exception, err:
2367 25cf4130 Petr Pudlak
        raise errors.ConfigurationError(err)
2368 25cf4130 Petr Pudlak
    else:
2369 25cf4130 Petr Pudlak
      self._wconfd = wc.Client()
2370 25cf4130 Petr Pudlak
      dict_data = self._wconfd.ReadConfig()
2371 13998ef2 Michael Hanselmann
2372 a8083063 Iustin Pop
    try:
2373 25cf4130 Petr Pudlak
      data = objects.ConfigData.FromDict(dict_data)
2374 13998ef2 Michael Hanselmann
    except Exception, err:
2375 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
2376 5b263ed7 Michael Hanselmann
2377 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
2378 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
2379 5b263ed7 Michael Hanselmann
2380 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
2381 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
2382 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
2383 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
2384 90d726a8 Iustin Pop
2385 1c3231aa Thomas Thrainer
    if not data.cluster.master_node in data.nodes:
2386 1c3231aa Thomas Thrainer
      msg = ("The configuration denotes node %s as master, but does not"
2387 1c3231aa Thomas Thrainer
             " contain information about this node" %
2388 1c3231aa Thomas Thrainer
             data.cluster.master_node)
2389 1c3231aa Thomas Thrainer
      raise errors.ConfigurationError(msg)
2390 1c3231aa Thomas Thrainer
2391 1c3231aa Thomas Thrainer
    master_info = data.nodes[data.cluster.master_node]
2392 1c3231aa Thomas Thrainer
    if master_info.name != self._my_hostname and not accept_foreign:
2393 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
2394 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
2395 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
2396 1c3231aa Thomas Thrainer
             (master_info.name, self._my_hostname))
2397 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
2398 eb180fe2 Iustin Pop
2399 a8083063 Iustin Pop
    self._config_data = data
2400 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
2401 0779e3aa Iustin Pop
    # ssconf update
2402 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
2403 a8083063 Iustin Pop
2404 45f62156 Bernardo Dal Seno
    # Upgrade configuration if needed
2405 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
2406 76d5d3a3 Iustin Pop
2407 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
2408 bd407597 Iustin Pop
2409 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
2410 45f62156 Bernardo Dal Seno
    """Run any upgrade steps.
2411 76d5d3a3 Iustin Pop

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

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

2422 76d5d3a3 Iustin Pop
    """
2423 45f62156 Bernardo Dal Seno
    # Keep a copy of the persistent part of _config_data to check for changes
2424 45f62156 Bernardo Dal Seno
    # Serialization doesn't guarantee order in dictionaries
2425 45f62156 Bernardo Dal Seno
    oldconf = copy.deepcopy(self._config_data.ToDict())
2426 45f62156 Bernardo Dal Seno
2427 45f62156 Bernardo Dal Seno
    # In-object upgrades
2428 45f62156 Bernardo Dal Seno
    self._config_data.UpgradeConfig()
2429 45f62156 Bernardo Dal Seno
2430 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
2431 76d5d3a3 Iustin Pop
      if item.uuid is None:
2432 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
2433 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
2434 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
2435 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
2436 75cf411a Adeodato Simo
                                            members=[])
2437 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
2438 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
2439 f936c153 Iustin Pop
      if not node.group:
2440 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
2441 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
2442 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
2443 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
2444 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
2445 1c3231aa Thomas Thrainer
      self._UnlockedAddNodeToGroup(node.uuid, node.group)
2446 45f62156 Bernardo Dal Seno
2447 45f62156 Bernardo Dal Seno
    modified = (oldconf != self._config_data.ToDict())
2448 76d5d3a3 Iustin Pop
    if modified:
2449 76d5d3a3 Iustin Pop
      self._WriteConfig()
2450 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
2451 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
2452 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
2453 04db1880 Bernardo Dal Seno
    else:
2454 04db1880 Bernardo Dal Seno
      config_errors = self._UnlockedVerifyConfig()
2455 04db1880 Bernardo Dal Seno
      if config_errors:
2456 04db1880 Bernardo Dal Seno
        errmsg = ("Loaded configuration data is not consistent: %s" %
2457 04db1880 Bernardo Dal Seno
                  (utils.CommaJoin(config_errors)))
2458 04db1880 Bernardo Dal Seno
        logging.critical(errmsg)
2459 4fae38c5 Guido Trotter
2460 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
2461 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
2462 a8083063 Iustin Pop

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

2466 a8083063 Iustin Pop
    """
2467 a8083063 Iustin Pop
    if self._offline:
2468 a8083063 Iustin Pop
      return True
2469 a4eae71f Michael Hanselmann
2470 a8083063 Iustin Pop
    bad = False
2471 a8083063 Iustin Pop
2472 6a5b8b4b Iustin Pop
    node_list = []
2473 6a5b8b4b Iustin Pop
    addr_list = []
2474 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
2475 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
2476 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
2477 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
2478 6b294c53 Iustin Pop
    # in between
2479 1c3231aa Thomas Thrainer
    for node_uuid in self._UnlockedGetNodeList():
2480 1c3231aa Thomas Thrainer
      node_info = self._UnlockedGetNodeInfo(node_uuid)
2481 1c3231aa Thomas Thrainer
      if node_info.name == myhostname or not node_info.master_candidate:
2482 6a5b8b4b Iustin Pop
        continue
2483 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
2484 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
2485 6b294c53 Iustin Pop
2486 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
2487 415a7304 Michael Hanselmann
    result = \
2488 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
2489 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
2490 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
2491 1b54fc6c Guido Trotter
      if msg:
2492 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
2493 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
2494 1b54fc6c Guido Trotter
        logging.error(msg)
2495 a4eae71f Michael Hanselmann
2496 a4eae71f Michael Hanselmann
        if feedback_fn:
2497 a4eae71f Michael Hanselmann
          feedback_fn(msg)
2498 a4eae71f Michael Hanselmann
2499 a8083063 Iustin Pop
        bad = True
2500 a4eae71f Michael Hanselmann
2501 a8083063 Iustin Pop
    return not bad
2502 a8083063 Iustin Pop
2503 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
2504 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
2505 a8083063 Iustin Pop

2506 a8083063 Iustin Pop
    """
2507 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
2508 a4eae71f Michael Hanselmann
2509 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
2510 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
2511 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
2512 d2231b8c Iustin Pop
    # recovery to the user
2513 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
2514 4a89c54a Iustin Pop
    if config_errors:
2515 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
2516 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
2517 d2231b8c Iustin Pop
      logging.critical(errmsg)
2518 d2231b8c Iustin Pop
      if feedback_fn:
2519 d2231b8c Iustin Pop
        feedback_fn(errmsg)
2520 d2231b8c Iustin Pop
2521 a8083063 Iustin Pop
    if destination is None:
2522 a8083063 Iustin Pop
      destination = self._cfg_file
2523 25cf4130 Petr Pudlak
2524 a8083063 Iustin Pop
    self._BumpSerialNo()
2525 25cf4130 Petr Pudlak
    # Save the configuration data. If offline, write the file directly.
2526 25cf4130 Petr Pudlak
    # If online, call WConfd.
2527 25cf4130 Petr Pudlak
    if self._offline:
2528 25cf4130 Petr Pudlak
      txt = serializer.DumpJson(
2529 25cf4130 Petr Pudlak
        self._config_data.ToDict(_with_private=True),
2530 25cf4130 Petr Pudlak
        private_encoder=serializer.EncodeWithPrivateFields
2531 25cf4130 Petr Pudlak
      )
2532 13998ef2 Michael Hanselmann
2533 25cf4130 Petr Pudlak
      getents = self._getents()
2534 25cf4130 Petr Pudlak
      try:
2535 25cf4130 Petr Pudlak
        fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
2536 25cf4130 Petr Pudlak
                                 close=False, gid=getents.confd_gid, mode=0640)
2537 25cf4130 Petr Pudlak
      except errors.LockError:
2538 25cf4130 Petr Pudlak
        raise errors.ConfigurationError("The configuration file has been"
2539 25cf4130 Petr Pudlak
                                        " modified since the last write, cannot"
2540 25cf4130 Petr Pudlak
                                        " update")
2541 25cf4130 Petr Pudlak
      try:
2542 25cf4130 Petr Pudlak
        self._cfg_id = utils.GetFileID(fd=fd)
2543 25cf4130 Petr Pudlak
      finally:
2544 25cf4130 Petr Pudlak
        os.close(fd)
2545 25cf4130 Petr Pudlak
    else:
2546 25cf4130 Petr Pudlak
      try:
2547 25cf4130 Petr Pudlak
        self._wconfd.WriteConfig(self._config_data.ToDict())
2548 25cf4130 Petr Pudlak
      except errors.LockError:
2549 25cf4130 Petr Pudlak
        raise errors.ConfigurationError("The configuration file has been"
2550 25cf4130 Petr Pudlak
                                        " modified since the last write, cannot"
2551 25cf4130 Petr Pudlak
                                        " update")
2552 13998ef2 Michael Hanselmann
2553 14e15659 Iustin Pop
    self.write_count += 1
2554 3d3a04bc Iustin Pop
2555 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
2556 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
2557 a8083063 Iustin Pop
2558 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
2559 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
2560 d9a855f1 Michael Hanselmann
      if not self._offline:
2561 b2acdbdc Michael Hanselmann
        result = self._GetRpc(None).call_write_ssconf_files(
2562 1c3231aa Thomas Thrainer
          self._UnlockedGetNodeNames(self._UnlockedGetOnlineNodeList()),
2563 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
2564 a4eae71f Michael Hanselmann
2565 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
2566 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
2567 e1e75d00 Iustin Pop
          if msg:
2568 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
2569 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
2570 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
2571 a4eae71f Michael Hanselmann
2572 a4eae71f Michael Hanselmann
            if feedback_fn:
2573 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
2574 a4eae71f Michael Hanselmann
2575 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
2576 54d1a06e Michael Hanselmann
2577 def6577f Helga Velroyen
  def _GetAllHvparamsStrings(self, hypervisors):
2578 def6577f Helga Velroyen
    """Get the hvparams of all given hypervisors from the config.
2579 def6577f Helga Velroyen

2580 def6577f Helga Velroyen
    @type hypervisors: list of string
2581 def6577f Helga Velroyen
    @param hypervisors: list of hypervisor names
2582 def6577f Helga Velroyen
    @rtype: dict of strings
2583 def6577f Helga Velroyen
    @returns: dictionary mapping the hypervisor name to a string representation
2584 def6577f Helga Velroyen
      of the hypervisor's hvparams
2585 def6577f Helga Velroyen

2586 def6577f Helga Velroyen
    """
2587 def6577f Helga Velroyen
    hvparams = {}
2588 def6577f Helga Velroyen
    for hv in hypervisors:
2589 def6577f Helga Velroyen
      hvparams[hv] = self._UnlockedGetHvparamsString(hv)
2590 def6577f Helga Velroyen
    return hvparams
2591 def6577f Helga Velroyen
2592 def6577f Helga Velroyen
  @staticmethod
2593 def6577f Helga Velroyen
  def _ExtendByAllHvparamsStrings(ssconf_values, all_hvparams):
2594 def6577f Helga Velroyen
    """Extends the ssconf_values dictionary by hvparams.
2595 def6577f Helga Velroyen

2596 def6577f Helga Velroyen
    @type ssconf_values: dict of strings
2597 def6577f Helga Velroyen
    @param ssconf_values: dictionary mapping ssconf_keys to strings
2598 def6577f Helga Velroyen
      representing the content of ssconf files
2599 def6577f Helga Velroyen
    @type all_hvparams: dict of strings
2600 def6577f Helga Velroyen
    @param all_hvparams: dictionary mapping hypervisor names to a string
2601 def6577f Helga Velroyen
      representation of their hvparams
2602 def6577f Helga Velroyen
    @rtype: same as ssconf_values
2603 def6577f Helga Velroyen
    @returns: the ssconf_values dictionary extended by hvparams
2604 def6577f Helga Velroyen

2605 def6577f Helga Velroyen
    """
2606 def6577f Helga Velroyen
    for hv in all_hvparams:
2607 def6577f Helga Velroyen
      ssconf_key = constants.SS_HVPARAMS_PREF + hv
2608 def6577f Helga Velroyen
      ssconf_values[ssconf_key] = all_hvparams[hv]
2609 def6577f Helga Velroyen
    return ssconf_values
2610 def6577f Helga Velroyen
2611 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
2612 054596f0 Iustin Pop
    """Return the values needed by ssconf.
2613 054596f0 Iustin Pop

2614 054596f0 Iustin Pop
    @rtype: dict
2615 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2616 054596f0 Iustin Pop
        associated value
2617 054596f0 Iustin Pop

2618 054596f0 Iustin Pop
    """
2619 a3316e4a Iustin Pop
    fn = "\n".join
2620 da4a52a3 Thomas Thrainer
    instance_names = utils.NiceSort(
2621 da4a52a3 Thomas Thrainer
                       [inst.name for inst in
2622 da4a52a3 Thomas Thrainer
                        self._UnlockedGetAllInstancesInfo().values()])
2623 1c3231aa Thomas Thrainer
    node_infos = self._UnlockedGetAllNodesInfo().values()
2624 1c3231aa Thomas Thrainer
    node_names = [node.name for node in node_infos]
2625 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
2626 1c3231aa Thomas Thrainer
                    for ninfo in node_infos]
2627 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
2628 1c3231aa Thomas Thrainer
                    for ninfo in node_infos]
2629 a3316e4a Iustin Pop
2630 81a49123 Iustin Pop
    instance_data = fn(instance_names)
2631 1c3231aa Thomas Thrainer
    off_data = fn(node.name for node in node_infos if node.offline)
2632 1c3231aa Thomas Thrainer
    on_data = fn(node.name for node in node_infos if not node.offline)
2633 1c3231aa Thomas Thrainer
    mc_data = fn(node.name for node in node_infos if node.master_candidate)
2634 1c3231aa Thomas Thrainer
    mc_ips_data = fn(node.primary_ip for node in node_infos
2635 8113a52e Luca Bigliardi
                     if node.master_candidate)
2636 a3316e4a Iustin Pop
    node_data = fn(node_names)
2637 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
2638 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
2639 f56618e0 Iustin Pop
2640 054596f0 Iustin Pop
    cluster = self._config_data.cluster
2641 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
2642 4f7a6a10 Iustin Pop
2643 1059337d Helga Velroyen
    master_candidates_certs = fn("%s=%s" % (mc_uuid, mc_cert)
2644 1059337d Helga Velroyen
                                 for mc_uuid, mc_cert
2645 1059337d Helga Velroyen
                                 in cluster.candidate_certs.items())
2646 1059337d Helga Velroyen
2647 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
2648 def6577f Helga Velroyen
    all_hvparams = self._GetAllHvparamsStrings(constants.HYPER_TYPES)
2649 4f7a6a10 Iustin Pop
2650 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
2651 0fbae49a Balazs Lecz
2652 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
2653 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
2654 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
2655 6c0a75db Dimitris Aragiorgis
    networks = ["%s %s" % (net.uuid, net.name) for net in
2656 6c0a75db Dimitris Aragiorgis
                self._config_data.networks.values()]
2657 6c0a75db Dimitris Aragiorgis
    networks_data = fn(utils.NiceSort(networks))
2658 6f076453 Guido Trotter
2659 2afc9238 Iustin Pop
    ssconf_values = {
2660 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
2661 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
2662 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
2663 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
2664 d3e6fd0e Santi Raffa
      constants.SS_GLUSTER_STORAGE_DIR: cluster.gluster_storage_dir,
2665 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
2666 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
2667 1059337d Helga Velroyen
      constants.SS_MASTER_CANDIDATES_CERTS: master_candidates_certs,
2668 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
2669 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
2670 5a8648eb Andrea Spadaccini
      constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
2671 1c3231aa Thomas Thrainer
      constants.SS_MASTER_NODE: self._UnlockedGetNodeName(cluster.master_node),
2672 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
2673 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
2674 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
2675 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
2676 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
2677 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
2678 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
2679 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
2680 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
2681 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
2682 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
2683 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
2684 6c0a75db Dimitris Aragiorgis
      constants.SS_NETWORKS: networks_data,
2685 03d1dba2 Michael Hanselmann
      }
2686 def6577f Helga Velroyen
    ssconf_values = self._ExtendByAllHvparamsStrings(ssconf_values,
2687 def6577f Helga Velroyen
                                                     all_hvparams)
2688 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
2689 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
2690 2afc9238 Iustin Pop
    if bad_values:
2691 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
2692 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
2693 2afc9238 Iustin Pop
                                      " values: %s" % err)
2694 2afc9238 Iustin Pop
    return ssconf_values
2695 03d1dba2 Michael Hanselmann
2696 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2697 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
2698 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
2699 d367b66c Manuel Franceschini

2700 d367b66c Manuel Franceschini
    """
2701 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2702 d367b66c Manuel Franceschini
2703 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2704 a8083063 Iustin Pop
  def GetVGName(self):
2705 a8083063 Iustin Pop
    """Return the volume group name.
2706 a8083063 Iustin Pop

2707 a8083063 Iustin Pop
    """
2708 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2709 a8083063 Iustin Pop
2710 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2711 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2712 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2713 89ff8e15 Manuel Franceschini

2714 89ff8e15 Manuel Franceschini
    """
2715 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2716 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2717 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2718 89ff8e15 Manuel Franceschini
2719 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2720 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2721 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2722 9e33896b Luca Bigliardi

2723 9e33896b Luca Bigliardi
    """
2724 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2725 9e33896b Luca Bigliardi
2726 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2727 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2728 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2729 9e33896b Luca Bigliardi

2730 9e33896b Luca Bigliardi
    """
2731 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2732 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2733 9e33896b Luca Bigliardi
    self._WriteConfig()
2734 9e33896b Luca Bigliardi
2735 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2736 a8083063 Iustin Pop
  def GetMACPrefix(self):
2737 a8083063 Iustin Pop
    """Return the mac prefix.
2738 a8083063 Iustin Pop

2739 a8083063 Iustin Pop
    """
2740 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2741 62779dd0 Iustin Pop
2742 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2743 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2744 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2745 62779dd0 Iustin Pop

2746 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2747 c41eea6e Iustin Pop
    @return: the cluster object
2748 62779dd0 Iustin Pop

2749 62779dd0 Iustin Pop
    """
2750 62779dd0 Iustin Pop
    return self._config_data.cluster
2751 e00fb268 Iustin Pop
2752 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2753 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2754 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2755 51cb1581 Luca Bigliardi

2756 51cb1581 Luca Bigliardi
    """
2757 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2758 51cb1581 Luca Bigliardi
2759 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2760 d1547283 Dimitris Aragiorgis
  def Update(self, target, feedback_fn, ec_id=None):
2761 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2762 e00fb268 Iustin Pop

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

2769 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2770 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2771 c41eea6e Iustin Pop
        the cluster
2772 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2773 c41eea6e Iustin Pop

2774 e00fb268 Iustin Pop
    """
2775 e00fb268 Iustin Pop
    if self._config_data is None:
2776 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2777 3ecf6786 Iustin Pop
                                   " cannot save.")
2778 f34901f8 Iustin Pop
    update_serial = False
2779 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2780 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2781 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2782 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2783 f34901f8 Iustin Pop
      update_serial = True
2784 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2785 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2786 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2787 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2788 1e0d3321 Dimitris Aragiorgis
    elif isinstance(target, objects.Network):
2789 1e0d3321 Dimitris Aragiorgis
      test = target in self._config_data.networks.values()
2790 e00fb268 Iustin Pop
    else:
2791 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2792 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2793 e00fb268 Iustin Pop
    if not test:
2794 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2795 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2796 f34901f8 Iustin Pop
    target.serial_no += 1
2797 d693c864 Iustin Pop
    target.mtime = now = time.time()
2798 f34901f8 Iustin Pop
2799 cff4c037 Iustin Pop
    if update_serial:
2800 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2801 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2802 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2803 b989e85d Iustin Pop
2804 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2805 da4a52a3 Thomas Thrainer
      self._UnlockedReleaseDRBDMinors(target.uuid)
2806 61cf6b5e Iustin Pop
2807 d1547283 Dimitris Aragiorgis
    if ec_id is not None:
2808 d1547283 Dimitris Aragiorgis
      # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams
2809 d1547283 Dimitris Aragiorgis
      self._UnlockedCommitTemporaryIps(ec_id)
2810 d1547283 Dimitris Aragiorgis
2811 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2812 73064714 Guido Trotter
2813 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2814 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2815 73064714 Guido Trotter
    """Drop per-execution-context reservations
2816 73064714 Guido Trotter

2817 73064714 Guido Trotter
    """
2818 d8aee57e Iustin Pop
    for rm in self._all_rms:
2819 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)
2820 6c0a75db Dimitris Aragiorgis
2821 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2822 6c0a75db Dimitris Aragiorgis
  def GetAllNetworksInfo(self):
2823 6a94d553 Dimitris Aragiorgis
    """Get configuration info of all the networks.
2824 6c0a75db Dimitris Aragiorgis

2825 6c0a75db Dimitris Aragiorgis
    """
2826 6c0a75db Dimitris Aragiorgis
    return dict(self._config_data.networks)
2827 6c0a75db Dimitris Aragiorgis
2828 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetworkList(self):
2829 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2830 6c0a75db Dimitris Aragiorgis

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

2833 6c0a75db Dimitris Aragiorgis
    """
2834 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks.keys()
2835 6c0a75db Dimitris Aragiorgis
2836 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2837 6c0a75db Dimitris Aragiorgis
  def GetNetworkList(self):
2838 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2839 6c0a75db Dimitris Aragiorgis

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

2842 6c0a75db Dimitris Aragiorgis
    """
2843 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetworkList()
2844 6c0a75db Dimitris Aragiorgis
2845 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2846 6c0a75db Dimitris Aragiorgis
  def GetNetworkNames(self):
2847 6c0a75db Dimitris Aragiorgis
    """Get a list of network names
2848 6c0a75db Dimitris Aragiorgis

2849 6c0a75db Dimitris Aragiorgis
    """
2850 beb81ea5 Dimitris Aragiorgis
    names = [net.name
2851 beb81ea5 Dimitris Aragiorgis
             for net in self._config_data.networks.values()]
2852 6c0a75db Dimitris Aragiorgis
    return names
2853 6c0a75db Dimitris Aragiorgis
2854 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetwork(self, uuid):
2855 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2856 6c0a75db Dimitris Aragiorgis

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

2859 6c0a75db Dimitris Aragiorgis
    """
2860 6c0a75db Dimitris Aragiorgis
    if uuid not in self._config_data.networks:
2861 6c0a75db Dimitris Aragiorgis
      return None
2862 6c0a75db Dimitris Aragiorgis
2863 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks[uuid]
2864 6c0a75db Dimitris Aragiorgis
2865 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2866 6c0a75db Dimitris Aragiorgis
  def GetNetwork(self, uuid):
2867 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2868 6c0a75db Dimitris Aragiorgis

2869 6c0a75db Dimitris Aragiorgis
    It takes the information from the configuration file.
2870 6c0a75db Dimitris Aragiorgis

2871 6c0a75db Dimitris Aragiorgis
    @param uuid: UUID of the network
2872 6c0a75db Dimitris Aragiorgis

2873 6c0a75db Dimitris Aragiorgis
    @rtype: L{objects.Network}
2874 6c0a75db Dimitris Aragiorgis
    @return: the network object
2875 6c0a75db Dimitris Aragiorgis

2876 6c0a75db Dimitris Aragiorgis
    """
2877 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetwork(uuid)
2878 6c0a75db Dimitris Aragiorgis
2879 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2880 6c0a75db Dimitris Aragiorgis
  def AddNetwork(self, net, ec_id, check_uuid=True):
2881 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2882 6c0a75db Dimitris Aragiorgis

2883 6c0a75db Dimitris Aragiorgis
    @type net: L{objects.Network}
2884 6c0a75db Dimitris Aragiorgis
    @param net: the Network object to add
2885 6c0a75db Dimitris Aragiorgis
    @type ec_id: string
2886 6c0a75db Dimitris Aragiorgis
    @param ec_id: unique id for the job to use when creating a missing UUID
2887 6c0a75db Dimitris Aragiorgis

2888 6c0a75db Dimitris Aragiorgis
    """
2889 6c0a75db Dimitris Aragiorgis
    self._UnlockedAddNetwork(net, ec_id, check_uuid)
2890 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2891 6c0a75db Dimitris Aragiorgis
2892 6c0a75db Dimitris Aragiorgis
  def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
2893 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2894 6c0a75db Dimitris Aragiorgis

2895 6c0a75db Dimitris Aragiorgis
    """
2896 6c0a75db Dimitris Aragiorgis
    logging.info("Adding network %s to configuration", net.name)
2897 6c0a75db Dimitris Aragiorgis
2898 6c0a75db Dimitris Aragiorgis
    if check_uuid:
2899 6c0a75db Dimitris Aragiorgis
      self._EnsureUUID(net, ec_id)
2900 6c0a75db Dimitris Aragiorgis
2901 6c0a75db Dimitris Aragiorgis
    net.serial_no = 1
2902 22ff02a7 Christos Stavrakakis
    net.ctime = net.mtime = time.time()
2903 6c0a75db Dimitris Aragiorgis
    self._config_data.networks[net.uuid] = net
2904 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2905 6c0a75db Dimitris Aragiorgis
2906 6c0a75db Dimitris Aragiorgis
  def _UnlockedLookupNetwork(self, target):
2907 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2908 6c0a75db Dimitris Aragiorgis

2909 6c0a75db Dimitris Aragiorgis
    @type target: string
2910 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2911 6c0a75db Dimitris Aragiorgis
    @rtype: string
2912 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2913 6c0a75db Dimitris Aragiorgis
    @raises errors.OpPrereqError: when the target network cannot be found
2914 6c0a75db Dimitris Aragiorgis

2915 6c0a75db Dimitris Aragiorgis
    """
2916 9394f4d1 Dimitris Aragiorgis
    if target is None:
2917 9394f4d1 Dimitris Aragiorgis
      return None
2918 6c0a75db Dimitris Aragiorgis
    if target in self._config_data.networks:
2919 6c0a75db Dimitris Aragiorgis
      return target
2920 6c0a75db Dimitris Aragiorgis
    for net in self._config_data.networks.values():
2921 6c0a75db Dimitris Aragiorgis
      if net.name == target:
2922 6c0a75db Dimitris Aragiorgis
        return net.uuid
2923 1b68f268 Helga Velroyen
    raise errors.OpPrereqError("Network '%s' not found" % target,
2924 1b68f268 Helga Velroyen
                               errors.ECODE_NOENT)
2925 6c0a75db Dimitris Aragiorgis
2926 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2927 6c0a75db Dimitris Aragiorgis
  def LookupNetwork(self, target):
2928 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2929 6c0a75db Dimitris Aragiorgis

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

2932 6c0a75db Dimitris Aragiorgis
    @type target: string
2933 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2934 6c0a75db Dimitris Aragiorgis
    @rtype: string
2935 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2936 6c0a75db Dimitris Aragiorgis

2937 6c0a75db Dimitris Aragiorgis
    """
2938 6c0a75db Dimitris Aragiorgis
    return self._UnlockedLookupNetwork(target)
2939 6c0a75db Dimitris Aragiorgis
2940 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2941 6c0a75db Dimitris Aragiorgis
  def RemoveNetwork(self, network_uuid):
2942 6c0a75db Dimitris Aragiorgis
    """Remove a network from the configuration.
2943 6c0a75db Dimitris Aragiorgis

2944 6c0a75db Dimitris Aragiorgis
    @type network_uuid: string
2945 6c0a75db Dimitris Aragiorgis
    @param network_uuid: the UUID of the network to remove
2946 6c0a75db Dimitris Aragiorgis

2947 6c0a75db Dimitris Aragiorgis
    """
2948 6c0a75db Dimitris Aragiorgis
    logging.info("Removing network %s from configuration", network_uuid)
2949 6c0a75db Dimitris Aragiorgis
2950 6c0a75db Dimitris Aragiorgis
    if network_uuid not in self._config_data.networks:
2951 6c0a75db Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
2952 6c0a75db Dimitris Aragiorgis
2953 6c0a75db Dimitris Aragiorgis
    del self._config_data.networks[network_uuid]
2954 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2955 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2956 ad4a9ae7 Dimitris Aragiorgis
2957 1c3231aa Thomas Thrainer
  def _UnlockedGetGroupNetParams(self, net_uuid, node_uuid):
2958 ad4a9ae7 Dimitris Aragiorgis
    """Get the netparams (mode, link) of a network.
2959 ad4a9ae7 Dimitris Aragiorgis

2960 ad4a9ae7 Dimitris Aragiorgis
    Get a network's netparams for a given node.
2961 ad4a9ae7 Dimitris Aragiorgis

2962 9ccacbc8 Dimitris Aragiorgis
    @type net_uuid: string
2963 9ccacbc8 Dimitris Aragiorgis
    @param net_uuid: network uuid
2964 1c3231aa Thomas Thrainer
    @type node_uuid: string
2965 1c3231aa Thomas Thrainer
    @param node_uuid: node UUID
2966 ad4a9ae7 Dimitris Aragiorgis
    @rtype: dict or None
2967 ad4a9ae7 Dimitris Aragiorgis
    @return: netparams
2968 ad4a9ae7 Dimitris Aragiorgis

2969 ad4a9ae7 Dimitris Aragiorgis
    """
2970 1c3231aa Thomas Thrainer
    node_info = self._UnlockedGetNodeInfo(node_uuid)
2971 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2972 ad4a9ae7 Dimitris Aragiorgis
    netparams = nodegroup_info.networks.get(net_uuid, None)
2973 ad4a9ae7 Dimitris Aragiorgis
2974 ad4a9ae7 Dimitris Aragiorgis
    return netparams
2975 ad4a9ae7 Dimitris Aragiorgis
2976 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2977 1c3231aa Thomas Thrainer
  def GetGroupNetParams(self, net_uuid, node_uuid):
2978 ad4a9ae7 Dimitris Aragiorgis
    """Locking wrapper of _UnlockedGetGroupNetParams()
2979 ad4a9ae7 Dimitris Aragiorgis

2980 ad4a9ae7 Dimitris Aragiorgis
    """
2981 1c3231aa Thomas Thrainer
    return self._UnlockedGetGroupNetParams(net_uuid, node_uuid)
2982 ad4a9ae7 Dimitris Aragiorgis
2983 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2984 1c3231aa Thomas Thrainer
  def CheckIPInNodeGroup(self, ip, node_uuid):
2985 6a94d553 Dimitris Aragiorgis
    """Check IP uniqueness in nodegroup.
2986 6a94d553 Dimitris Aragiorgis

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

2991 ad4a9ae7 Dimitris Aragiorgis
    @type ip: string
2992 ad4a9ae7 Dimitris Aragiorgis
    @param ip: ip address
2993 1c3231aa Thomas Thrainer
    @type node_uuid: string
2994 1c3231aa Thomas Thrainer
    @param node_uuid: node UUID
2995 ad4a9ae7 Dimitris Aragiorgis
    @rtype: (string, dict) or (None, None)
2996 ad4a9ae7 Dimitris Aragiorgis
    @return: (network name, netparams)
2997 ad4a9ae7 Dimitris Aragiorgis

2998 ad4a9ae7 Dimitris Aragiorgis
    """
2999 ad4a9ae7 Dimitris Aragiorgis
    if ip is None:
3000 ad4a9ae7 Dimitris Aragiorgis
      return (None, None)
3001 1c3231aa Thomas Thrainer
    node_info = self._UnlockedGetNodeInfo(node_uuid)
3002 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
3003 ad4a9ae7 Dimitris Aragiorgis
    for net_uuid in nodegroup_info.networks.keys():
3004 ad4a9ae7 Dimitris Aragiorgis
      net_info = self._UnlockedGetNetwork(net_uuid)
3005 ad4a9ae7 Dimitris Aragiorgis
      pool = network.AddressPool(net_info)
3006 beb81ea5 Dimitris Aragiorgis
      if pool.Contains(ip):
3007 ad4a9ae7 Dimitris Aragiorgis
        return (net_info.name, nodegroup_info.networks[net_uuid])
3008 ad4a9ae7 Dimitris Aragiorgis
3009 ad4a9ae7 Dimitris Aragiorgis
    return (None, None)