Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ fb62843c

History | View | Annotate | Download (96.5 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

235 5768e6a6 René Nussbaumer
    """
236 5768e6a6 René Nussbaumer
    nodegroup = self._UnlockedGetNodeGroup(node.group)
237 5768e6a6 René Nussbaumer
    return self._config_data.cluster.FillND(node, nodegroup)
238 5768e6a6 René Nussbaumer
239 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
240 8a147bba René Nussbaumer
  def GetInstanceDiskParams(self, instance):
241 8a147bba René Nussbaumer
    """Get the disk params populated with inherit chain.
242 8a147bba René Nussbaumer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

451 b87a9c5f Christos Stavrakakis
    """
452 79780863 Michele Tartara
    def DiskAndAllChildren(disk):
453 79780863 Michele Tartara
      """Returns a list containing the given disk and all of his children.
454 79780863 Michele Tartara

455 79780863 Michele Tartara
      """
456 79780863 Michele Tartara
      disks = [disk]
457 79780863 Michele Tartara
      if disk.children:
458 79780863 Michele Tartara
        for child_disk in disk.children:
459 79780863 Michele Tartara
          disks.extend(DiskAndAllChildren(child_disk))
460 79780863 Michele Tartara
      return disks
461 79780863 Michele Tartara
462 b87a9c5f Christos Stavrakakis
    disks = []
463 b87a9c5f Christos Stavrakakis
    for instance in self._config_data.instances.values():
464 79780863 Michele Tartara
      for disk in instance.disks:
465 79780863 Michele Tartara
        disks.extend(DiskAndAllChildren(disk))
466 b87a9c5f Christos Stavrakakis
    return disks
467 b87a9c5f Christos Stavrakakis
468 b87a9c5f Christos Stavrakakis
  def _AllNICs(self):
469 b87a9c5f Christos Stavrakakis
    """Compute the list of all NICs.
470 b87a9c5f Christos Stavrakakis

471 b87a9c5f Christos Stavrakakis
    """
472 b87a9c5f Christos Stavrakakis
    nics = []
473 b87a9c5f Christos Stavrakakis
    for instance in self._config_data.instances.values():
474 b87a9c5f Christos Stavrakakis
      nics.extend(instance.nics)
475 b87a9c5f Christos Stavrakakis
    return nics
476 b87a9c5f Christos Stavrakakis
477 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
478 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
479 34e54ebc Iustin Pop

480 34e54ebc Iustin Pop
    @type include_temporary: boolean
481 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
482 34e54ebc Iustin Pop
    @rtype: set
483 34e54ebc Iustin Pop
    @return: a set of IDs
484 34e54ebc Iustin Pop

485 34e54ebc Iustin Pop
    """
486 34e54ebc Iustin Pop
    existing = set()
487 34e54ebc Iustin Pop
    if include_temporary:
488 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
489 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
490 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
491 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
492 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
493 34e54ebc Iustin Pop
    return existing
494 34e54ebc Iustin Pop
495 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
496 430b923c Iustin Pop
    """Generate an unique UUID.
497 923b1523 Iustin Pop

498 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
499 923b1523 Iustin Pop
    duplicates.
500 923b1523 Iustin Pop

501 c41eea6e Iustin Pop
    @rtype: string
502 c41eea6e Iustin Pop
    @return: the unique id
503 923b1523 Iustin Pop

504 923b1523 Iustin Pop
    """
505 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
506 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
507 923b1523 Iustin Pop
508 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
509 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
510 430b923c Iustin Pop
    """Generate an unique ID.
511 430b923c Iustin Pop

512 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
513 430b923c Iustin Pop

514 4fae38c5 Guido Trotter
    @type ec_id: string
515 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
516 34d657ba Iustin Pop

517 34d657ba Iustin Pop
    """
518 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
519 34d657ba Iustin Pop
520 a8083063 Iustin Pop
  def _AllMACs(self):
521 a8083063 Iustin Pop
    """Return all MACs present in the config.
522 a8083063 Iustin Pop

523 c41eea6e Iustin Pop
    @rtype: list
524 c41eea6e Iustin Pop
    @return: the list of all MACs
525 c41eea6e Iustin Pop

526 a8083063 Iustin Pop
    """
527 a8083063 Iustin Pop
    result = []
528 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
529 a8083063 Iustin Pop
      for nic in instance.nics:
530 a8083063 Iustin Pop
        result.append(nic.mac)
531 a8083063 Iustin Pop
532 a8083063 Iustin Pop
    return result
533 a8083063 Iustin Pop
534 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
535 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
536 f9518d38 Iustin Pop

537 c41eea6e Iustin Pop
    @rtype: list
538 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
539 c41eea6e Iustin Pop

540 f9518d38 Iustin Pop
    """
541 f9518d38 Iustin Pop
    def helper(disk, result):
542 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
543 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
544 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
545 f9518d38 Iustin Pop
      if disk.children:
546 f9518d38 Iustin Pop
        for child in disk.children:
547 f9518d38 Iustin Pop
          helper(child, result)
548 f9518d38 Iustin Pop
549 f9518d38 Iustin Pop
    result = []
550 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
551 f9518d38 Iustin Pop
      for disk in instance.disks:
552 f9518d38 Iustin Pop
        helper(disk, result)
553 f9518d38 Iustin Pop
554 f9518d38 Iustin Pop
    return result
555 f9518d38 Iustin Pop
556 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
557 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
558 4b98ac29 Iustin Pop

559 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
560 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
561 4b98ac29 Iustin Pop
    @type l_ids: list
562 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
563 4b98ac29 Iustin Pop
    @type p_ids: list
564 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
565 4b98ac29 Iustin Pop
    @rtype: list
566 4b98ac29 Iustin Pop
    @return: a list of error messages
567 4b98ac29 Iustin Pop

568 4b98ac29 Iustin Pop
    """
569 4b98ac29 Iustin Pop
    result = []
570 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
571 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
572 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
573 25ae22e4 Iustin Pop
      else:
574 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
575 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
576 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
577 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
578 25ae22e4 Iustin Pop
      else:
579 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
580 4b98ac29 Iustin Pop
581 4b98ac29 Iustin Pop
    if disk.children:
582 4b98ac29 Iustin Pop
      for child in disk.children:
583 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
584 4b98ac29 Iustin Pop
    return result
585 4b98ac29 Iustin Pop
586 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
587 a8efbb40 Iustin Pop
    """Verify function.
588 a8efbb40 Iustin Pop

589 4a89c54a Iustin Pop
    @rtype: list
590 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
591 4a89c54a Iustin Pop
        configuration errors
592 4a89c54a Iustin Pop

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

867 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
868 4a89c54a Iustin Pop

869 4a89c54a Iustin Pop
    @rtype: list
870 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
871 4a89c54a Iustin Pop
        configuration errors
872 4a89c54a Iustin Pop

873 4a89c54a Iustin Pop
    """
874 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
875 4a89c54a Iustin Pop
876 1c3231aa Thomas Thrainer
  def _UnlockedSetDiskID(self, disk, node_uuid):
877 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
878 a8083063 Iustin Pop

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

881 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
882 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
883 a8083063 Iustin Pop
    node.
884 a8083063 Iustin Pop

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

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

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

920 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
921 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
922 f78ede4e Guido Trotter
    node.
923 f78ede4e Guido Trotter

924 f78ede4e Guido Trotter
    """
925 1c3231aa Thomas Thrainer
    return self._UnlockedSetDiskID(disk, node_uuid)
926 f78ede4e Guido Trotter
927 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
928 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
929 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
930 b2fddf63 Iustin Pop

931 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
932 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
933 3b3b1bca Dimitris Aragiorgis
        configuration is stable
934 3b3b1bca Dimitris Aragiorgis

935 b2fddf63 Iustin Pop
    """
936 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
937 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
938 264bb3c5 Michael Hanselmann
939 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
940 264bb3c5 Michael Hanselmann
941 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
942 b2fddf63 Iustin Pop
  def GetPortList(self):
943 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
944 264bb3c5 Michael Hanselmann

945 264bb3c5 Michael Hanselmann
    """
946 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
947 264bb3c5 Michael Hanselmann
948 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
949 a8083063 Iustin Pop
  def AllocatePort(self):
950 a8083063 Iustin Pop
    """Allocate a port.
951 a8083063 Iustin Pop

952 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
953 b2fddf63 Iustin Pop
    default port range (and in this case we increase
954 b2fddf63 Iustin Pop
    highest_used_port).
955 a8083063 Iustin Pop

956 a8083063 Iustin Pop
    """
957 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
958 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
959 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
960 264bb3c5 Michael Hanselmann
    else:
961 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
962 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
963 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
964 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
965 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
966 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
967 a8083063 Iustin Pop
968 a8083063 Iustin Pop
    self._WriteConfig()
969 a8083063 Iustin Pop
    return port
970 a8083063 Iustin Pop
971 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
972 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
973 a81c53c9 Iustin Pop

974 4a89c54a Iustin Pop
    @rtype: (dict, list)
975 da4a52a3 Thomas Thrainer
    @return: dictionary of node_uuid: dict of minor: instance_uuid;
976 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
977 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
978 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
979 4a89c54a Iustin Pop
        should raise an exception
980 a81c53c9 Iustin Pop

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

1019 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
1020 6d2e83d5 Iustin Pop

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

1025 6d2e83d5 Iustin Pop
    """
1026 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
1027 4a89c54a Iustin Pop
    if duplicates:
1028 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
1029 4a89c54a Iustin Pop
                                      str(duplicates))
1030 4a89c54a Iustin Pop
    return d_map
1031 6d2e83d5 Iustin Pop
1032 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
1033 da4a52a3 Thomas Thrainer
  def AllocateDRBDMinor(self, node_uuids, inst_uuid):
1034 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
1035 a81c53c9 Iustin Pop

1036 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
1037 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
1038 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
1039 a81c53c9 Iustin Pop
    order as the passed nodes.
1040 a81c53c9 Iustin Pop

1041 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1042 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which we allocate minors
1043 32388e6d Iustin Pop

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

1091 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1092 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1093 da4a52a3 Thomas Thrainer
                      released
1094 a81c53c9 Iustin Pop

1095 a81c53c9 Iustin Pop
    """
1096 da4a52a3 Thomas Thrainer
    assert isinstance(inst_uuid, basestring), \
1097 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
1098 da4a52a3 Thomas Thrainer
    for key, uuid in self._temporary_drbds.items():
1099 da4a52a3 Thomas Thrainer
      if uuid == inst_uuid:
1100 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
1101 a81c53c9 Iustin Pop
1102 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
1103 da4a52a3 Thomas Thrainer
  def ReleaseDRBDMinors(self, inst_uuid):
1104 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
1105 61cf6b5e Iustin Pop

1106 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
1107 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
1108 61cf6b5e Iustin Pop
    functions.
1109 61cf6b5e Iustin Pop

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

1112 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1113 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1114 da4a52a3 Thomas Thrainer
                      released
1115 61cf6b5e Iustin Pop

1116 61cf6b5e Iustin Pop
    """
1117 da4a52a3 Thomas Thrainer
    self._UnlockedReleaseDRBDMinors(inst_uuid)
1118 61cf6b5e Iustin Pop
1119 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1120 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
1121 4a8b186a Michael Hanselmann
    """Get the configuration version.
1122 4a8b186a Michael Hanselmann

1123 4a8b186a Michael Hanselmann
    @return: Config version
1124 4a8b186a Michael Hanselmann

1125 4a8b186a Michael Hanselmann
    """
1126 4a8b186a Michael Hanselmann
    return self._config_data.version
1127 4a8b186a Michael Hanselmann
1128 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1129 4a8b186a Michael Hanselmann
  def GetClusterName(self):
1130 4a8b186a Michael Hanselmann
    """Get cluster name.
1131 4a8b186a Michael Hanselmann

1132 4a8b186a Michael Hanselmann
    @return: Cluster name
1133 4a8b186a Michael Hanselmann

1134 4a8b186a Michael Hanselmann
    """
1135 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
1136 4a8b186a Michael Hanselmann
1137 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1138 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
1139 1c3231aa Thomas Thrainer
    """Get the UUID of the master node for this cluster.
1140 4a8b186a Michael Hanselmann

1141 1c3231aa Thomas Thrainer
    @return: Master node UUID
1142 4a8b186a Michael Hanselmann

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

1150 1c3231aa Thomas Thrainer
    @return: Master node hostname
1151 1c3231aa Thomas Thrainer

1152 1c3231aa Thomas Thrainer
    """
1153 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(self._config_data.cluster.master_node)
1154 1c3231aa Thomas Thrainer
1155 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1156 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
1157 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
1158 4a8b186a Michael Hanselmann

1159 4a8b186a Michael Hanselmann
    @return: Master IP
1160 4a8b186a Michael Hanselmann

1161 4a8b186a Michael Hanselmann
    """
1162 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
1163 4a8b186a Michael Hanselmann
1164 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1165 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
1166 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
1167 4a8b186a Michael Hanselmann

1168 4a8b186a Michael Hanselmann
    """
1169 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
1170 4a8b186a Michael Hanselmann
1171 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1172 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
1173 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
1174 5a8648eb Andrea Spadaccini

1175 5a8648eb Andrea Spadaccini
    """
1176 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
1177 5a8648eb Andrea Spadaccini
1178 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1179 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
1180 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
1181 33be7576 Andrea Spadaccini

1182 33be7576 Andrea Spadaccini
    """
1183 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
1184 33be7576 Andrea Spadaccini
1185 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1186 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
1187 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
1188 4a8b186a Michael Hanselmann

1189 4a8b186a Michael Hanselmann
    """
1190 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
1191 4a8b186a Michael Hanselmann
1192 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1193 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
1194 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
1195 4b97f902 Apollon Oikonomopoulos

1196 4b97f902 Apollon Oikonomopoulos
    """
1197 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1198 4b97f902 Apollon Oikonomopoulos
1199 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1200 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1201 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1202 4a8b186a Michael Hanselmann

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

1210 c41eea6e Iustin Pop
    @rtype: string
1211 c41eea6e Iustin Pop
    @return: the rsa hostkey
1212 a8083063 Iustin Pop

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

1220 a9542a4f Thomas Thrainer
    @rtype: string
1221 a9542a4f Thomas Thrainer
    @return: the dsa hostkey
1222 a9542a4f Thomas Thrainer

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

1230 bf4af505 Apollon Oikonomopoulos
    """
1231 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1232 bf4af505 Apollon Oikonomopoulos
1233 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1234 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1235 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1236 868a98ca Manuel Franceschini

1237 868a98ca Manuel Franceschini
    @return: primary ip family
1238 868a98ca Manuel Franceschini

1239 868a98ca Manuel Franceschini
    """
1240 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1241 868a98ca Manuel Franceschini
1242 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1243 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1244 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1245 c9f4b8e6 Andrea Spadaccini

1246 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1247 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1248 c9f4b8e6 Andrea Spadaccini

1249 c9f4b8e6 Andrea Spadaccini
    """
1250 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1251 5ae4945a Iustin Pop
    result = objects.MasterNetworkParameters(
1252 1c3231aa Thomas Thrainer
      uuid=cluster.master_node, ip=cluster.master_ip,
1253 5ae4945a Iustin Pop
      netmask=cluster.master_netmask, netdev=cluster.master_netdev,
1254 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1255 c9f4b8e6 Andrea Spadaccini
1256 f9d20654 Andrea Spadaccini
    return result
1257 f9d20654 Andrea Spadaccini
1258 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1259 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1260 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1261 e11a1b77 Adeodato Simo

1262 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1263 90e99856 Adeodato Simo
    according to their default values.
1264 90e99856 Adeodato Simo

1265 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1266 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1267 e11a1b77 Adeodato Simo
    @type ec_id: string
1268 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1269 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1270 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1271 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1272 e11a1b77 Adeodato Simo
                       configuration already
1273 e11a1b77 Adeodato Simo

1274 e11a1b77 Adeodato Simo
    """
1275 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1276 e11a1b77 Adeodato Simo
    self._WriteConfig()
1277 e11a1b77 Adeodato Simo
1278 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1279 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1280 e11a1b77 Adeodato Simo

1281 e11a1b77 Adeodato Simo
    """
1282 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
1283 e11a1b77 Adeodato Simo
1284 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
1285 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
1286 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
1287 e11a1b77 Adeodato Simo
    if check_uuid:
1288 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
1289 e11a1b77 Adeodato Simo
1290 18ffc0fe Stephen Shirley
    try:
1291 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
1292 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
1293 18ffc0fe Stephen Shirley
      pass
1294 18ffc0fe Stephen Shirley
    else:
1295 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
1296 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
1297 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
1298 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
1299 18ffc0fe Stephen Shirley
1300 e11a1b77 Adeodato Simo
    group.serial_no = 1
1301 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
1302 90e99856 Adeodato Simo
    group.UpgradeConfig()
1303 e11a1b77 Adeodato Simo
1304 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
1305 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1306 e11a1b77 Adeodato Simo
1307 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1308 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
1309 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
1310 e11a1b77 Adeodato Simo

1311 e11a1b77 Adeodato Simo
    @type group_uuid: string
1312 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1313 e11a1b77 Adeodato Simo

1314 e11a1b77 Adeodato Simo
    """
1315 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1316 e11a1b77 Adeodato Simo
1317 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1318 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1319 e11a1b77 Adeodato Simo
1320 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1321 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1322 0389c42a Stephen Shirley
1323 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1324 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1325 e11a1b77 Adeodato Simo
    self._WriteConfig()
1326 e11a1b77 Adeodato Simo
1327 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1328 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1329 eaa98a04 Guido Trotter

1330 eaa98a04 Guido Trotter
    @type target: string or None
1331 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1332 eaa98a04 Guido Trotter
    @rtype: string
1333 412b3531 Guido Trotter
    @return: nodegroup UUID
1334 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1335 eaa98a04 Guido Trotter

1336 eaa98a04 Guido Trotter
    """
1337 eaa98a04 Guido Trotter
    if target is None:
1338 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1339 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1340 2ed0e208 Iustin Pop
                                   " group must be specified explicitly.")
1341 eaa98a04 Guido Trotter
      else:
1342 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1343 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1344 eaa98a04 Guido Trotter
      return target
1345 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1346 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1347 eaa98a04 Guido Trotter
        return nodegroup.uuid
1348 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1349 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1350 eaa98a04 Guido Trotter
1351 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1352 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1353 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1354 e85d8982 Stephen Shirley

1355 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1356 e85d8982 Stephen Shirley

1357 e85d8982 Stephen Shirley
    @type target: string or None
1358 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1359 e85d8982 Stephen Shirley
    @rtype: string
1360 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1361 e85d8982 Stephen Shirley

1362 e85d8982 Stephen Shirley
    """
1363 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1364 e85d8982 Stephen Shirley
1365 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1366 648e4196 Guido Trotter
    """Lookup a node group.
1367 648e4196 Guido Trotter

1368 648e4196 Guido Trotter
    @type uuid: string
1369 648e4196 Guido Trotter
    @param uuid: group UUID
1370 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1371 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1372 648e4196 Guido Trotter

1373 648e4196 Guido Trotter
    """
1374 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1375 648e4196 Guido Trotter
      return None
1376 648e4196 Guido Trotter
1377 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1378 648e4196 Guido Trotter
1379 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1380 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1381 5768e6a6 René Nussbaumer
    """Lookup a node group.
1382 5768e6a6 René Nussbaumer

1383 5768e6a6 René Nussbaumer
    @type uuid: string
1384 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1385 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1386 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1387 5768e6a6 René Nussbaumer

1388 5768e6a6 René Nussbaumer
    """
1389 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1390 5768e6a6 René Nussbaumer
1391 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1392 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1393 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1394 622444e5 Iustin Pop

1395 622444e5 Iustin Pop
    """
1396 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1397 622444e5 Iustin Pop
1398 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1399 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1400 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1401 1ac6f2ad Guido Trotter

1402 1ac6f2ad Guido Trotter
    """
1403 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1404 1ac6f2ad Guido Trotter
1405 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1406 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1407 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1408 dac81741 Michael Hanselmann

1409 dac81741 Michael Hanselmann
    """
1410 1c3231aa Thomas Thrainer
    ngfn = lambda node_uuid: self._UnlockedGetNodeInfo(node_uuid).group
1411 1c3231aa Thomas Thrainer
    return frozenset(member_uuid
1412 1c3231aa Thomas Thrainer
                     for node_uuid in nodes
1413 1c3231aa Thomas Thrainer
                     for member_uuid in
1414 1c3231aa Thomas Thrainer
                       self._UnlockedGetNodeGroup(ngfn(node_uuid)).members)
1415 dac81741 Michael Hanselmann
1416 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1417 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1418 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1419 080fbeea Michael Hanselmann

1420 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1421 080fbeea Michael Hanselmann
    @rtype: list
1422 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1423 080fbeea Michael Hanselmann

1424 080fbeea Michael Hanselmann
    """
1425 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1426 080fbeea Michael Hanselmann
1427 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1428 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1429 a8083063 Iustin Pop
    """Add an instance to the config.
1430 a8083063 Iustin Pop

1431 a8083063 Iustin Pop
    This should be used after creating a new instance.
1432 a8083063 Iustin Pop

1433 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1434 c41eea6e Iustin Pop
    @param instance: the instance object
1435 c41eea6e Iustin Pop

1436 a8083063 Iustin Pop
    """
1437 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1438 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1439 a8083063 Iustin Pop
1440 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1441 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1442 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1443 923b1523 Iustin Pop
1444 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1445 e4640214 Guido Trotter
    for nic in instance.nics:
1446 e4640214 Guido Trotter
      if nic.mac in all_macs:
1447 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1448 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1449 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1450 430b923c Iustin Pop
1451 da4a52a3 Thomas Thrainer
    self._CheckUniqueUUID(instance, include_temporary=False)
1452 e4640214 Guido Trotter
1453 b989e85d Iustin Pop
    instance.serial_no = 1
1454 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1455 da4a52a3 Thomas Thrainer
    self._config_data.instances[instance.uuid] = instance
1456 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1457 da4a52a3 Thomas Thrainer
    self._UnlockedReleaseDRBDMinors(instance.uuid)
1458 e8e079f3 Dimitris Aragiorgis
    self._UnlockedCommitTemporaryIps(ec_id)
1459 a8083063 Iustin Pop
    self._WriteConfig()
1460 a8083063 Iustin Pop
1461 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1462 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1463 430b923c Iustin Pop

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

1467 430b923c Iustin Pop
    """
1468 430b923c Iustin Pop
    if not item.uuid:
1469 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1470 da4a52a3 Thomas Thrainer
    else:
1471 da4a52a3 Thomas Thrainer
      self._CheckUniqueUUID(item, include_temporary=True)
1472 da4a52a3 Thomas Thrainer
1473 da4a52a3 Thomas Thrainer
  def _CheckUniqueUUID(self, item, include_temporary):
1474 da4a52a3 Thomas Thrainer
    """Checks that the UUID of the given object is unique.
1475 da4a52a3 Thomas Thrainer

1476 da4a52a3 Thomas Thrainer
    @param item: the instance or node to be checked
1477 da4a52a3 Thomas Thrainer
    @param include_temporary: whether temporarily generated UUID's should be
1478 da4a52a3 Thomas Thrainer
              included in the check. If the UUID of the item to be checked is
1479 da4a52a3 Thomas Thrainer
              a temporarily generated one, this has to be C{False}.
1480 da4a52a3 Thomas Thrainer

1481 da4a52a3 Thomas Thrainer
    """
1482 da4a52a3 Thomas Thrainer
    if not item.uuid:
1483 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("'%s' must have an UUID" % (item.name,))
1484 da4a52a3 Thomas Thrainer
    if item.uuid in self._AllIDs(include_temporary=include_temporary):
1485 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1486 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1487 430b923c Iustin Pop
1488 da4a52a3 Thomas Thrainer
  def _SetInstanceStatus(self, inst_uuid, status, disks_active):
1489 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1490 a8083063 Iustin Pop

1491 a8083063 Iustin Pop
    """
1492 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1493 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1494 da4a52a3 Thomas Thrainer
                                      inst_uuid)
1495 da4a52a3 Thomas Thrainer
    instance = self._config_data.instances[inst_uuid]
1496 1d4a4b26 Thomas Thrainer
1497 1d4a4b26 Thomas Thrainer
    if status is None:
1498 1d4a4b26 Thomas Thrainer
      status = instance.admin_state
1499 1d4a4b26 Thomas Thrainer
    if disks_active is None:
1500 1d4a4b26 Thomas Thrainer
      disks_active = instance.disks_active
1501 1d4a4b26 Thomas Thrainer
1502 1d4a4b26 Thomas Thrainer
    assert status in constants.ADMINST_ALL, \
1503 1d4a4b26 Thomas Thrainer
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1504 1d4a4b26 Thomas Thrainer
1505 1d4a4b26 Thomas Thrainer
    if instance.admin_state != status or \
1506 1d4a4b26 Thomas Thrainer
       instance.disks_active != disks_active:
1507 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1508 1d4a4b26 Thomas Thrainer
      instance.disks_active = disks_active
1509 b989e85d Iustin Pop
      instance.serial_no += 1
1510 d693c864 Iustin Pop
      instance.mtime = time.time()
1511 455a3445 Iustin Pop
      self._WriteConfig()
1512 a8083063 Iustin Pop
1513 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1514 da4a52a3 Thomas Thrainer
  def MarkInstanceUp(self, inst_uuid):
1515 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1516 6a408fb2 Iustin Pop

1517 1d4a4b26 Thomas Thrainer
    This also sets the instance disks active flag.
1518 1d4a4b26 Thomas Thrainer

1519 6a408fb2 Iustin Pop
    """
1520 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_UP, True)
1521 57de31c0 Agata Murawska
1522 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1523 da4a52a3 Thomas Thrainer
  def MarkInstanceOffline(self, inst_uuid):
1524 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1525 57de31c0 Agata Murawska

1526 1d4a4b26 Thomas Thrainer
    This also clears the instance disks active flag.
1527 1d4a4b26 Thomas Thrainer

1528 57de31c0 Agata Murawska
    """
1529 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_OFFLINE, False)
1530 6a408fb2 Iustin Pop
1531 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1532 da4a52a3 Thomas Thrainer
  def RemoveInstance(self, inst_uuid):
1533 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1534 a8083063 Iustin Pop

1535 a8083063 Iustin Pop
    """
1536 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1537 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1538 f396ad8c Vangelis Koukis
1539 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1540 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1541 da4a52a3 Thomas Thrainer
    inst = self._config_data.instances[inst_uuid]
1542 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1543 f396ad8c Vangelis Koukis
    if network_port is not None:
1544 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1545 f396ad8c Vangelis Koukis
1546 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1547 ced51149 Dimitris Aragiorgis
1548 ced51149 Dimitris Aragiorgis
    for nic in instance.nics:
1549 9394f4d1 Dimitris Aragiorgis
      if nic.network and nic.ip:
1550 1b68f268 Helga Velroyen
        # Return all IP addresses to the respective address pools
1551 9394f4d1 Dimitris Aragiorgis
        self._UnlockedCommitIp(constants.RELEASE_ACTION, nic.network, nic.ip)
1552 ced51149 Dimitris Aragiorgis
1553 da4a52a3 Thomas Thrainer
    del self._config_data.instances[inst_uuid]
1554 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1555 a8083063 Iustin Pop
    self._WriteConfig()
1556 a8083063 Iustin Pop
1557 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1558 da4a52a3 Thomas Thrainer
  def RenameInstance(self, inst_uuid, new_name):
1559 fc95f88f Iustin Pop
    """Rename an instance.
1560 fc95f88f Iustin Pop

1561 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1562 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1563 fc95f88f Iustin Pop
    rename.
1564 fc95f88f Iustin Pop

1565 fc95f88f Iustin Pop
    """
1566 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1567 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1568 ea642319 Michael Hanselmann
1569 da4a52a3 Thomas Thrainer
    inst = self._config_data.instances[inst_uuid]
1570 fc95f88f Iustin Pop
    inst.name = new_name
1571 b23c4333 Manuel Franceschini
1572 ea642319 Michael Hanselmann
    for (idx, disk) in enumerate(inst.disks):
1573 cd3b4ff4 Helga Velroyen
      if disk.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1574 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1575 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1576 ea642319 Michael Hanselmann
        disk.logical_id = (disk.logical_id[0],
1577 ea642319 Michael Hanselmann
                           utils.PathJoin(file_storage_dir, inst.name,
1578 ea642319 Michael Hanselmann
                                          "disk%s" % idx))
1579 ea642319 Michael Hanselmann
        disk.physical_id = disk.logical_id
1580 ea642319 Michael Hanselmann
1581 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1582 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1583 1fc34c48 Michael Hanselmann
1584 fc95f88f Iustin Pop
    self._WriteConfig()
1585 fc95f88f Iustin Pop
1586 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1587 da4a52a3 Thomas Thrainer
  def MarkInstanceDown(self, inst_uuid):
1588 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1589 a8083063 Iustin Pop

1590 1d4a4b26 Thomas Thrainer
    This does not touch the instance disks active flag, as shut down instances
1591 1d4a4b26 Thomas Thrainer
    can still have active disks.
1592 1d4a4b26 Thomas Thrainer

1593 1d4a4b26 Thomas Thrainer
    """
1594 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None)
1595 1d4a4b26 Thomas Thrainer
1596 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1597 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksActive(self, inst_uuid):
1598 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks active.
1599 1d4a4b26 Thomas Thrainer

1600 1d4a4b26 Thomas Thrainer
    """
1601 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, True)
1602 1d4a4b26 Thomas Thrainer
1603 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1604 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksInactive(self, inst_uuid):
1605 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks inactive.
1606 1d4a4b26 Thomas Thrainer

1607 a8083063 Iustin Pop
    """
1608 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, False)
1609 a8083063 Iustin Pop
1610 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1611 94bbfece Iustin Pop
    """Get the list of instances.
1612 94bbfece Iustin Pop

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

1615 94bbfece Iustin Pop
    """
1616 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1617 94bbfece Iustin Pop
1618 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1619 a8083063 Iustin Pop
  def GetInstanceList(self):
1620 a8083063 Iustin Pop
    """Get the list of instances.
1621 a8083063 Iustin Pop

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

1624 a8083063 Iustin Pop
    """
1625 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1626 a8083063 Iustin Pop
1627 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1628 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1629 a8083063 Iustin Pop

1630 a8083063 Iustin Pop
    """
1631 da4a52a3 Thomas Thrainer
    # Locking is done in L{ConfigWriter.GetAllInstancesInfo}
1632 da4a52a3 Thomas Thrainer
    all_insts = self.GetAllInstancesInfo().values()
1633 da4a52a3 Thomas Thrainer
    expanded_name = _MatchNameComponentIgnoreCase(
1634 da4a52a3 Thomas Thrainer
                      short_name, [inst.name for inst in all_insts])
1635 da4a52a3 Thomas Thrainer
1636 da4a52a3 Thomas Thrainer
    if expanded_name is not None:
1637 da4a52a3 Thomas Thrainer
      # there has to be exactly one instance with that name
1638 da4a52a3 Thomas Thrainer
      inst = (filter(lambda n: n.name == expanded_name, all_insts)[0])
1639 da4a52a3 Thomas Thrainer
      return (inst.uuid, inst.name)
1640 da4a52a3 Thomas Thrainer
    else:
1641 738436bf Thomas Thrainer
      return (None, None)
1642 a8083063 Iustin Pop
1643 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceInfo(self, inst_uuid):
1644 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
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 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1650 94bbfece Iustin Pop
      return None
1651 94bbfece Iustin Pop
1652 da4a52a3 Thomas Thrainer
    return self._config_data.instances[inst_uuid]
1653 94bbfece Iustin Pop
1654 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1655 da4a52a3 Thomas Thrainer
  def GetInstanceInfo(self, inst_uuid):
1656 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1657 a8083063 Iustin Pop

1658 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1659 a8083063 Iustin Pop
    an instance are taken from the live systems.
1660 a8083063 Iustin Pop

1661 da4a52a3 Thomas Thrainer
    @param inst_uuid: UUID of the instance
1662 a8083063 Iustin Pop

1663 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1664 c41eea6e Iustin Pop
    @return: the instance object
1665 a8083063 Iustin Pop

1666 a8083063 Iustin Pop
    """
1667 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfo(inst_uuid)
1668 a8083063 Iustin Pop
1669 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1670 da4a52a3 Thomas Thrainer
  def GetInstanceNodeGroups(self, inst_uuid, primary_only=False):
1671 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1672 2674690b Michael Hanselmann

1673 2674690b Michael Hanselmann
    @rtype: frozenset
1674 2674690b Michael Hanselmann

1675 2674690b Michael Hanselmann
    """
1676 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1677 2674690b Michael Hanselmann
    if not instance:
1678 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1679 2674690b Michael Hanselmann
1680 2674690b Michael Hanselmann
    if primary_only:
1681 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1682 2674690b Michael Hanselmann
    else:
1683 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1684 2674690b Michael Hanselmann
1685 1c3231aa Thomas Thrainer
    return frozenset(self._UnlockedGetNodeInfo(node_uuid).group
1686 1c3231aa Thomas Thrainer
                     for node_uuid in nodes)
1687 2674690b Michael Hanselmann
1688 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1689 da4a52a3 Thomas Thrainer
  def GetInstanceNetworks(self, inst_uuid):
1690 922610c9 Dimitris Aragiorgis
    """Returns set of network UUIDs for instance's nics.
1691 922610c9 Dimitris Aragiorgis

1692 922610c9 Dimitris Aragiorgis
    @rtype: frozenset
1693 922610c9 Dimitris Aragiorgis

1694 922610c9 Dimitris Aragiorgis
    """
1695 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1696 922610c9 Dimitris Aragiorgis
    if not instance:
1697 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1698 922610c9 Dimitris Aragiorgis
1699 922610c9 Dimitris Aragiorgis
    networks = set()
1700 922610c9 Dimitris Aragiorgis
    for nic in instance.nics:
1701 922610c9 Dimitris Aragiorgis
      if nic.network:
1702 922610c9 Dimitris Aragiorgis
        networks.add(nic.network)
1703 922610c9 Dimitris Aragiorgis
1704 922610c9 Dimitris Aragiorgis
    return frozenset(networks)
1705 922610c9 Dimitris Aragiorgis
1706 922610c9 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
1707 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfo(self, inst_uuids):
1708 da4a52a3 Thomas Thrainer
    """Get the configuration of multiple instances.
1709 da4a52a3 Thomas Thrainer

1710 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs
1711 da4a52a3 Thomas Thrainer
    @rtype: list
1712 da4a52a3 Thomas Thrainer
    @return: list of tuples (instance UUID, instance_info), where
1713 da4a52a3 Thomas Thrainer
        instance_info is what would GetInstanceInfo return for the
1714 da4a52a3 Thomas Thrainer
        node, while keeping the original order
1715 da4a52a3 Thomas Thrainer

1716 da4a52a3 Thomas Thrainer
    """
1717 da4a52a3 Thomas Thrainer
    return [(uuid, self._UnlockedGetInstanceInfo(uuid)) for uuid in inst_uuids]
1718 da4a52a3 Thomas Thrainer
1719 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1720 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfoByName(self, inst_names):
1721 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1722 71333cb9 Iustin Pop

1723 da4a52a3 Thomas Thrainer
    @param inst_names: list of instance names
1724 71333cb9 Iustin Pop
    @rtype: list
1725 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1726 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1727 71333cb9 Iustin Pop
        node, while keeping the original order
1728 71333cb9 Iustin Pop

1729 71333cb9 Iustin Pop
    """
1730 da4a52a3 Thomas Thrainer
    result = []
1731 da4a52a3 Thomas Thrainer
    for name in inst_names:
1732 da4a52a3 Thomas Thrainer
      instance = self._UnlockedGetInstanceInfoByName(name)
1733 da4a52a3 Thomas Thrainer
      result.append((instance.uuid, instance))
1734 da4a52a3 Thomas Thrainer
    return result
1735 71333cb9 Iustin Pop
1736 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1737 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1738 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1739 0b2de758 Iustin Pop

1740 0b2de758 Iustin Pop
    @rtype: dict
1741 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1742 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1743 0b2de758 Iustin Pop

1744 0b2de758 Iustin Pop
    """
1745 da4a52a3 Thomas Thrainer
    return self._UnlockedGetAllInstancesInfo()
1746 da4a52a3 Thomas Thrainer
1747 da4a52a3 Thomas Thrainer
  def _UnlockedGetAllInstancesInfo(self):
1748 da4a52a3 Thomas Thrainer
    my_dict = dict([(inst_uuid, self._UnlockedGetInstanceInfo(inst_uuid))
1749 da4a52a3 Thomas Thrainer
                    for inst_uuid in self._UnlockedGetInstanceList()])
1750 0b2de758 Iustin Pop
    return my_dict
1751 0b2de758 Iustin Pop
1752 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1753 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1754 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1755 cc19798f Michael Hanselmann

1756 cc19798f Michael Hanselmann
    @type filter_fn: callable
1757 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1758 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1759 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1760 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1761 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1762 cc19798f Michael Hanselmann

1763 cc19798f Michael Hanselmann
    """
1764 da4a52a3 Thomas Thrainer
    return dict((uuid, inst)
1765 da4a52a3 Thomas Thrainer
                for (uuid, inst) in self._config_data.instances.items()
1766 cc19798f Michael Hanselmann
                if filter_fn(inst))
1767 cc19798f Michael Hanselmann
1768 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1769 da4a52a3 Thomas Thrainer
  def GetInstanceInfoByName(self, inst_name):
1770 da4a52a3 Thomas Thrainer
    """Get the L{objects.Instance} object for a named instance.
1771 da4a52a3 Thomas Thrainer

1772 da4a52a3 Thomas Thrainer
    @param inst_name: name of the instance to get information for
1773 da4a52a3 Thomas Thrainer
    @type inst_name: string
1774 da4a52a3 Thomas Thrainer
    @return: the corresponding L{objects.Instance} instance or None if no
1775 da4a52a3 Thomas Thrainer
          information is available
1776 da4a52a3 Thomas Thrainer

1777 da4a52a3 Thomas Thrainer
    """
1778 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfoByName(inst_name)
1779 da4a52a3 Thomas Thrainer
1780 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceInfoByName(self, inst_name):
1781 da4a52a3 Thomas Thrainer
    for inst in self._UnlockedGetAllInstancesInfo().values():
1782 da4a52a3 Thomas Thrainer
      if inst.name == inst_name:
1783 da4a52a3 Thomas Thrainer
        return inst
1784 da4a52a3 Thomas Thrainer
    return None
1785 da4a52a3 Thomas Thrainer
1786 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceName(self, inst_uuid):
1787 da4a52a3 Thomas Thrainer
    inst_info = self._UnlockedGetInstanceInfo(inst_uuid)
1788 da4a52a3 Thomas Thrainer
    if inst_info is None:
1789 da4a52a3 Thomas Thrainer
      raise errors.OpExecError("Unknown instance: %s" % inst_uuid)
1790 da4a52a3 Thomas Thrainer
    return inst_info.name
1791 da4a52a3 Thomas Thrainer
1792 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1793 da4a52a3 Thomas Thrainer
  def GetInstanceName(self, inst_uuid):
1794 da4a52a3 Thomas Thrainer
    """Gets the instance name for the passed instance.
1795 da4a52a3 Thomas Thrainer

1796 da4a52a3 Thomas Thrainer
    @param inst_uuid: instance UUID to get name for
1797 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1798 da4a52a3 Thomas Thrainer
    @rtype: string
1799 da4a52a3 Thomas Thrainer
    @return: instance name
1800 da4a52a3 Thomas Thrainer

1801 da4a52a3 Thomas Thrainer
    """
1802 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceName(inst_uuid)
1803 da4a52a3 Thomas Thrainer
1804 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1805 da4a52a3 Thomas Thrainer
  def GetInstanceNames(self, inst_uuids):
1806 da4a52a3 Thomas Thrainer
    """Gets the instance names for the passed list of nodes.
1807 da4a52a3 Thomas Thrainer

1808 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs to get names for
1809 da4a52a3 Thomas Thrainer
    @type inst_uuids: list of strings
1810 da4a52a3 Thomas Thrainer
    @rtype: list of strings
1811 da4a52a3 Thomas Thrainer
    @return: list of instance names
1812 da4a52a3 Thomas Thrainer

1813 da4a52a3 Thomas Thrainer
    """
1814 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceNames(inst_uuids)
1815 da4a52a3 Thomas Thrainer
1816 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceNames(self, inst_uuids):
1817 da4a52a3 Thomas Thrainer
    return [self._UnlockedGetInstanceName(uuid) for uuid in inst_uuids]
1818 da4a52a3 Thomas Thrainer
1819 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1820 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1821 a8083063 Iustin Pop
    """Add a node to the configuration.
1822 a8083063 Iustin Pop

1823 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1824 c41eea6e Iustin Pop
    @param node: a Node instance
1825 a8083063 Iustin Pop

1826 a8083063 Iustin Pop
    """
1827 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1828 d8470559 Michael Hanselmann
1829 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1830 430b923c Iustin Pop
1831 b989e85d Iustin Pop
    node.serial_no = 1
1832 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1833 1c3231aa Thomas Thrainer
    self._UnlockedAddNodeToGroup(node.uuid, node.group)
1834 1c3231aa Thomas Thrainer
    self._config_data.nodes[node.uuid] = node
1835 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1836 a8083063 Iustin Pop
    self._WriteConfig()
1837 a8083063 Iustin Pop
1838 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1839 1c3231aa Thomas Thrainer
  def RemoveNode(self, node_uuid):
1840 a8083063 Iustin Pop
    """Remove a node from the configuration.
1841 a8083063 Iustin Pop

1842 a8083063 Iustin Pop
    """
1843 1c3231aa Thomas Thrainer
    logging.info("Removing node %s from configuration", node_uuid)
1844 d8470559 Michael Hanselmann
1845 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
1846 1c3231aa Thomas Thrainer
      raise errors.ConfigurationError("Unknown node '%s'" % node_uuid)
1847 a8083063 Iustin Pop
1848 1c3231aa Thomas Thrainer
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_uuid])
1849 1c3231aa Thomas Thrainer
    del self._config_data.nodes[node_uuid]
1850 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1851 a8083063 Iustin Pop
    self._WriteConfig()
1852 a8083063 Iustin Pop
1853 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1854 1c3231aa Thomas Thrainer
    """Attempt to expand an incomplete node name into a node UUID.
1855 a8083063 Iustin Pop

1856 a8083063 Iustin Pop
    """
1857 1c3231aa Thomas Thrainer
    # Locking is done in L{ConfigWriter.GetAllNodesInfo}
1858 1c3231aa Thomas Thrainer
    all_nodes = self.GetAllNodesInfo().values()
1859 1c3231aa Thomas Thrainer
    expanded_name = _MatchNameComponentIgnoreCase(
1860 1c3231aa Thomas Thrainer
                      short_name, [node.name for node in all_nodes])
1861 a8083063 Iustin Pop
1862 1c3231aa Thomas Thrainer
    if expanded_name is not None:
1863 da4a52a3 Thomas Thrainer
      # there has to be exactly one node with that name
1864 1c3231aa Thomas Thrainer
      node = (filter(lambda n: n.name == expanded_name, all_nodes)[0])
1865 1c3231aa Thomas Thrainer
      return (node.uuid, node.name)
1866 1c3231aa Thomas Thrainer
    else:
1867 738436bf Thomas Thrainer
      return (None, None)
1868 1c3231aa Thomas Thrainer
1869 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfo(self, node_uuid):
1870 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1871 a8083063 Iustin Pop

1872 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1873 c41eea6e Iustin Pop
    held.
1874 f78ede4e Guido Trotter

1875 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1876 a8083063 Iustin Pop

1877 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1878 c41eea6e Iustin Pop
    @return: the node object
1879 a8083063 Iustin Pop

1880 a8083063 Iustin Pop
    """
1881 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
1882 a8083063 Iustin Pop
      return None
1883 a8083063 Iustin Pop
1884 1c3231aa Thomas Thrainer
    return self._config_data.nodes[node_uuid]
1885 a8083063 Iustin Pop
1886 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1887 1c3231aa Thomas Thrainer
  def GetNodeInfo(self, node_uuid):
1888 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1889 f78ede4e Guido Trotter

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

1892 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1893 c41eea6e Iustin Pop

1894 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1895 c41eea6e Iustin Pop
    @return: the node object
1896 f78ede4e Guido Trotter

1897 f78ede4e Guido Trotter
    """
1898 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfo(node_uuid)
1899 f78ede4e Guido Trotter
1900 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1901 1c3231aa Thomas Thrainer
  def GetNodeInstances(self, node_uuid):
1902 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1903 8bf9e9a5 Iustin Pop

1904 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1905 8bf9e9a5 Iustin Pop

1906 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1907 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1908 8bf9e9a5 Iustin Pop

1909 8bf9e9a5 Iustin Pop
    """
1910 8bf9e9a5 Iustin Pop
    pri = []
1911 8bf9e9a5 Iustin Pop
    sec = []
1912 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1913 1c3231aa Thomas Thrainer
      if inst.primary_node == node_uuid:
1914 da4a52a3 Thomas Thrainer
        pri.append(inst.uuid)
1915 1c3231aa Thomas Thrainer
      if node_uuid in inst.secondary_nodes:
1916 da4a52a3 Thomas Thrainer
        sec.append(inst.uuid)
1917 8bf9e9a5 Iustin Pop
    return (pri, sec)
1918 8bf9e9a5 Iustin Pop
1919 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1920 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1921 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1922 c71b049c Michael Hanselmann

1923 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1924 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1925 c71b049c Michael Hanselmann
    @rtype: frozenset
1926 da4a52a3 Thomas Thrainer
    @return: List of instance UUIDs in node group
1927 c71b049c Michael Hanselmann

1928 c71b049c Michael Hanselmann
    """
1929 c71b049c Michael Hanselmann
    if primary_only:
1930 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1931 c71b049c Michael Hanselmann
    else:
1932 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1933 c71b049c Michael Hanselmann
1934 da4a52a3 Thomas Thrainer
    return frozenset(inst.uuid
1935 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1936 1c3231aa Thomas Thrainer
                     for node_uuid in nodes_fn(inst)
1937 1c3231aa Thomas Thrainer
                     if self._UnlockedGetNodeInfo(node_uuid).group == uuid)
1938 c71b049c Michael Hanselmann
1939 def6577f Helga Velroyen
  def _UnlockedGetHvparamsString(self, hvname):
1940 def6577f Helga Velroyen
    """Return the string representation of the list of hyervisor parameters of
1941 def6577f Helga Velroyen
    the given hypervisor.
1942 def6577f Helga Velroyen

1943 def6577f Helga Velroyen
    @see: C{GetHvparams}
1944 def6577f Helga Velroyen

1945 def6577f Helga Velroyen
    """
1946 def6577f Helga Velroyen
    result = ""
1947 def6577f Helga Velroyen
    hvparams = self._config_data.cluster.hvparams[hvname]
1948 def6577f Helga Velroyen
    for key in hvparams:
1949 def6577f Helga Velroyen
      result += "%s=%s\n" % (key, hvparams[key])
1950 def6577f Helga Velroyen
    return result
1951 def6577f Helga Velroyen
1952 def6577f Helga Velroyen
  @locking.ssynchronized(_config_lock, shared=1)
1953 def6577f Helga Velroyen
  def GetHvparamsString(self, hvname):
1954 def6577f Helga Velroyen
    """Return the hypervisor parameters of the given hypervisor.
1955 def6577f Helga Velroyen

1956 def6577f Helga Velroyen
    @type hvname: string
1957 def6577f Helga Velroyen
    @param hvname: name of a hypervisor
1958 def6577f Helga Velroyen
    @rtype: string
1959 def6577f Helga Velroyen
    @return: string containing key-value-pairs, one pair on each line;
1960 def6577f Helga Velroyen
      format: KEY=VALUE
1961 def6577f Helga Velroyen

1962 def6577f Helga Velroyen
    """
1963 def6577f Helga Velroyen
    return self._UnlockedGetHvparamsString(hvname)
1964 def6577f Helga Velroyen
1965 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1966 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1967 a8083063 Iustin Pop

1968 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1969 c41eea6e Iustin Pop
    held.
1970 c41eea6e Iustin Pop

1971 c41eea6e Iustin Pop
    @rtype: list
1972 f78ede4e Guido Trotter

1973 a8083063 Iustin Pop
    """
1974 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1975 a8083063 Iustin Pop
1976 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1977 f78ede4e Guido Trotter
  def GetNodeList(self):
1978 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1979 f78ede4e Guido Trotter

1980 f78ede4e Guido Trotter
    """
1981 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1982 f78ede4e Guido Trotter
1983 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1984 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1985 94a02bb5 Iustin Pop

1986 94a02bb5 Iustin Pop
    """
1987 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1988 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1989 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.offline]
1990 94a02bb5 Iustin Pop
1991 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1992 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1993 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1994 6819dc49 Iustin Pop

1995 6819dc49 Iustin Pop
    """
1996 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1997 6819dc49 Iustin Pop
1998 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1999 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
2000 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
2001 075b62ca Iustin Pop

2002 075b62ca Iustin Pop
    """
2003 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
2004 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
2005 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if node.vm_capable]
2006 075b62ca Iustin Pop
2007 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2008 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
2009 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
2010 8bf9e9a5 Iustin Pop

2011 8bf9e9a5 Iustin Pop
    """
2012 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
2013 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
2014 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.vm_capable]
2015 8bf9e9a5 Iustin Pop
2016 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2017 1c3231aa Thomas Thrainer
  def GetMultiNodeInfo(self, node_uuids):
2018 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
2019 f5eaa3c1 Iustin Pop

2020 1c3231aa Thomas Thrainer
    @param node_uuids: list of node UUIDs
2021 f5eaa3c1 Iustin Pop
    @rtype: list
2022 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
2023 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
2024 f5eaa3c1 Iustin Pop
        order
2025 f5eaa3c1 Iustin Pop

2026 f5eaa3c1 Iustin Pop
    """
2027 1c3231aa Thomas Thrainer
    return [(uuid, self._UnlockedGetNodeInfo(uuid)) for uuid in node_uuids]
2028 1c3231aa Thomas Thrainer
2029 1c3231aa Thomas Thrainer
  def _UnlockedGetAllNodesInfo(self):
2030 1c3231aa Thomas Thrainer
    """Gets configuration of all nodes.
2031 1c3231aa Thomas Thrainer

2032 1c3231aa Thomas Thrainer
    @note: See L{GetAllNodesInfo}
2033 1c3231aa Thomas Thrainer

2034 1c3231aa Thomas Thrainer
    """
2035 1c3231aa Thomas Thrainer
    return dict([(node_uuid, self._UnlockedGetNodeInfo(node_uuid))
2036 1c3231aa Thomas Thrainer
                 for node_uuid in self._UnlockedGetNodeList()])
2037 f5eaa3c1 Iustin Pop
2038 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2039 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
2040 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
2041 d65e5776 Iustin Pop

2042 d65e5776 Iustin Pop
    @rtype: dict
2043 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
2044 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
2045 d65e5776 Iustin Pop

2046 d65e5776 Iustin Pop
    """
2047 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
2048 ee14d800 Michael Hanselmann
2049 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfoByName(self, node_name):
2050 1c3231aa Thomas Thrainer
    for node in self._UnlockedGetAllNodesInfo().values():
2051 1c3231aa Thomas Thrainer
      if node.name == node_name:
2052 1c3231aa Thomas Thrainer
        return node
2053 1c3231aa Thomas Thrainer
    return None
2054 ee14d800 Michael Hanselmann
2055 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2056 1c3231aa Thomas Thrainer
  def GetNodeInfoByName(self, node_name):
2057 1c3231aa Thomas Thrainer
    """Get the L{objects.Node} object for a named node.
2058 1c3231aa Thomas Thrainer

2059 1c3231aa Thomas Thrainer
    @param node_name: name of the node to get information for
2060 1c3231aa Thomas Thrainer
    @type node_name: string
2061 1c3231aa Thomas Thrainer
    @return: the corresponding L{objects.Node} instance or None if no
2062 1c3231aa Thomas Thrainer
          information is available
2063 1c3231aa Thomas Thrainer

2064 1c3231aa Thomas Thrainer
    """
2065 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfoByName(node_name)
2066 1c3231aa Thomas Thrainer
2067 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeName(self, node_spec):
2068 1c3231aa Thomas Thrainer
    if isinstance(node_spec, objects.Node):
2069 1c3231aa Thomas Thrainer
      return node_spec.name
2070 1c3231aa Thomas Thrainer
    elif isinstance(node_spec, basestring):
2071 1c3231aa Thomas Thrainer
      node_info = self._UnlockedGetNodeInfo(node_spec)
2072 1c3231aa Thomas Thrainer
      if node_info is None:
2073 1c3231aa Thomas Thrainer
        raise errors.OpExecError("Unknown node: %s" % node_spec)
2074 1c3231aa Thomas Thrainer
      return node_info.name
2075 1c3231aa Thomas Thrainer
    else:
2076 1c3231aa Thomas Thrainer
      raise errors.ProgrammerError("Can't handle node spec '%s'" % node_spec)
2077 1c3231aa Thomas Thrainer
2078 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2079 1c3231aa Thomas Thrainer
  def GetNodeName(self, node_spec):
2080 1c3231aa Thomas Thrainer
    """Gets the node name for the passed node.
2081 1c3231aa Thomas Thrainer

2082 1c3231aa Thomas Thrainer
    @param node_spec: node to get names for
2083 1c3231aa Thomas Thrainer
    @type node_spec: either node UUID or a L{objects.Node} object
2084 1c3231aa Thomas Thrainer
    @rtype: string
2085 1c3231aa Thomas Thrainer
    @return: node name
2086 1c3231aa Thomas Thrainer

2087 1c3231aa Thomas Thrainer
    """
2088 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(node_spec)
2089 1c3231aa Thomas Thrainer
2090 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeNames(self, node_specs):
2091 1c3231aa Thomas Thrainer
    return [self._UnlockedGetNodeName(node_spec) for node_spec in node_specs]
2092 1c3231aa Thomas Thrainer
2093 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2094 1c3231aa Thomas Thrainer
  def GetNodeNames(self, node_specs):
2095 1c3231aa Thomas Thrainer
    """Gets the node names for the passed list of nodes.
2096 1c3231aa Thomas Thrainer

2097 1c3231aa Thomas Thrainer
    @param node_specs: list of nodes to get names for
2098 1c3231aa Thomas Thrainer
    @type node_specs: list of either node UUIDs or L{objects.Node} objects
2099 1c3231aa Thomas Thrainer
    @rtype: list of strings
2100 1c3231aa Thomas Thrainer
    @return: list of node names
2101 ee14d800 Michael Hanselmann

2102 ee14d800 Michael Hanselmann
    """
2103 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeNames(node_specs)
2104 d65e5776 Iustin Pop
2105 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
2106 1c3231aa Thomas Thrainer
  def GetNodeGroupsFromNodes(self, node_uuids):
2107 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
2108 9d5b1371 Michael Hanselmann

2109 1c3231aa Thomas Thrainer
    @type node_uuids: list of string
2110 1c3231aa Thomas Thrainer
    @param node_uuids: List of node UUIDs
2111 9d5b1371 Michael Hanselmann
    @rtype: frozenset
2112 9d5b1371 Michael Hanselmann

2113 9d5b1371 Michael Hanselmann
    """
2114 1c3231aa Thomas Thrainer
    return frozenset(self._UnlockedGetNodeInfo(uuid).group
2115 1c3231aa Thomas Thrainer
                     for uuid in node_uuids)
2116 9d5b1371 Michael Hanselmann
2117 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
2118 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
2119 ec0292f1 Iustin Pop

2120 23f06b2b Iustin Pop
    @type exceptions: list
2121 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
2122 ec0292f1 Iustin Pop
    @rtype: tuple
2123 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
2124 ec0292f1 Iustin Pop

2125 ec0292f1 Iustin Pop
    """
2126 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
2127 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
2128 1c3231aa Thomas Thrainer
      if exceptions and node.uuid in exceptions:
2129 23f06b2b Iustin Pop
        continue
2130 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
2131 ec0292f1 Iustin Pop
        mc_max += 1
2132 ec0292f1 Iustin Pop
      if node.master_candidate:
2133 ec0292f1 Iustin Pop
        mc_now += 1
2134 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
2135 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
2136 ec0292f1 Iustin Pop
2137 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2138 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
2139 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
2140 ec0292f1 Iustin Pop

2141 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
2142 ec0292f1 Iustin Pop

2143 23f06b2b Iustin Pop
    @type exceptions: list
2144 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
2145 ec0292f1 Iustin Pop
    @rtype: tuple
2146 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
2147 ec0292f1 Iustin Pop

2148 ec0292f1 Iustin Pop
    """
2149 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
2150 ec0292f1 Iustin Pop
2151 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
2152 1c3231aa Thomas Thrainer
  def MaintainCandidatePool(self, exception_node_uuids):
2153 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
2154 ec0292f1 Iustin Pop

2155 1c3231aa Thomas Thrainer
    @type exception_node_uuids: list
2156 1c3231aa Thomas Thrainer
    @param exception_node_uuids: if passed, list of nodes that should be ignored
2157 ec0292f1 Iustin Pop
    @rtype: list
2158 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
2159 ec0292f1 Iustin Pop

2160 ec0292f1 Iustin Pop
    """
2161 1c3231aa Thomas Thrainer
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(
2162 1c3231aa Thomas Thrainer
                          exception_node_uuids)
2163 ec0292f1 Iustin Pop
    mod_list = []
2164 ec0292f1 Iustin Pop
    if mc_now < mc_max:
2165 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
2166 ec0292f1 Iustin Pop
      random.shuffle(node_list)
2167 1c3231aa Thomas Thrainer
      for uuid in node_list:
2168 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
2169 ec0292f1 Iustin Pop
          break
2170 1c3231aa Thomas Thrainer
        node = self._config_data.nodes[uuid]
2171 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
2172 1c3231aa Thomas Thrainer
            node.uuid in exception_node_uuids or not node.master_capable):
2173 ec0292f1 Iustin Pop
          continue
2174 ee513a66 Iustin Pop
        mod_list.append(node)
2175 ec0292f1 Iustin Pop
        node.master_candidate = True
2176 ec0292f1 Iustin Pop
        node.serial_no += 1
2177 ec0292f1 Iustin Pop
        mc_now += 1
2178 ec0292f1 Iustin Pop
      if mc_now != mc_max:
2179 ec0292f1 Iustin Pop
        # this should not happen
2180 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
2181 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
2182 ec0292f1 Iustin Pop
      if mod_list:
2183 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
2184 ec0292f1 Iustin Pop
        self._WriteConfig()
2185 ec0292f1 Iustin Pop
2186 ec0292f1 Iustin Pop
    return mod_list
2187 ec0292f1 Iustin Pop
2188 1c3231aa Thomas Thrainer
  def _UnlockedAddNodeToGroup(self, node_uuid, nodegroup_uuid):
2189 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
2190 190e3cb6 Guido Trotter

2191 190e3cb6 Guido Trotter
    """
2192 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
2193 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
2194 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
2195 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
2196 190e3cb6 Guido Trotter
      # is not found anymore.
2197 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
2198 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodegroups[nodegroup_uuid].members:
2199 1c3231aa Thomas Thrainer
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_uuid)
2200 190e3cb6 Guido Trotter
2201 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
2202 190e3cb6 Guido Trotter
    """Remove a given node from its group.
2203 190e3cb6 Guido Trotter

2204 190e3cb6 Guido Trotter
    """
2205 f936c153 Iustin Pop
    nodegroup = node.group
2206 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
2207 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
2208 1c3231aa Thomas Thrainer
                      " (while being removed from it)", node.uuid, nodegroup)
2209 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
2210 1c3231aa Thomas Thrainer
    if node.uuid not in nodegroup_obj.members:
2211 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
2212 1c3231aa Thomas Thrainer
                      " (while being removed from it)", node.uuid, nodegroup)
2213 190e3cb6 Guido Trotter
    else:
2214 1c3231aa Thomas Thrainer
      nodegroup_obj.members.remove(node.uuid)
2215 190e3cb6 Guido Trotter
2216 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
2217 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
2218 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
2219 54c31fd3 Michael Hanselmann

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

2223 54c31fd3 Michael Hanselmann
    """
2224 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
2225 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
2226 54c31fd3 Michael Hanselmann
2227 54c31fd3 Michael Hanselmann
    resmod = []
2228 54c31fd3 Michael Hanselmann
2229 1c3231aa Thomas Thrainer
    # Try to resolve UUIDs first
2230 1c3231aa Thomas Thrainer
    for (node_uuid, new_group_uuid) in mods:
2231 54c31fd3 Michael Hanselmann
      try:
2232 1c3231aa Thomas Thrainer
        node = nodes[node_uuid]
2233 54c31fd3 Michael Hanselmann
      except KeyError:
2234 1c3231aa Thomas Thrainer
        raise errors.ConfigurationError("Unable to find node '%s'" % node_uuid)
2235 54c31fd3 Michael Hanselmann
2236 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
2237 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
2238 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
2239 1c3231aa Thomas Thrainer
                      node_uuid, node.group)
2240 54c31fd3 Michael Hanselmann
        continue
2241 54c31fd3 Michael Hanselmann
2242 54c31fd3 Michael Hanselmann
      # Try to find current group of node
2243 54c31fd3 Michael Hanselmann
      try:
2244 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
2245 54c31fd3 Michael Hanselmann
      except KeyError:
2246 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
2247 54c31fd3 Michael Hanselmann
                                        node.group)
2248 54c31fd3 Michael Hanselmann
2249 54c31fd3 Michael Hanselmann
      # Try to find new group for node
2250 54c31fd3 Michael Hanselmann
      try:
2251 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
2252 54c31fd3 Michael Hanselmann
      except KeyError:
2253 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
2254 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
2255 54c31fd3 Michael Hanselmann
2256 1c3231aa Thomas Thrainer
      assert node.uuid in old_group.members, \
2257 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
2258 1c3231aa Thomas Thrainer
         " old group '%s'" % (node.uuid, old_group.uuid))
2259 1c3231aa Thomas Thrainer
      assert node.uuid not in new_group.members, \
2260 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
2261 1c3231aa Thomas Thrainer
         " its new group '%s'" % (node.uuid, new_group.uuid))
2262 54c31fd3 Michael Hanselmann
2263 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
2264 54c31fd3 Michael Hanselmann
2265 54c31fd3 Michael Hanselmann
    # Apply changes
2266 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
2267 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
2268 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
2269 54c31fd3 Michael Hanselmann
2270 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
2271 54c31fd3 Michael Hanselmann
2272 54c31fd3 Michael Hanselmann
      # Update members of involved groups
2273 1c3231aa Thomas Thrainer
      if node.uuid in old_group.members:
2274 1c3231aa Thomas Thrainer
        old_group.members.remove(node.uuid)
2275 1c3231aa Thomas Thrainer
      if node.uuid not in new_group.members:
2276 1c3231aa Thomas Thrainer
        new_group.members.append(node.uuid)
2277 54c31fd3 Michael Hanselmann
2278 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
2279 54c31fd3 Michael Hanselmann
    now = time.time()
2280 75191077 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142
2281 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
2282 54c31fd3 Michael Hanselmann
      obj.mtime = now
2283 54c31fd3 Michael Hanselmann
2284 54c31fd3 Michael Hanselmann
    # Force ssconf update
2285 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
2286 54c31fd3 Michael Hanselmann
2287 54c31fd3 Michael Hanselmann
    self._WriteConfig()
2288 54c31fd3 Michael Hanselmann
2289 a8083063 Iustin Pop
  def _BumpSerialNo(self):
2290 a8083063 Iustin Pop
    """Bump up the serial number of the config.
2291 a8083063 Iustin Pop

2292 a8083063 Iustin Pop
    """
2293 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
2294 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
2295 a8083063 Iustin Pop
2296 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
2297 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
2298 76d5d3a3 Iustin Pop

2299 76d5d3a3 Iustin Pop
    """
2300 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
2301 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
2302 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
2303 e0519c34 Dimitris Aragiorgis
            self._config_data.networks.values() +
2304 b87a9c5f Christos Stavrakakis
            self._AllDisks() +
2305 b87a9c5f Christos Stavrakakis
            self._AllNICs() +
2306 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
2307 76d5d3a3 Iustin Pop
2308 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
2309 a8083063 Iustin Pop
    """Read the config data from disk.
2310 a8083063 Iustin Pop

2311 a8083063 Iustin Pop
    """
2312 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
2313 13998ef2 Michael Hanselmann
2314 a8083063 Iustin Pop
    try:
2315 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
2316 13998ef2 Michael Hanselmann
    except Exception, err:
2317 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
2318 5b263ed7 Michael Hanselmann
2319 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
2320 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
2321 5b263ed7 Michael Hanselmann
2322 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
2323 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
2324 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
2325 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
2326 90d726a8 Iustin Pop
2327 1c3231aa Thomas Thrainer
    if not data.cluster.master_node in data.nodes:
2328 1c3231aa Thomas Thrainer
      msg = ("The configuration denotes node %s as master, but does not"
2329 1c3231aa Thomas Thrainer
             " contain information about this node" %
2330 1c3231aa Thomas Thrainer
             data.cluster.master_node)
2331 1c3231aa Thomas Thrainer
      raise errors.ConfigurationError(msg)
2332 1c3231aa Thomas Thrainer
2333 1c3231aa Thomas Thrainer
    master_info = data.nodes[data.cluster.master_node]
2334 1c3231aa Thomas Thrainer
    if master_info.name != self._my_hostname and not accept_foreign:
2335 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
2336 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
2337 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
2338 1c3231aa Thomas Thrainer
             (master_info.name, self._my_hostname))
2339 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
2340 eb180fe2 Iustin Pop
2341 a8083063 Iustin Pop
    self._config_data = data
2342 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
2343 0779e3aa Iustin Pop
    # ssconf update
2344 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
2345 a8083063 Iustin Pop
2346 45f62156 Bernardo Dal Seno
    # Upgrade configuration if needed
2347 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
2348 76d5d3a3 Iustin Pop
2349 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
2350 bd407597 Iustin Pop
2351 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
2352 45f62156 Bernardo Dal Seno
    """Run any upgrade steps.
2353 76d5d3a3 Iustin Pop

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

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

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

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

2408 a8083063 Iustin Pop
    """
2409 a8083063 Iustin Pop
    if self._offline:
2410 a8083063 Iustin Pop
      return True
2411 a4eae71f Michael Hanselmann
2412 a8083063 Iustin Pop
    bad = False
2413 a8083063 Iustin Pop
2414 6a5b8b4b Iustin Pop
    node_list = []
2415 6a5b8b4b Iustin Pop
    addr_list = []
2416 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
2417 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
2418 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
2419 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
2420 6b294c53 Iustin Pop
    # in between
2421 1c3231aa Thomas Thrainer
    for node_uuid in self._UnlockedGetNodeList():
2422 1c3231aa Thomas Thrainer
      node_info = self._UnlockedGetNodeInfo(node_uuid)
2423 1c3231aa Thomas Thrainer
      if node_info.name == myhostname or not node_info.master_candidate:
2424 6a5b8b4b Iustin Pop
        continue
2425 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
2426 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
2427 6b294c53 Iustin Pop
2428 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
2429 415a7304 Michael Hanselmann
    result = \
2430 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
2431 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
2432 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
2433 1b54fc6c Guido Trotter
      if msg:
2434 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
2435 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
2436 1b54fc6c Guido Trotter
        logging.error(msg)
2437 a4eae71f Michael Hanselmann
2438 a4eae71f Michael Hanselmann
        if feedback_fn:
2439 a4eae71f Michael Hanselmann
          feedback_fn(msg)
2440 a4eae71f Michael Hanselmann
2441 a8083063 Iustin Pop
        bad = True
2442 a4eae71f Michael Hanselmann
2443 a8083063 Iustin Pop
    return not bad
2444 a8083063 Iustin Pop
2445 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
2446 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
2447 a8083063 Iustin Pop

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

2508 def6577f Helga Velroyen
    @type hypervisors: list of string
2509 def6577f Helga Velroyen
    @param hypervisors: list of hypervisor names
2510 def6577f Helga Velroyen
    @rtype: dict of strings
2511 def6577f Helga Velroyen
    @returns: dictionary mapping the hypervisor name to a string representation
2512 def6577f Helga Velroyen
      of the hypervisor's hvparams
2513 def6577f Helga Velroyen

2514 def6577f Helga Velroyen
    """
2515 def6577f Helga Velroyen
    hvparams = {}
2516 def6577f Helga Velroyen
    for hv in hypervisors:
2517 def6577f Helga Velroyen
      hvparams[hv] = self._UnlockedGetHvparamsString(hv)
2518 def6577f Helga Velroyen
    return hvparams
2519 def6577f Helga Velroyen
2520 def6577f Helga Velroyen
  @staticmethod
2521 def6577f Helga Velroyen
  def _ExtendByAllHvparamsStrings(ssconf_values, all_hvparams):
2522 def6577f Helga Velroyen
    """Extends the ssconf_values dictionary by hvparams.
2523 def6577f Helga Velroyen

2524 def6577f Helga Velroyen
    @type ssconf_values: dict of strings
2525 def6577f Helga Velroyen
    @param ssconf_values: dictionary mapping ssconf_keys to strings
2526 def6577f Helga Velroyen
      representing the content of ssconf files
2527 def6577f Helga Velroyen
    @type all_hvparams: dict of strings
2528 def6577f Helga Velroyen
    @param all_hvparams: dictionary mapping hypervisor names to a string
2529 def6577f Helga Velroyen
      representation of their hvparams
2530 def6577f Helga Velroyen
    @rtype: same as ssconf_values
2531 def6577f Helga Velroyen
    @returns: the ssconf_values dictionary extended by hvparams
2532 def6577f Helga Velroyen

2533 def6577f Helga Velroyen
    """
2534 def6577f Helga Velroyen
    for hv in all_hvparams:
2535 def6577f Helga Velroyen
      ssconf_key = constants.SS_HVPARAMS_PREF + hv
2536 def6577f Helga Velroyen
      ssconf_values[ssconf_key] = all_hvparams[hv]
2537 def6577f Helga Velroyen
    return ssconf_values
2538 def6577f Helga Velroyen
2539 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
2540 054596f0 Iustin Pop
    """Return the values needed by ssconf.
2541 054596f0 Iustin Pop

2542 054596f0 Iustin Pop
    @rtype: dict
2543 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2544 054596f0 Iustin Pop
        associated value
2545 054596f0 Iustin Pop

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

2622 d367b66c Manuel Franceschini
    """
2623 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2624 d367b66c Manuel Franceschini
2625 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2626 a8083063 Iustin Pop
  def GetVGName(self):
2627 a8083063 Iustin Pop
    """Return the volume group name.
2628 a8083063 Iustin Pop

2629 a8083063 Iustin Pop
    """
2630 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2631 a8083063 Iustin Pop
2632 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2633 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2634 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2635 89ff8e15 Manuel Franceschini

2636 89ff8e15 Manuel Franceschini
    """
2637 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2638 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2639 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2640 89ff8e15 Manuel Franceschini
2641 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2642 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2643 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2644 9e33896b Luca Bigliardi

2645 9e33896b Luca Bigliardi
    """
2646 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2647 9e33896b Luca Bigliardi
2648 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2649 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2650 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2651 9e33896b Luca Bigliardi

2652 9e33896b Luca Bigliardi
    """
2653 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2654 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2655 9e33896b Luca Bigliardi
    self._WriteConfig()
2656 9e33896b Luca Bigliardi
2657 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2658 a8083063 Iustin Pop
  def GetMACPrefix(self):
2659 a8083063 Iustin Pop
    """Return the mac prefix.
2660 a8083063 Iustin Pop

2661 a8083063 Iustin Pop
    """
2662 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2663 62779dd0 Iustin Pop
2664 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2665 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2666 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2667 62779dd0 Iustin Pop

2668 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2669 c41eea6e Iustin Pop
    @return: the cluster object
2670 62779dd0 Iustin Pop

2671 62779dd0 Iustin Pop
    """
2672 62779dd0 Iustin Pop
    return self._config_data.cluster
2673 e00fb268 Iustin Pop
2674 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2675 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2676 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2677 51cb1581 Luca Bigliardi

2678 51cb1581 Luca Bigliardi
    """
2679 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2680 51cb1581 Luca Bigliardi
2681 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2682 d1547283 Dimitris Aragiorgis
  def Update(self, target, feedback_fn, ec_id=None):
2683 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2684 e00fb268 Iustin Pop

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

2691 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2692 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2693 c41eea6e Iustin Pop
        the cluster
2694 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2695 c41eea6e Iustin Pop

2696 e00fb268 Iustin Pop
    """
2697 e00fb268 Iustin Pop
    if self._config_data is None:
2698 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2699 3ecf6786 Iustin Pop
                                   " cannot save.")
2700 f34901f8 Iustin Pop
    update_serial = False
2701 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2702 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2703 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2704 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2705 f34901f8 Iustin Pop
      update_serial = True
2706 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2707 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2708 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2709 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2710 1e0d3321 Dimitris Aragiorgis
    elif isinstance(target, objects.Network):
2711 1e0d3321 Dimitris Aragiorgis
      test = target in self._config_data.networks.values()
2712 e00fb268 Iustin Pop
    else:
2713 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2714 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2715 e00fb268 Iustin Pop
    if not test:
2716 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2717 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2718 f34901f8 Iustin Pop
    target.serial_no += 1
2719 d693c864 Iustin Pop
    target.mtime = now = time.time()
2720 f34901f8 Iustin Pop
2721 cff4c037 Iustin Pop
    if update_serial:
2722 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2723 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2724 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2725 b989e85d Iustin Pop
2726 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2727 da4a52a3 Thomas Thrainer
      self._UnlockedReleaseDRBDMinors(target.uuid)
2728 61cf6b5e Iustin Pop
2729 d1547283 Dimitris Aragiorgis
    if ec_id is not None:
2730 d1547283 Dimitris Aragiorgis
      # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams
2731 d1547283 Dimitris Aragiorgis
      self._UnlockedCommitTemporaryIps(ec_id)
2732 d1547283 Dimitris Aragiorgis
2733 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2734 73064714 Guido Trotter
2735 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2736 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2737 73064714 Guido Trotter
    """Drop per-execution-context reservations
2738 73064714 Guido Trotter

2739 73064714 Guido Trotter
    """
2740 d8aee57e Iustin Pop
    for rm in self._all_rms:
2741 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)
2742 6c0a75db Dimitris Aragiorgis
2743 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2744 6c0a75db Dimitris Aragiorgis
  def GetAllNetworksInfo(self):
2745 6a94d553 Dimitris Aragiorgis
    """Get configuration info of all the networks.
2746 6c0a75db Dimitris Aragiorgis

2747 6c0a75db Dimitris Aragiorgis
    """
2748 6c0a75db Dimitris Aragiorgis
    return dict(self._config_data.networks)
2749 6c0a75db Dimitris Aragiorgis
2750 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetworkList(self):
2751 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2752 6c0a75db Dimitris Aragiorgis

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

2755 6c0a75db Dimitris Aragiorgis
    """
2756 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks.keys()
2757 6c0a75db Dimitris Aragiorgis
2758 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2759 6c0a75db Dimitris Aragiorgis
  def GetNetworkList(self):
2760 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2761 6c0a75db Dimitris Aragiorgis

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

2764 6c0a75db Dimitris Aragiorgis
    """
2765 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetworkList()
2766 6c0a75db Dimitris Aragiorgis
2767 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2768 6c0a75db Dimitris Aragiorgis
  def GetNetworkNames(self):
2769 6c0a75db Dimitris Aragiorgis
    """Get a list of network names
2770 6c0a75db Dimitris Aragiorgis

2771 6c0a75db Dimitris Aragiorgis
    """
2772 beb81ea5 Dimitris Aragiorgis
    names = [net.name
2773 beb81ea5 Dimitris Aragiorgis
             for net in self._config_data.networks.values()]
2774 6c0a75db Dimitris Aragiorgis
    return names
2775 6c0a75db Dimitris Aragiorgis
2776 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetwork(self, uuid):
2777 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2778 6c0a75db Dimitris Aragiorgis

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

2781 6c0a75db Dimitris Aragiorgis
    """
2782 6c0a75db Dimitris Aragiorgis
    if uuid not in self._config_data.networks:
2783 6c0a75db Dimitris Aragiorgis
      return None
2784 6c0a75db Dimitris Aragiorgis
2785 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks[uuid]
2786 6c0a75db Dimitris Aragiorgis
2787 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2788 6c0a75db Dimitris Aragiorgis
  def GetNetwork(self, uuid):
2789 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2790 6c0a75db Dimitris Aragiorgis

2791 6c0a75db Dimitris Aragiorgis
    It takes the information from the configuration file.
2792 6c0a75db Dimitris Aragiorgis

2793 6c0a75db Dimitris Aragiorgis
    @param uuid: UUID of the network
2794 6c0a75db Dimitris Aragiorgis

2795 6c0a75db Dimitris Aragiorgis
    @rtype: L{objects.Network}
2796 6c0a75db Dimitris Aragiorgis
    @return: the network object
2797 6c0a75db Dimitris Aragiorgis

2798 6c0a75db Dimitris Aragiorgis
    """
2799 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetwork(uuid)
2800 6c0a75db Dimitris Aragiorgis
2801 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2802 6c0a75db Dimitris Aragiorgis
  def AddNetwork(self, net, ec_id, check_uuid=True):
2803 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2804 6c0a75db Dimitris Aragiorgis

2805 6c0a75db Dimitris Aragiorgis
    @type net: L{objects.Network}
2806 6c0a75db Dimitris Aragiorgis
    @param net: the Network object to add
2807 6c0a75db Dimitris Aragiorgis
    @type ec_id: string
2808 6c0a75db Dimitris Aragiorgis
    @param ec_id: unique id for the job to use when creating a missing UUID
2809 6c0a75db Dimitris Aragiorgis

2810 6c0a75db Dimitris Aragiorgis
    """
2811 6c0a75db Dimitris Aragiorgis
    self._UnlockedAddNetwork(net, ec_id, check_uuid)
2812 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2813 6c0a75db Dimitris Aragiorgis
2814 6c0a75db Dimitris Aragiorgis
  def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
2815 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2816 6c0a75db Dimitris Aragiorgis

2817 6c0a75db Dimitris Aragiorgis
    """
2818 6c0a75db Dimitris Aragiorgis
    logging.info("Adding network %s to configuration", net.name)
2819 6c0a75db Dimitris Aragiorgis
2820 6c0a75db Dimitris Aragiorgis
    if check_uuid:
2821 6c0a75db Dimitris Aragiorgis
      self._EnsureUUID(net, ec_id)
2822 6c0a75db Dimitris Aragiorgis
2823 6c0a75db Dimitris Aragiorgis
    net.serial_no = 1
2824 22ff02a7 Christos Stavrakakis
    net.ctime = net.mtime = time.time()
2825 6c0a75db Dimitris Aragiorgis
    self._config_data.networks[net.uuid] = net
2826 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2827 6c0a75db Dimitris Aragiorgis
2828 6c0a75db Dimitris Aragiorgis
  def _UnlockedLookupNetwork(self, target):
2829 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2830 6c0a75db Dimitris Aragiorgis

2831 6c0a75db Dimitris Aragiorgis
    @type target: string
2832 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2833 6c0a75db Dimitris Aragiorgis
    @rtype: string
2834 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2835 6c0a75db Dimitris Aragiorgis
    @raises errors.OpPrereqError: when the target network cannot be found
2836 6c0a75db Dimitris Aragiorgis

2837 6c0a75db Dimitris Aragiorgis
    """
2838 9394f4d1 Dimitris Aragiorgis
    if target is None:
2839 9394f4d1 Dimitris Aragiorgis
      return None
2840 6c0a75db Dimitris Aragiorgis
    if target in self._config_data.networks:
2841 6c0a75db Dimitris Aragiorgis
      return target
2842 6c0a75db Dimitris Aragiorgis
    for net in self._config_data.networks.values():
2843 6c0a75db Dimitris Aragiorgis
      if net.name == target:
2844 6c0a75db Dimitris Aragiorgis
        return net.uuid
2845 1b68f268 Helga Velroyen
    raise errors.OpPrereqError("Network '%s' not found" % target,
2846 1b68f268 Helga Velroyen
                               errors.ECODE_NOENT)
2847 6c0a75db Dimitris Aragiorgis
2848 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2849 6c0a75db Dimitris Aragiorgis
  def LookupNetwork(self, target):
2850 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2851 6c0a75db Dimitris Aragiorgis

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

2854 6c0a75db Dimitris Aragiorgis
    @type target: string
2855 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2856 6c0a75db Dimitris Aragiorgis
    @rtype: string
2857 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2858 6c0a75db Dimitris Aragiorgis

2859 6c0a75db Dimitris Aragiorgis
    """
2860 6c0a75db Dimitris Aragiorgis
    return self._UnlockedLookupNetwork(target)
2861 6c0a75db Dimitris Aragiorgis
2862 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2863 6c0a75db Dimitris Aragiorgis
  def RemoveNetwork(self, network_uuid):
2864 6c0a75db Dimitris Aragiorgis
    """Remove a network from the configuration.
2865 6c0a75db Dimitris Aragiorgis

2866 6c0a75db Dimitris Aragiorgis
    @type network_uuid: string
2867 6c0a75db Dimitris Aragiorgis
    @param network_uuid: the UUID of the network to remove
2868 6c0a75db Dimitris Aragiorgis

2869 6c0a75db Dimitris Aragiorgis
    """
2870 6c0a75db Dimitris Aragiorgis
    logging.info("Removing network %s from configuration", network_uuid)
2871 6c0a75db Dimitris Aragiorgis
2872 6c0a75db Dimitris Aragiorgis
    if network_uuid not in self._config_data.networks:
2873 6c0a75db Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
2874 6c0a75db Dimitris Aragiorgis
2875 6c0a75db Dimitris Aragiorgis
    del self._config_data.networks[network_uuid]
2876 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2877 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2878 ad4a9ae7 Dimitris Aragiorgis
2879 1c3231aa Thomas Thrainer
  def _UnlockedGetGroupNetParams(self, net_uuid, node_uuid):
2880 ad4a9ae7 Dimitris Aragiorgis
    """Get the netparams (mode, link) of a network.
2881 ad4a9ae7 Dimitris Aragiorgis

2882 ad4a9ae7 Dimitris Aragiorgis
    Get a network's netparams for a given node.
2883 ad4a9ae7 Dimitris Aragiorgis

2884 9ccacbc8 Dimitris Aragiorgis
    @type net_uuid: string
2885 9ccacbc8 Dimitris Aragiorgis
    @param net_uuid: network uuid
2886 1c3231aa Thomas Thrainer
    @type node_uuid: string
2887 1c3231aa Thomas Thrainer
    @param node_uuid: node UUID
2888 ad4a9ae7 Dimitris Aragiorgis
    @rtype: dict or None
2889 ad4a9ae7 Dimitris Aragiorgis
    @return: netparams
2890 ad4a9ae7 Dimitris Aragiorgis

2891 ad4a9ae7 Dimitris Aragiorgis
    """
2892 1c3231aa Thomas Thrainer
    node_info = self._UnlockedGetNodeInfo(node_uuid)
2893 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2894 ad4a9ae7 Dimitris Aragiorgis
    netparams = nodegroup_info.networks.get(net_uuid, None)
2895 ad4a9ae7 Dimitris Aragiorgis
2896 ad4a9ae7 Dimitris Aragiorgis
    return netparams
2897 ad4a9ae7 Dimitris Aragiorgis
2898 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2899 1c3231aa Thomas Thrainer
  def GetGroupNetParams(self, net_uuid, node_uuid):
2900 ad4a9ae7 Dimitris Aragiorgis
    """Locking wrapper of _UnlockedGetGroupNetParams()
2901 ad4a9ae7 Dimitris Aragiorgis

2902 ad4a9ae7 Dimitris Aragiorgis
    """
2903 1c3231aa Thomas Thrainer
    return self._UnlockedGetGroupNetParams(net_uuid, node_uuid)
2904 ad4a9ae7 Dimitris Aragiorgis
2905 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2906 1c3231aa Thomas Thrainer
  def CheckIPInNodeGroup(self, ip, node_uuid):
2907 6a94d553 Dimitris Aragiorgis
    """Check IP uniqueness in nodegroup.
2908 6a94d553 Dimitris Aragiorgis

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

2913 ad4a9ae7 Dimitris Aragiorgis
    @type ip: string
2914 ad4a9ae7 Dimitris Aragiorgis
    @param ip: ip address
2915 1c3231aa Thomas Thrainer
    @type node_uuid: string
2916 1c3231aa Thomas Thrainer
    @param node_uuid: node UUID
2917 ad4a9ae7 Dimitris Aragiorgis
    @rtype: (string, dict) or (None, None)
2918 ad4a9ae7 Dimitris Aragiorgis
    @return: (network name, netparams)
2919 ad4a9ae7 Dimitris Aragiorgis

2920 ad4a9ae7 Dimitris Aragiorgis
    """
2921 ad4a9ae7 Dimitris Aragiorgis
    if ip is None:
2922 ad4a9ae7 Dimitris Aragiorgis
      return (None, None)
2923 1c3231aa Thomas Thrainer
    node_info = self._UnlockedGetNodeInfo(node_uuid)
2924 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2925 ad4a9ae7 Dimitris Aragiorgis
    for net_uuid in nodegroup_info.networks.keys():
2926 ad4a9ae7 Dimitris Aragiorgis
      net_info = self._UnlockedGetNetwork(net_uuid)
2927 ad4a9ae7 Dimitris Aragiorgis
      pool = network.AddressPool(net_info)
2928 beb81ea5 Dimitris Aragiorgis
      if pool.Contains(ip):
2929 ad4a9ae7 Dimitris Aragiorgis
        return (net_info.name, nodegroup_info.networks[net_uuid])
2930 ad4a9ae7 Dimitris Aragiorgis
2931 ad4a9ae7 Dimitris Aragiorgis
    return (None, None)