Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ a9f33339

History | View | Annotate | Download (96.3 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 3efa7659 Thomas Thrainer
class ConfigWriter(object):
171 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
172 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

318 a8083063 Iustin Pop
    This should check the current instances for duplicates.
319 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

374 ad4a9ae7 Dimitris Aragiorgis
    This is just a wrapper around _UnlockedReleaseIp.
375 ad4a9ae7 Dimitris Aragiorgis

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

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

401 ad4a9ae7 Dimitris Aragiorgis
    """
402 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
403 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
404 ad4a9ae7 Dimitris Aragiorgis
    try:
405 ad4a9ae7 Dimitris Aragiorgis
      isreserved = pool.IsReserved(address)
406 ad4a9ae7 Dimitris Aragiorgis
    except errors.AddressPoolError:
407 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address not in network")
408 ad4a9ae7 Dimitris Aragiorgis
    if isreserved:
409 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address already in use")
410 ad4a9ae7 Dimitris Aragiorgis
411 e81eef56 Dimitris Aragiorgis
    return self._temporary_ips.Reserve(ec_id,
412 e81eef56 Dimitris Aragiorgis
                                       (constants.RESERVE_ACTION,
413 e81eef56 Dimitris Aragiorgis
                                        address, net_uuid))
414 ad4a9ae7 Dimitris Aragiorgis
415 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
416 9ccacbc8 Dimitris Aragiorgis
  def ReserveIp(self, net_uuid, address, ec_id):
417 ad4a9ae7 Dimitris Aragiorgis
    """Reserve a given IPv4 address for use by an instance.
418 ad4a9ae7 Dimitris Aragiorgis

419 ad4a9ae7 Dimitris Aragiorgis
    """
420 9ccacbc8 Dimitris Aragiorgis
    if net_uuid:
421 9ccacbc8 Dimitris Aragiorgis
      return self._UnlockedReserveIp(net_uuid, address, ec_id)
422 ad4a9ae7 Dimitris Aragiorgis
423 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
424 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
425 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
426 d8aee57e Iustin Pop

427 d8aee57e Iustin Pop
    @type lv_name: string
428 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
429 d8aee57e Iustin Pop

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

441 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
442 f9518d38 Iustin Pop

443 f9518d38 Iustin Pop
    """
444 afa1386e Guido Trotter
    return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
445 afa1386e Guido Trotter
                                            utils.GenerateSecret,
446 afa1386e Guido Trotter
                                            ec_id)
447 8d9c3bef Michael Hanselmann
448 34e54ebc Iustin Pop
  def _AllLVs(self):
449 923b1523 Iustin Pop
    """Compute the list of all LVs.
450 923b1523 Iustin Pop

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

462 b87a9c5f Christos Stavrakakis
    """
463 79780863 Michele Tartara
    def DiskAndAllChildren(disk):
464 79780863 Michele Tartara
      """Returns a list containing the given disk and all of his children.
465 79780863 Michele Tartara

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

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

491 34e54ebc Iustin Pop
    @type include_temporary: boolean
492 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
493 34e54ebc Iustin Pop
    @rtype: set
494 34e54ebc Iustin Pop
    @return: a set of IDs
495 34e54ebc Iustin Pop

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

509 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
510 923b1523 Iustin Pop
    duplicates.
511 923b1523 Iustin Pop

512 c41eea6e Iustin Pop
    @rtype: string
513 c41eea6e Iustin Pop
    @return: the unique id
514 923b1523 Iustin Pop

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

523 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
524 430b923c Iustin Pop

525 4fae38c5 Guido Trotter
    @type ec_id: string
526 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
527 34d657ba Iustin Pop

528 34d657ba Iustin Pop
    """
529 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
530 34d657ba Iustin Pop
531 a8083063 Iustin Pop
  def _AllMACs(self):
532 a8083063 Iustin Pop
    """Return all MACs present in the config.
533 a8083063 Iustin Pop

534 c41eea6e Iustin Pop
    @rtype: list
535 c41eea6e Iustin Pop
    @return: the list of all MACs
536 c41eea6e Iustin Pop

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

548 c41eea6e Iustin Pop
    @rtype: list
549 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
550 c41eea6e Iustin Pop

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

570 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
571 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
572 4b98ac29 Iustin Pop
    @type l_ids: list
573 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
574 4b98ac29 Iustin Pop
    @rtype: list
575 4b98ac29 Iustin Pop
    @return: a list of error messages
576 4b98ac29 Iustin Pop

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

593 4a89c54a Iustin Pop
    @rtype: list
594 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
595 4a89c54a Iustin Pop
        configuration errors
596 4a89c54a Iustin Pop

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

880 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
881 4a89c54a Iustin Pop

882 4a89c54a Iustin Pop
    @rtype: list
883 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
884 4a89c54a Iustin Pop
        configuration errors
885 4a89c54a Iustin Pop

886 4a89c54a Iustin Pop
    """
887 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
888 4a89c54a Iustin Pop
889 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
890 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
891 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
892 b2fddf63 Iustin Pop

893 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
894 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
895 3b3b1bca Dimitris Aragiorgis
        configuration is stable
896 3b3b1bca Dimitris Aragiorgis

897 b2fddf63 Iustin Pop
    """
898 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
899 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
900 264bb3c5 Michael Hanselmann
901 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
902 264bb3c5 Michael Hanselmann
903 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
904 b2fddf63 Iustin Pop
  def GetPortList(self):
905 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
906 264bb3c5 Michael Hanselmann

907 264bb3c5 Michael Hanselmann
    """
908 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
909 264bb3c5 Michael Hanselmann
910 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
911 a8083063 Iustin Pop
  def AllocatePort(self):
912 a8083063 Iustin Pop
    """Allocate a port.
913 a8083063 Iustin Pop

914 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
915 b2fddf63 Iustin Pop
    default port range (and in this case we increase
916 b2fddf63 Iustin Pop
    highest_used_port).
917 a8083063 Iustin Pop

918 a8083063 Iustin Pop
    """
919 264bb3c5 Michael Hanselmann
    # If there are TCP/IP ports configured, we use them first.
920 b2fddf63 Iustin Pop
    if self._config_data.cluster.tcpudp_port_pool:
921 b2fddf63 Iustin Pop
      port = self._config_data.cluster.tcpudp_port_pool.pop()
922 264bb3c5 Michael Hanselmann
    else:
923 264bb3c5 Michael Hanselmann
      port = self._config_data.cluster.highest_used_port + 1
924 264bb3c5 Michael Hanselmann
      if port >= constants.LAST_DRBD_PORT:
925 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("The highest used port is greater"
926 3ecf6786 Iustin Pop
                                        " than %s. Aborting." %
927 3ecf6786 Iustin Pop
                                        constants.LAST_DRBD_PORT)
928 264bb3c5 Michael Hanselmann
      self._config_data.cluster.highest_used_port = port
929 a8083063 Iustin Pop
930 a8083063 Iustin Pop
    self._WriteConfig()
931 a8083063 Iustin Pop
    return port
932 a8083063 Iustin Pop
933 6d2e83d5 Iustin Pop
  def _UnlockedComputeDRBDMap(self):
934 a81c53c9 Iustin Pop
    """Compute the used DRBD minor/nodes.
935 a81c53c9 Iustin Pop

936 4a89c54a Iustin Pop
    @rtype: (dict, list)
937 da4a52a3 Thomas Thrainer
    @return: dictionary of node_uuid: dict of minor: instance_uuid;
938 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
939 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
940 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
941 4a89c54a Iustin Pop
        should raise an exception
942 a81c53c9 Iustin Pop

943 a81c53c9 Iustin Pop
    """
944 da4a52a3 Thomas Thrainer
    def _AppendUsedMinors(get_node_name_fn, instance, disk, used):
945 4a89c54a Iustin Pop
      duplicates = []
946 cd3b4ff4 Helga Velroyen
      if disk.dev_type == constants.DT_DRBD8 and len(disk.logical_id) >= 5:
947 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
948 da4a52a3 Thomas Thrainer
        for node_uuid, minor in ((node_a, minor_a), (node_b, minor_b)):
949 b691385f Thomas Thrainer
          assert node_uuid in used, \
950 b691385f Thomas Thrainer
            ("Node '%s' of instance '%s' not found in node list" %
951 da4a52a3 Thomas Thrainer
             (get_node_name_fn(node_uuid), instance.name))
952 da4a52a3 Thomas Thrainer
          if minor in used[node_uuid]:
953 da4a52a3 Thomas Thrainer
            duplicates.append((node_uuid, minor, instance.uuid,
954 da4a52a3 Thomas Thrainer
                               used[node_uuid][minor]))
955 4a89c54a Iustin Pop
          else:
956 da4a52a3 Thomas Thrainer
            used[node_uuid][minor] = instance.uuid
957 a81c53c9 Iustin Pop
      if disk.children:
958 a81c53c9 Iustin Pop
        for child in disk.children:
959 da4a52a3 Thomas Thrainer
          duplicates.extend(_AppendUsedMinors(get_node_name_fn, instance, child,
960 da4a52a3 Thomas Thrainer
                                              used))
961 4a89c54a Iustin Pop
      return duplicates
962 a81c53c9 Iustin Pop
963 4a89c54a Iustin Pop
    duplicates = []
964 da4a52a3 Thomas Thrainer
    my_dict = dict((node_uuid, {}) for node_uuid in self._config_data.nodes)
965 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
966 79b26a7a Iustin Pop
      for disk in instance.disks:
967 da4a52a3 Thomas Thrainer
        duplicates.extend(_AppendUsedMinors(self._UnlockedGetNodeName,
968 da4a52a3 Thomas Thrainer
                                            instance, disk, my_dict))
969 da4a52a3 Thomas Thrainer
    for (node_uuid, minor), inst_uuid in self._temporary_drbds.iteritems():
970 da4a52a3 Thomas Thrainer
      if minor in my_dict[node_uuid] and my_dict[node_uuid][minor] != inst_uuid:
971 da4a52a3 Thomas Thrainer
        duplicates.append((node_uuid, minor, inst_uuid,
972 da4a52a3 Thomas Thrainer
                           my_dict[node_uuid][minor]))
973 4a89c54a Iustin Pop
      else:
974 da4a52a3 Thomas Thrainer
        my_dict[node_uuid][minor] = inst_uuid
975 4a89c54a Iustin Pop
    return my_dict, duplicates
976 a81c53c9 Iustin Pop
977 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
978 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
979 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
980 6d2e83d5 Iustin Pop

981 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
982 6d2e83d5 Iustin Pop

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

987 6d2e83d5 Iustin Pop
    """
988 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
989 4a89c54a Iustin Pop
    if duplicates:
990 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
991 4a89c54a Iustin Pop
                                      str(duplicates))
992 4a89c54a Iustin Pop
    return d_map
993 6d2e83d5 Iustin Pop
994 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
995 da4a52a3 Thomas Thrainer
  def AllocateDRBDMinor(self, node_uuids, inst_uuid):
996 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
997 a81c53c9 Iustin Pop

998 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
999 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
1000 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
1001 a81c53c9 Iustin Pop
    order as the passed nodes.
1002 a81c53c9 Iustin Pop

1003 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1004 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which we allocate minors
1005 32388e6d Iustin Pop

1006 a81c53c9 Iustin Pop
    """
1007 da4a52a3 Thomas Thrainer
    assert isinstance(inst_uuid, basestring), \
1008 da4a52a3 Thomas Thrainer
           "Invalid argument '%s' passed to AllocateDRBDMinor" % inst_uuid
1009 32388e6d Iustin Pop
1010 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
1011 4a89c54a Iustin Pop
    if duplicates:
1012 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
1013 4a89c54a Iustin Pop
                                      str(duplicates))
1014 a81c53c9 Iustin Pop
    result = []
1015 1c3231aa Thomas Thrainer
    for nuuid in node_uuids:
1016 1c3231aa Thomas Thrainer
      ndata = d_map[nuuid]
1017 a81c53c9 Iustin Pop
      if not ndata:
1018 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
1019 a81c53c9 Iustin Pop
        result.append(0)
1020 da4a52a3 Thomas Thrainer
        ndata[0] = inst_uuid
1021 da4a52a3 Thomas Thrainer
        self._temporary_drbds[(nuuid, 0)] = inst_uuid
1022 a81c53c9 Iustin Pop
        continue
1023 a81c53c9 Iustin Pop
      keys = ndata.keys()
1024 a81c53c9 Iustin Pop
      keys.sort()
1025 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
1026 a81c53c9 Iustin Pop
      if ffree is None:
1027 a81c53c9 Iustin Pop
        # return the next minor
1028 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
1029 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
1030 a81c53c9 Iustin Pop
      else:
1031 a81c53c9 Iustin Pop
        minor = ffree
1032 4a89c54a Iustin Pop
      # double-check minor against current instances
1033 1c3231aa Thomas Thrainer
      assert minor not in d_map[nuuid], \
1034 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
1035 4a89c54a Iustin Pop
              " already allocated to instance %s" %
1036 1c3231aa Thomas Thrainer
              (minor, nuuid, d_map[nuuid][minor]))
1037 da4a52a3 Thomas Thrainer
      ndata[minor] = inst_uuid
1038 4a89c54a Iustin Pop
      # double-check minor against reservation
1039 1c3231aa Thomas Thrainer
      r_key = (nuuid, minor)
1040 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
1041 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
1042 4a89c54a Iustin Pop
              " reserved for instance %s" %
1043 1c3231aa Thomas Thrainer
              (minor, nuuid, self._temporary_drbds[r_key]))
1044 da4a52a3 Thomas Thrainer
      self._temporary_drbds[r_key] = inst_uuid
1045 4a89c54a Iustin Pop
      result.append(minor)
1046 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
1047 1c3231aa Thomas Thrainer
                  node_uuids, result)
1048 a81c53c9 Iustin Pop
    return result
1049 a81c53c9 Iustin Pop
1050 da4a52a3 Thomas Thrainer
  def _UnlockedReleaseDRBDMinors(self, inst_uuid):
1051 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
1052 a81c53c9 Iustin Pop

1053 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1054 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1055 da4a52a3 Thomas Thrainer
                      released
1056 a81c53c9 Iustin Pop

1057 a81c53c9 Iustin Pop
    """
1058 da4a52a3 Thomas Thrainer
    assert isinstance(inst_uuid, basestring), \
1059 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
1060 da4a52a3 Thomas Thrainer
    for key, uuid in self._temporary_drbds.items():
1061 da4a52a3 Thomas Thrainer
      if uuid == inst_uuid:
1062 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
1063 a81c53c9 Iustin Pop
1064 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
1065 da4a52a3 Thomas Thrainer
  def ReleaseDRBDMinors(self, inst_uuid):
1066 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
1067 61cf6b5e Iustin Pop

1068 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
1069 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
1070 61cf6b5e Iustin Pop
    functions.
1071 61cf6b5e Iustin Pop

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

1074 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1075 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1076 da4a52a3 Thomas Thrainer
                      released
1077 61cf6b5e Iustin Pop

1078 61cf6b5e Iustin Pop
    """
1079 da4a52a3 Thomas Thrainer
    self._UnlockedReleaseDRBDMinors(inst_uuid)
1080 61cf6b5e Iustin Pop
1081 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1082 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
1083 4a8b186a Michael Hanselmann
    """Get the configuration version.
1084 4a8b186a Michael Hanselmann

1085 4a8b186a Michael Hanselmann
    @return: Config version
1086 4a8b186a Michael Hanselmann

1087 4a8b186a Michael Hanselmann
    """
1088 4a8b186a Michael Hanselmann
    return self._config_data.version
1089 4a8b186a Michael Hanselmann
1090 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1091 4a8b186a Michael Hanselmann
  def GetClusterName(self):
1092 4a8b186a Michael Hanselmann
    """Get cluster name.
1093 4a8b186a Michael Hanselmann

1094 4a8b186a Michael Hanselmann
    @return: Cluster name
1095 4a8b186a Michael Hanselmann

1096 4a8b186a Michael Hanselmann
    """
1097 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
1098 4a8b186a Michael Hanselmann
1099 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1100 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
1101 1c3231aa Thomas Thrainer
    """Get the UUID of the master node for this cluster.
1102 4a8b186a Michael Hanselmann

1103 1c3231aa Thomas Thrainer
    @return: Master node UUID
1104 4a8b186a Michael Hanselmann

1105 4a8b186a Michael Hanselmann
    """
1106 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
1107 4a8b186a Michael Hanselmann
1108 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1109 1c3231aa Thomas Thrainer
  def GetMasterNodeName(self):
1110 1c3231aa Thomas Thrainer
    """Get the hostname of the master node for this cluster.
1111 1c3231aa Thomas Thrainer

1112 1c3231aa Thomas Thrainer
    @return: Master node hostname
1113 1c3231aa Thomas Thrainer

1114 1c3231aa Thomas Thrainer
    """
1115 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(self._config_data.cluster.master_node)
1116 1c3231aa Thomas Thrainer
1117 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1118 b730e2a7 Thomas Thrainer
  def GetMasterNodeInfo(self):
1119 b730e2a7 Thomas Thrainer
    """Get the master node information for this cluster.
1120 b730e2a7 Thomas Thrainer

1121 b730e2a7 Thomas Thrainer
    @rtype: objects.Node
1122 b730e2a7 Thomas Thrainer
    @return: Master node L{objects.Node} object
1123 b730e2a7 Thomas Thrainer

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

1131 4a8b186a Michael Hanselmann
    @return: Master IP
1132 4a8b186a Michael Hanselmann

1133 4a8b186a Michael Hanselmann
    """
1134 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
1135 4a8b186a Michael Hanselmann
1136 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1137 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
1138 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
1139 4a8b186a Michael Hanselmann

1140 4a8b186a Michael Hanselmann
    """
1141 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
1142 4a8b186a Michael Hanselmann
1143 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1144 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
1145 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
1146 5a8648eb Andrea Spadaccini

1147 5a8648eb Andrea Spadaccini
    """
1148 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
1149 5a8648eb Andrea Spadaccini
1150 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1151 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
1152 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
1153 33be7576 Andrea Spadaccini

1154 33be7576 Andrea Spadaccini
    """
1155 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
1156 33be7576 Andrea Spadaccini
1157 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1158 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
1159 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
1160 4a8b186a Michael Hanselmann

1161 4a8b186a Michael Hanselmann
    """
1162 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
1163 4a8b186a Michael Hanselmann
1164 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1165 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
1166 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
1167 4b97f902 Apollon Oikonomopoulos

1168 4b97f902 Apollon Oikonomopoulos
    """
1169 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1170 4b97f902 Apollon Oikonomopoulos
1171 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1172 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1173 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1174 4a8b186a Michael Hanselmann

1175 4a8b186a Michael Hanselmann
    """
1176 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
1177 4a8b186a Michael Hanselmann
1178 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1179 a9542a4f Thomas Thrainer
  def GetRsaHostKey(self):
1180 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1181 a8083063 Iustin Pop

1182 c41eea6e Iustin Pop
    @rtype: string
1183 c41eea6e Iustin Pop
    @return: the rsa hostkey
1184 a8083063 Iustin Pop

1185 a8083063 Iustin Pop
    """
1186 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1187 a8083063 Iustin Pop
1188 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1189 a9542a4f Thomas Thrainer
  def GetDsaHostKey(self):
1190 a9542a4f Thomas Thrainer
    """Return the dsa hostkey from the config.
1191 a9542a4f Thomas Thrainer

1192 a9542a4f Thomas Thrainer
    @rtype: string
1193 a9542a4f Thomas Thrainer
    @return: the dsa hostkey
1194 a9542a4f Thomas Thrainer

1195 a9542a4f Thomas Thrainer
    """
1196 a9542a4f Thomas Thrainer
    return self._config_data.cluster.dsahostkeypub
1197 a9542a4f Thomas Thrainer
1198 a9542a4f Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1199 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1200 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1201 bf4af505 Apollon Oikonomopoulos

1202 bf4af505 Apollon Oikonomopoulos
    """
1203 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1204 bf4af505 Apollon Oikonomopoulos
1205 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1206 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1207 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1208 868a98ca Manuel Franceschini

1209 868a98ca Manuel Franceschini
    @return: primary ip family
1210 868a98ca Manuel Franceschini

1211 868a98ca Manuel Franceschini
    """
1212 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1213 868a98ca Manuel Franceschini
1214 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1215 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1216 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1217 c9f4b8e6 Andrea Spadaccini

1218 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1219 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1220 c9f4b8e6 Andrea Spadaccini

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

1234 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1235 90e99856 Adeodato Simo
    according to their default values.
1236 90e99856 Adeodato Simo

1237 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1238 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1239 e11a1b77 Adeodato Simo
    @type ec_id: string
1240 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1241 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1242 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1243 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1244 e11a1b77 Adeodato Simo
                       configuration already
1245 e11a1b77 Adeodato Simo

1246 e11a1b77 Adeodato Simo
    """
1247 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1248 e11a1b77 Adeodato Simo
    self._WriteConfig()
1249 e11a1b77 Adeodato Simo
1250 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1251 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1252 e11a1b77 Adeodato Simo

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

1283 e11a1b77 Adeodato Simo
    @type group_uuid: string
1284 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1285 e11a1b77 Adeodato Simo

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

1302 eaa98a04 Guido Trotter
    @type target: string or None
1303 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1304 eaa98a04 Guido Trotter
    @rtype: string
1305 412b3531 Guido Trotter
    @return: nodegroup UUID
1306 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1307 eaa98a04 Guido Trotter

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

1327 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1328 e85d8982 Stephen Shirley

1329 e85d8982 Stephen Shirley
    @type target: string or None
1330 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1331 e85d8982 Stephen Shirley
    @rtype: string
1332 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1333 e85d8982 Stephen Shirley

1334 e85d8982 Stephen Shirley
    """
1335 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1336 e85d8982 Stephen Shirley
1337 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1338 648e4196 Guido Trotter
    """Lookup a node group.
1339 648e4196 Guido Trotter

1340 648e4196 Guido Trotter
    @type uuid: string
1341 648e4196 Guido Trotter
    @param uuid: group UUID
1342 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1343 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1344 648e4196 Guido Trotter

1345 648e4196 Guido Trotter
    """
1346 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1347 648e4196 Guido Trotter
      return None
1348 648e4196 Guido Trotter
1349 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1350 648e4196 Guido Trotter
1351 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1352 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1353 5768e6a6 René Nussbaumer
    """Lookup a node group.
1354 5768e6a6 René Nussbaumer

1355 5768e6a6 René Nussbaumer
    @type uuid: string
1356 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1357 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1358 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1359 5768e6a6 René Nussbaumer

1360 5768e6a6 René Nussbaumer
    """
1361 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1362 5768e6a6 René Nussbaumer
1363 6b2a2942 Petr Pudlak
  def _UnlockedGetAllNodeGroupsInfo(self):
1364 6b2a2942 Petr Pudlak
    """Get the configuration of all node groups.
1365 6b2a2942 Petr Pudlak

1366 6b2a2942 Petr Pudlak
    """
1367 6b2a2942 Petr Pudlak
    return dict(self._config_data.nodegroups)
1368 6b2a2942 Petr Pudlak
1369 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1370 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1371 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1372 622444e5 Iustin Pop

1373 622444e5 Iustin Pop
    """
1374 6b2a2942 Petr Pudlak
    return self._UnlockedGetAllNodeGroupsInfo()
1375 622444e5 Iustin Pop
1376 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1377 a9f33339 Petr Pudlak
  def GetAllNodeGroupsInfoDict(self):
1378 a9f33339 Petr Pudlak
    """Get the configuration of all node groups expressed as a dictionary of
1379 a9f33339 Petr Pudlak
    dictionaries.
1380 a9f33339 Petr Pudlak

1381 a9f33339 Petr Pudlak
    """
1382 a9f33339 Petr Pudlak
    return dict(map(lambda (uuid, ng): (uuid, ng.ToDict()),
1383 a9f33339 Petr Pudlak
                    self._UnlockedGetAllNodeGroupsInfo().items()))
1384 a9f33339 Petr Pudlak
1385 a9f33339 Petr Pudlak
  @locking.ssynchronized(_config_lock, shared=1)
1386 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1387 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1388 1ac6f2ad Guido Trotter

1389 1ac6f2ad Guido Trotter
    """
1390 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1391 1ac6f2ad Guido Trotter
1392 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1393 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1394 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1395 dac81741 Michael Hanselmann

1396 dac81741 Michael Hanselmann
    """
1397 1c3231aa Thomas Thrainer
    ngfn = lambda node_uuid: self._UnlockedGetNodeInfo(node_uuid).group
1398 1c3231aa Thomas Thrainer
    return frozenset(member_uuid
1399 1c3231aa Thomas Thrainer
                     for node_uuid in nodes
1400 1c3231aa Thomas Thrainer
                     for member_uuid in
1401 1c3231aa Thomas Thrainer
                       self._UnlockedGetNodeGroup(ngfn(node_uuid)).members)
1402 dac81741 Michael Hanselmann
1403 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1404 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1405 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1406 080fbeea Michael Hanselmann

1407 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1408 080fbeea Michael Hanselmann
    @rtype: list
1409 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1410 080fbeea Michael Hanselmann

1411 080fbeea Michael Hanselmann
    """
1412 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1413 080fbeea Michael Hanselmann
1414 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1415 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1416 a8083063 Iustin Pop
    """Add an instance to the config.
1417 a8083063 Iustin Pop

1418 a8083063 Iustin Pop
    This should be used after creating a new instance.
1419 a8083063 Iustin Pop

1420 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1421 c41eea6e Iustin Pop
    @param instance: the instance object
1422 c41eea6e Iustin Pop

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

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

1454 430b923c Iustin Pop
    """
1455 430b923c Iustin Pop
    if not item.uuid:
1456 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1457 da4a52a3 Thomas Thrainer
    else:
1458 da4a52a3 Thomas Thrainer
      self._CheckUniqueUUID(item, include_temporary=True)
1459 da4a52a3 Thomas Thrainer
1460 da4a52a3 Thomas Thrainer
  def _CheckUniqueUUID(self, item, include_temporary):
1461 da4a52a3 Thomas Thrainer
    """Checks that the UUID of the given object is unique.
1462 da4a52a3 Thomas Thrainer

1463 da4a52a3 Thomas Thrainer
    @param item: the instance or node to be checked
1464 da4a52a3 Thomas Thrainer
    @param include_temporary: whether temporarily generated UUID's should be
1465 da4a52a3 Thomas Thrainer
              included in the check. If the UUID of the item to be checked is
1466 da4a52a3 Thomas Thrainer
              a temporarily generated one, this has to be C{False}.
1467 da4a52a3 Thomas Thrainer

1468 da4a52a3 Thomas Thrainer
    """
1469 da4a52a3 Thomas Thrainer
    if not item.uuid:
1470 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("'%s' must have an UUID" % (item.name,))
1471 da4a52a3 Thomas Thrainer
    if item.uuid in self._AllIDs(include_temporary=include_temporary):
1472 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1473 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1474 430b923c Iustin Pop
1475 da4a52a3 Thomas Thrainer
  def _SetInstanceStatus(self, inst_uuid, status, disks_active):
1476 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1477 a8083063 Iustin Pop

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

1504 1d4a4b26 Thomas Thrainer
    This also sets the instance disks active flag.
1505 1d4a4b26 Thomas Thrainer

1506 6a408fb2 Iustin Pop
    """
1507 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_UP, True)
1508 57de31c0 Agata Murawska
1509 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1510 da4a52a3 Thomas Thrainer
  def MarkInstanceOffline(self, inst_uuid):
1511 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1512 57de31c0 Agata Murawska

1513 1d4a4b26 Thomas Thrainer
    This also clears the instance disks active flag.
1514 1d4a4b26 Thomas Thrainer

1515 57de31c0 Agata Murawska
    """
1516 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_OFFLINE, False)
1517 6a408fb2 Iustin Pop
1518 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1519 da4a52a3 Thomas Thrainer
  def RemoveInstance(self, inst_uuid):
1520 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1521 a8083063 Iustin Pop

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

1548 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1549 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1550 fc95f88f Iustin Pop
    rename.
1551 fc95f88f Iustin Pop

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

1576 1d4a4b26 Thomas Thrainer
    This does not touch the instance disks active flag, as shut down instances
1577 1d4a4b26 Thomas Thrainer
    can still have active disks.
1578 1d4a4b26 Thomas Thrainer

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

1586 1d4a4b26 Thomas Thrainer
    """
1587 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, True)
1588 1d4a4b26 Thomas Thrainer
1589 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1590 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksInactive(self, inst_uuid):
1591 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks inactive.
1592 1d4a4b26 Thomas Thrainer

1593 a8083063 Iustin Pop
    """
1594 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, False)
1595 a8083063 Iustin Pop
1596 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1597 94bbfece Iustin Pop
    """Get the list of instances.
1598 94bbfece Iustin Pop

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

1601 94bbfece Iustin Pop
    """
1602 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1603 94bbfece Iustin Pop
1604 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1605 a8083063 Iustin Pop
  def GetInstanceList(self):
1606 a8083063 Iustin Pop
    """Get the list of instances.
1607 a8083063 Iustin Pop

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

1610 a8083063 Iustin Pop
    """
1611 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1612 a8083063 Iustin Pop
1613 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1614 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1615 a8083063 Iustin Pop

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

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

1634 94bbfece Iustin Pop
    """
1635 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1636 94bbfece Iustin Pop
      return None
1637 94bbfece Iustin Pop
1638 da4a52a3 Thomas Thrainer
    return self._config_data.instances[inst_uuid]
1639 94bbfece Iustin Pop
1640 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1641 da4a52a3 Thomas Thrainer
  def GetInstanceInfo(self, inst_uuid):
1642 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1643 a8083063 Iustin Pop

1644 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1645 a8083063 Iustin Pop
    an instance are taken from the live systems.
1646 a8083063 Iustin Pop

1647 da4a52a3 Thomas Thrainer
    @param inst_uuid: UUID of the instance
1648 a8083063 Iustin Pop

1649 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1650 c41eea6e Iustin Pop
    @return: the instance object
1651 a8083063 Iustin Pop

1652 a8083063 Iustin Pop
    """
1653 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfo(inst_uuid)
1654 a8083063 Iustin Pop
1655 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1656 da4a52a3 Thomas Thrainer
  def GetInstanceNodeGroups(self, inst_uuid, primary_only=False):
1657 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1658 2674690b Michael Hanselmann

1659 2674690b Michael Hanselmann
    @rtype: frozenset
1660 2674690b Michael Hanselmann

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

1678 922610c9 Dimitris Aragiorgis
    @rtype: frozenset
1679 922610c9 Dimitris Aragiorgis

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

1696 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs
1697 da4a52a3 Thomas Thrainer
    @rtype: list
1698 da4a52a3 Thomas Thrainer
    @return: list of tuples (instance UUID, instance_info), where
1699 da4a52a3 Thomas Thrainer
        instance_info is what would GetInstanceInfo return for the
1700 da4a52a3 Thomas Thrainer
        node, while keeping the original order
1701 da4a52a3 Thomas Thrainer

1702 da4a52a3 Thomas Thrainer
    """
1703 da4a52a3 Thomas Thrainer
    return [(uuid, self._UnlockedGetInstanceInfo(uuid)) for uuid in inst_uuids]
1704 da4a52a3 Thomas Thrainer
1705 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1706 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfoByName(self, inst_names):
1707 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1708 71333cb9 Iustin Pop

1709 da4a52a3 Thomas Thrainer
    @param inst_names: list of instance names
1710 71333cb9 Iustin Pop
    @rtype: list
1711 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1712 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1713 71333cb9 Iustin Pop
        node, while keeping the original order
1714 71333cb9 Iustin Pop

1715 71333cb9 Iustin Pop
    """
1716 da4a52a3 Thomas Thrainer
    result = []
1717 da4a52a3 Thomas Thrainer
    for name in inst_names:
1718 da4a52a3 Thomas Thrainer
      instance = self._UnlockedGetInstanceInfoByName(name)
1719 da4a52a3 Thomas Thrainer
      result.append((instance.uuid, instance))
1720 da4a52a3 Thomas Thrainer
    return result
1721 71333cb9 Iustin Pop
1722 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1723 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1724 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1725 0b2de758 Iustin Pop

1726 0b2de758 Iustin Pop
    @rtype: dict
1727 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1728 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1729 0b2de758 Iustin Pop

1730 0b2de758 Iustin Pop
    """
1731 da4a52a3 Thomas Thrainer
    return self._UnlockedGetAllInstancesInfo()
1732 da4a52a3 Thomas Thrainer
1733 da4a52a3 Thomas Thrainer
  def _UnlockedGetAllInstancesInfo(self):
1734 da4a52a3 Thomas Thrainer
    my_dict = dict([(inst_uuid, self._UnlockedGetInstanceInfo(inst_uuid))
1735 da4a52a3 Thomas Thrainer
                    for inst_uuid in self._UnlockedGetInstanceList()])
1736 0b2de758 Iustin Pop
    return my_dict
1737 0b2de758 Iustin Pop
1738 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1739 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1740 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1741 cc19798f Michael Hanselmann

1742 cc19798f Michael Hanselmann
    @type filter_fn: callable
1743 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1744 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1745 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1746 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1747 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1748 cc19798f Michael Hanselmann

1749 cc19798f Michael Hanselmann
    """
1750 da4a52a3 Thomas Thrainer
    return dict((uuid, inst)
1751 da4a52a3 Thomas Thrainer
                for (uuid, inst) in self._config_data.instances.items()
1752 cc19798f Michael Hanselmann
                if filter_fn(inst))
1753 cc19798f Michael Hanselmann
1754 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1755 da4a52a3 Thomas Thrainer
  def GetInstanceInfoByName(self, inst_name):
1756 da4a52a3 Thomas Thrainer
    """Get the L{objects.Instance} object for a named instance.
1757 da4a52a3 Thomas Thrainer

1758 da4a52a3 Thomas Thrainer
    @param inst_name: name of the instance to get information for
1759 da4a52a3 Thomas Thrainer
    @type inst_name: string
1760 da4a52a3 Thomas Thrainer
    @return: the corresponding L{objects.Instance} instance or None if no
1761 da4a52a3 Thomas Thrainer
          information is available
1762 da4a52a3 Thomas Thrainer

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

1782 da4a52a3 Thomas Thrainer
    @param inst_uuid: instance UUID to get name for
1783 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1784 da4a52a3 Thomas Thrainer
    @rtype: string
1785 da4a52a3 Thomas Thrainer
    @return: instance name
1786 da4a52a3 Thomas Thrainer

1787 da4a52a3 Thomas Thrainer
    """
1788 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceName(inst_uuid)
1789 da4a52a3 Thomas Thrainer
1790 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1791 da4a52a3 Thomas Thrainer
  def GetInstanceNames(self, inst_uuids):
1792 da4a52a3 Thomas Thrainer
    """Gets the instance names for the passed list of nodes.
1793 da4a52a3 Thomas Thrainer

1794 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs to get names for
1795 da4a52a3 Thomas Thrainer
    @type inst_uuids: list of strings
1796 da4a52a3 Thomas Thrainer
    @rtype: list of strings
1797 da4a52a3 Thomas Thrainer
    @return: list of instance names
1798 da4a52a3 Thomas Thrainer

1799 da4a52a3 Thomas Thrainer
    """
1800 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceNames(inst_uuids)
1801 da4a52a3 Thomas Thrainer
1802 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceNames(self, inst_uuids):
1803 da4a52a3 Thomas Thrainer
    return [self._UnlockedGetInstanceName(uuid) for uuid in inst_uuids]
1804 da4a52a3 Thomas Thrainer
1805 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1806 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1807 a8083063 Iustin Pop
    """Add a node to the configuration.
1808 a8083063 Iustin Pop

1809 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1810 c41eea6e Iustin Pop
    @param node: a Node instance
1811 a8083063 Iustin Pop

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

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

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

1858 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1859 c41eea6e Iustin Pop
    held.
1860 f78ede4e Guido Trotter

1861 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1862 a8083063 Iustin Pop

1863 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1864 c41eea6e Iustin Pop
    @return: the node object
1865 a8083063 Iustin Pop

1866 a8083063 Iustin Pop
    """
1867 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
1868 a8083063 Iustin Pop
      return None
1869 a8083063 Iustin Pop
1870 1c3231aa Thomas Thrainer
    return self._config_data.nodes[node_uuid]
1871 a8083063 Iustin Pop
1872 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1873 1c3231aa Thomas Thrainer
  def GetNodeInfo(self, node_uuid):
1874 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1875 f78ede4e Guido Trotter

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

1878 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1879 c41eea6e Iustin Pop

1880 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1881 c41eea6e Iustin Pop
    @return: the node object
1882 f78ede4e Guido Trotter

1883 f78ede4e Guido Trotter
    """
1884 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfo(node_uuid)
1885 f78ede4e Guido Trotter
1886 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1887 1c3231aa Thomas Thrainer
  def GetNodeInstances(self, node_uuid):
1888 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1889 8bf9e9a5 Iustin Pop

1890 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1891 8bf9e9a5 Iustin Pop

1892 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1893 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1894 8bf9e9a5 Iustin Pop

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

1909 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1910 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1911 c71b049c Michael Hanselmann
    @rtype: frozenset
1912 da4a52a3 Thomas Thrainer
    @return: List of instance UUIDs in node group
1913 c71b049c Michael Hanselmann

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

1929 def6577f Helga Velroyen
    @see: C{GetHvparams}
1930 def6577f Helga Velroyen

1931 def6577f Helga Velroyen
    """
1932 def6577f Helga Velroyen
    result = ""
1933 def6577f Helga Velroyen
    hvparams = self._config_data.cluster.hvparams[hvname]
1934 def6577f Helga Velroyen
    for key in hvparams:
1935 def6577f Helga Velroyen
      result += "%s=%s\n" % (key, hvparams[key])
1936 def6577f Helga Velroyen
    return result
1937 def6577f Helga Velroyen
1938 def6577f Helga Velroyen
  @locking.ssynchronized(_config_lock, shared=1)
1939 def6577f Helga Velroyen
  def GetHvparamsString(self, hvname):
1940 def6577f Helga Velroyen
    """Return the hypervisor parameters of the given hypervisor.
1941 def6577f Helga Velroyen

1942 def6577f Helga Velroyen
    @type hvname: string
1943 def6577f Helga Velroyen
    @param hvname: name of a hypervisor
1944 def6577f Helga Velroyen
    @rtype: string
1945 def6577f Helga Velroyen
    @return: string containing key-value-pairs, one pair on each line;
1946 def6577f Helga Velroyen
      format: KEY=VALUE
1947 def6577f Helga Velroyen

1948 def6577f Helga Velroyen
    """
1949 def6577f Helga Velroyen
    return self._UnlockedGetHvparamsString(hvname)
1950 def6577f Helga Velroyen
1951 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1952 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1953 a8083063 Iustin Pop

1954 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1955 c41eea6e Iustin Pop
    held.
1956 c41eea6e Iustin Pop

1957 c41eea6e Iustin Pop
    @rtype: list
1958 f78ede4e Guido Trotter

1959 a8083063 Iustin Pop
    """
1960 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1961 a8083063 Iustin Pop
1962 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1963 f78ede4e Guido Trotter
  def GetNodeList(self):
1964 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1965 f78ede4e Guido Trotter

1966 f78ede4e Guido Trotter
    """
1967 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1968 f78ede4e Guido Trotter
1969 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1970 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1971 94a02bb5 Iustin Pop

1972 94a02bb5 Iustin Pop
    """
1973 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1974 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1975 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.offline]
1976 94a02bb5 Iustin Pop
1977 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1978 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1979 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1980 6819dc49 Iustin Pop

1981 6819dc49 Iustin Pop
    """
1982 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1983 6819dc49 Iustin Pop
1984 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1985 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1986 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1987 075b62ca Iustin Pop

1988 075b62ca Iustin Pop
    """
1989 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1990 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1991 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if node.vm_capable]
1992 075b62ca Iustin Pop
1993 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1994 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1995 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1996 8bf9e9a5 Iustin Pop

1997 8bf9e9a5 Iustin Pop
    """
1998 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1999 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
2000 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.vm_capable]
2001 8bf9e9a5 Iustin Pop
2002 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2003 1c3231aa Thomas Thrainer
  def GetMultiNodeInfo(self, node_uuids):
2004 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
2005 f5eaa3c1 Iustin Pop

2006 1c3231aa Thomas Thrainer
    @param node_uuids: list of node UUIDs
2007 f5eaa3c1 Iustin Pop
    @rtype: list
2008 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
2009 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
2010 f5eaa3c1 Iustin Pop
        order
2011 f5eaa3c1 Iustin Pop

2012 f5eaa3c1 Iustin Pop
    """
2013 1c3231aa Thomas Thrainer
    return [(uuid, self._UnlockedGetNodeInfo(uuid)) for uuid in node_uuids]
2014 1c3231aa Thomas Thrainer
2015 1c3231aa Thomas Thrainer
  def _UnlockedGetAllNodesInfo(self):
2016 1c3231aa Thomas Thrainer
    """Gets configuration of all nodes.
2017 1c3231aa Thomas Thrainer

2018 1c3231aa Thomas Thrainer
    @note: See L{GetAllNodesInfo}
2019 1c3231aa Thomas Thrainer

2020 1c3231aa Thomas Thrainer
    """
2021 1c3231aa Thomas Thrainer
    return dict([(node_uuid, self._UnlockedGetNodeInfo(node_uuid))
2022 1c3231aa Thomas Thrainer
                 for node_uuid in self._UnlockedGetNodeList()])
2023 f5eaa3c1 Iustin Pop
2024 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2025 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
2026 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
2027 d65e5776 Iustin Pop

2028 d65e5776 Iustin Pop
    @rtype: dict
2029 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
2030 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
2031 d65e5776 Iustin Pop

2032 d65e5776 Iustin Pop
    """
2033 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
2034 ee14d800 Michael Hanselmann
2035 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfoByName(self, node_name):
2036 1c3231aa Thomas Thrainer
    for node in self._UnlockedGetAllNodesInfo().values():
2037 1c3231aa Thomas Thrainer
      if node.name == node_name:
2038 1c3231aa Thomas Thrainer
        return node
2039 1c3231aa Thomas Thrainer
    return None
2040 ee14d800 Michael Hanselmann
2041 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2042 1c3231aa Thomas Thrainer
  def GetNodeInfoByName(self, node_name):
2043 1c3231aa Thomas Thrainer
    """Get the L{objects.Node} object for a named node.
2044 1c3231aa Thomas Thrainer

2045 1c3231aa Thomas Thrainer
    @param node_name: name of the node to get information for
2046 1c3231aa Thomas Thrainer
    @type node_name: string
2047 1c3231aa Thomas Thrainer
    @return: the corresponding L{objects.Node} instance or None if no
2048 1c3231aa Thomas Thrainer
          information is available
2049 1c3231aa Thomas Thrainer

2050 1c3231aa Thomas Thrainer
    """
2051 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfoByName(node_name)
2052 1c3231aa Thomas Thrainer
2053 6b2a2942 Petr Pudlak
  @locking.ssynchronized(_config_lock, shared=1)
2054 6b2a2942 Petr Pudlak
  def GetNodeGroupInfoByName(self, nodegroup_name):
2055 6b2a2942 Petr Pudlak
    """Get the L{objects.NodeGroup} object for a named node group.
2056 6b2a2942 Petr Pudlak

2057 6b2a2942 Petr Pudlak
    @param nodegroup_name: name of the node group to get information for
2058 6b2a2942 Petr Pudlak
    @type nodegroup_name: string
2059 6b2a2942 Petr Pudlak
    @return: the corresponding L{objects.NodeGroup} instance or None if no
2060 6b2a2942 Petr Pudlak
          information is available
2061 6b2a2942 Petr Pudlak

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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