Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ bbfa259c

History | View | Annotate | Download (86.7 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

861 4a89c54a Iustin Pop
    """
862 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
863 4a89c54a Iustin Pop
864 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
865 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
866 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1002 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
1003 6d2e83d5 Iustin Pop

1004 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
1005 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
1006 6d2e83d5 Iustin Pop
        an empty list).
1007 6d2e83d5 Iustin Pop

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

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

1024 32388e6d Iustin Pop
    @type instance: string
1025 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
1026 32388e6d Iustin Pop

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

1074 a81c53c9 Iustin Pop
    @type instance: string
1075 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
1076 a81c53c9 Iustin Pop
                     released
1077 a81c53c9 Iustin Pop

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

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

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

1095 61cf6b5e Iustin Pop
    @type instance: string
1096 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
1097 61cf6b5e Iustin Pop
                     released
1098 61cf6b5e Iustin Pop

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

1106 4a8b186a Michael Hanselmann
    @return: Config version
1107 4a8b186a Michael Hanselmann

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

1115 4a8b186a Michael Hanselmann
    @return: Cluster name
1116 4a8b186a Michael Hanselmann

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

1124 4a8b186a Michael Hanselmann
    @return: Master hostname
1125 4a8b186a Michael Hanselmann

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

1133 4a8b186a Michael Hanselmann
    @return: Master IP
1134 4a8b186a Michael Hanselmann

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

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

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

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

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

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

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

1184 c41eea6e Iustin Pop
    @rtype: string
1185 c41eea6e Iustin Pop
    @return: the rsa hostkey
1186 a8083063 Iustin Pop

1187 a8083063 Iustin Pop
    """
1188 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1189 a8083063 Iustin Pop
1190 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1191 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1192 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1193 bf4af505 Apollon Oikonomopoulos

1194 bf4af505 Apollon Oikonomopoulos
    """
1195 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1196 bf4af505 Apollon Oikonomopoulos
1197 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1198 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1199 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1200 868a98ca Manuel Franceschini

1201 868a98ca Manuel Franceschini
    @return: primary ip family
1202 868a98ca Manuel Franceschini

1203 868a98ca Manuel Franceschini
    """
1204 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1205 868a98ca Manuel Franceschini
1206 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1207 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1208 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1209 c9f4b8e6 Andrea Spadaccini

1210 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1211 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1212 c9f4b8e6 Andrea Spadaccini

1213 c9f4b8e6 Andrea Spadaccini
    """
1214 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1215 5ae4945a Iustin Pop
    result = objects.MasterNetworkParameters(
1216 5ae4945a Iustin Pop
      name=cluster.master_node, ip=cluster.master_ip,
1217 5ae4945a Iustin Pop
      netmask=cluster.master_netmask, netdev=cluster.master_netdev,
1218 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1219 c9f4b8e6 Andrea Spadaccini
1220 f9d20654 Andrea Spadaccini
    return result
1221 f9d20654 Andrea Spadaccini
1222 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1223 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1224 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1225 e11a1b77 Adeodato Simo

1226 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1227 90e99856 Adeodato Simo
    according to their default values.
1228 90e99856 Adeodato Simo

1229 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1230 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1231 e11a1b77 Adeodato Simo
    @type ec_id: string
1232 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1233 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1234 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1235 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1236 e11a1b77 Adeodato Simo
                       configuration already
1237 e11a1b77 Adeodato Simo

1238 e11a1b77 Adeodato Simo
    """
1239 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1240 e11a1b77 Adeodato Simo
    self._WriteConfig()
1241 e11a1b77 Adeodato Simo
1242 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1243 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1244 e11a1b77 Adeodato Simo

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

1275 e11a1b77 Adeodato Simo
    @type group_uuid: string
1276 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1277 e11a1b77 Adeodato Simo

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

1294 eaa98a04 Guido Trotter
    @type target: string or None
1295 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1296 eaa98a04 Guido Trotter
    @rtype: string
1297 412b3531 Guido Trotter
    @return: nodegroup UUID
1298 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1299 eaa98a04 Guido Trotter

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

1319 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1320 e85d8982 Stephen Shirley

1321 e85d8982 Stephen Shirley
    @type target: string or None
1322 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1323 e85d8982 Stephen Shirley
    @rtype: string
1324 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1325 e85d8982 Stephen Shirley

1326 e85d8982 Stephen Shirley
    """
1327 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1328 e85d8982 Stephen Shirley
1329 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1330 648e4196 Guido Trotter
    """Lookup a node group.
1331 648e4196 Guido Trotter

1332 648e4196 Guido Trotter
    @type uuid: string
1333 648e4196 Guido Trotter
    @param uuid: group UUID
1334 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1335 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1336 648e4196 Guido Trotter

1337 648e4196 Guido Trotter
    """
1338 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1339 648e4196 Guido Trotter
      return None
1340 648e4196 Guido Trotter
1341 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1342 648e4196 Guido Trotter
1343 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1344 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1345 5768e6a6 René Nussbaumer
    """Lookup a node group.
1346 5768e6a6 René Nussbaumer

1347 5768e6a6 René Nussbaumer
    @type uuid: string
1348 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1349 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1350 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1351 5768e6a6 René Nussbaumer

1352 5768e6a6 René Nussbaumer
    """
1353 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1354 5768e6a6 René Nussbaumer
1355 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1356 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1357 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1358 622444e5 Iustin Pop

1359 622444e5 Iustin Pop
    """
1360 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1361 622444e5 Iustin Pop
1362 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1363 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1364 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1365 1ac6f2ad Guido Trotter

1366 1ac6f2ad Guido Trotter
    """
1367 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1368 1ac6f2ad Guido Trotter
1369 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1370 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1371 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1372 dac81741 Michael Hanselmann

1373 dac81741 Michael Hanselmann
    """
1374 dac81741 Michael Hanselmann
    ngfn = lambda node_name: self._UnlockedGetNodeInfo(node_name).group
1375 dac81741 Michael Hanselmann
    return frozenset(member_name
1376 dac81741 Michael Hanselmann
                     for node_name in nodes
1377 dac81741 Michael Hanselmann
                     for member_name in
1378 dac81741 Michael Hanselmann
                       self._UnlockedGetNodeGroup(ngfn(node_name)).members)
1379 dac81741 Michael Hanselmann
1380 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1381 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1382 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1383 080fbeea Michael Hanselmann

1384 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1385 080fbeea Michael Hanselmann
    @rtype: list
1386 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1387 080fbeea Michael Hanselmann

1388 080fbeea Michael Hanselmann
    """
1389 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1390 080fbeea Michael Hanselmann
1391 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1392 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1393 a8083063 Iustin Pop
    """Add an instance to the config.
1394 a8083063 Iustin Pop

1395 a8083063 Iustin Pop
    This should be used after creating a new instance.
1396 a8083063 Iustin Pop

1397 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1398 c41eea6e Iustin Pop
    @param instance: the instance object
1399 c41eea6e Iustin Pop

1400 a8083063 Iustin Pop
    """
1401 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1402 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1403 a8083063 Iustin Pop
1404 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1405 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1406 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1407 923b1523 Iustin Pop
1408 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1409 e4640214 Guido Trotter
    for nic in instance.nics:
1410 e4640214 Guido Trotter
      if nic.mac in all_macs:
1411 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1412 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1413 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1414 430b923c Iustin Pop
1415 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1416 e4640214 Guido Trotter
1417 b989e85d Iustin Pop
    instance.serial_no = 1
1418 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1419 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1420 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1421 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1422 e8e079f3 Dimitris Aragiorgis
    self._UnlockedCommitTemporaryIps(ec_id)
1423 a8083063 Iustin Pop
    self._WriteConfig()
1424 a8083063 Iustin Pop
1425 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1426 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1427 430b923c Iustin Pop

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

1431 430b923c Iustin Pop
    """
1432 430b923c Iustin Pop
    if not item.uuid:
1433 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1434 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1435 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1436 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1437 430b923c Iustin Pop
1438 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1439 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1440 a8083063 Iustin Pop

1441 a8083063 Iustin Pop
    """
1442 2e04d454 Agata Murawska
    assert status in constants.ADMINST_ALL, \
1443 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1444 a8083063 Iustin Pop
1445 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1446 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1447 3ecf6786 Iustin Pop
                                      instance_name)
1448 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1449 9ca8a7c5 Agata Murawska
    if instance.admin_state != status:
1450 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1451 b989e85d Iustin Pop
      instance.serial_no += 1
1452 d693c864 Iustin Pop
      instance.mtime = time.time()
1453 455a3445 Iustin Pop
      self._WriteConfig()
1454 a8083063 Iustin Pop
1455 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1456 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1457 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1458 6a408fb2 Iustin Pop

1459 6a408fb2 Iustin Pop
    """
1460 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_UP)
1461 57de31c0 Agata Murawska
1462 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1463 57de31c0 Agata Murawska
  def MarkInstanceOffline(self, instance_name):
1464 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1465 57de31c0 Agata Murawska

1466 57de31c0 Agata Murawska
    """
1467 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_OFFLINE)
1468 6a408fb2 Iustin Pop
1469 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1470 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1471 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1472 a8083063 Iustin Pop

1473 a8083063 Iustin Pop
    """
1474 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1475 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1476 f396ad8c Vangelis Koukis
1477 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1478 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1479 f396ad8c Vangelis Koukis
    inst = self._config_data.instances[instance_name]
1480 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1481 f396ad8c Vangelis Koukis
    if network_port is not None:
1482 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1483 f396ad8c Vangelis Koukis
1484 ced51149 Dimitris Aragiorgis
    instance = self._UnlockedGetInstanceInfo(instance_name)
1485 ced51149 Dimitris Aragiorgis
1486 ced51149 Dimitris Aragiorgis
    for nic in instance.nics:
1487 9394f4d1 Dimitris Aragiorgis
      if nic.network and nic.ip:
1488 1b68f268 Helga Velroyen
        # Return all IP addresses to the respective address pools
1489 9394f4d1 Dimitris Aragiorgis
        self._UnlockedCommitIp(constants.RELEASE_ACTION, nic.network, nic.ip)
1490 ced51149 Dimitris Aragiorgis
1491 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1492 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1493 a8083063 Iustin Pop
    self._WriteConfig()
1494 a8083063 Iustin Pop
1495 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1496 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1497 fc95f88f Iustin Pop
    """Rename an instance.
1498 fc95f88f Iustin Pop

1499 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1500 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1501 fc95f88f Iustin Pop
    rename.
1502 fc95f88f Iustin Pop

1503 fc95f88f Iustin Pop
    """
1504 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
1505 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
1506 ea642319 Michael Hanselmann
1507 ea642319 Michael Hanselmann
    # Operate on a copy to not loose instance object in case of a failure
1508 ea642319 Michael Hanselmann
    inst = self._config_data.instances[old_name].Copy()
1509 fc95f88f Iustin Pop
    inst.name = new_name
1510 b23c4333 Manuel Franceschini
1511 ea642319 Michael Hanselmann
    for (idx, disk) in enumerate(inst.disks):
1512 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1513 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1514 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1515 ea642319 Michael Hanselmann
        disk.logical_id = (disk.logical_id[0],
1516 ea642319 Michael Hanselmann
                           utils.PathJoin(file_storage_dir, inst.name,
1517 ea642319 Michael Hanselmann
                                          "disk%s" % idx))
1518 ea642319 Michael Hanselmann
        disk.physical_id = disk.logical_id
1519 ea642319 Michael Hanselmann
1520 ea642319 Michael Hanselmann
    # Actually replace instance object
1521 ea642319 Michael Hanselmann
    del self._config_data.instances[old_name]
1522 ea642319 Michael Hanselmann
    self._config_data.instances[inst.name] = inst
1523 b23c4333 Manuel Franceschini
1524 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1525 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1526 1fc34c48 Michael Hanselmann
1527 fc95f88f Iustin Pop
    self._WriteConfig()
1528 fc95f88f Iustin Pop
1529 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1530 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
1531 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1532 a8083063 Iustin Pop

1533 a8083063 Iustin Pop
    """
1534 2e04d454 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_DOWN)
1535 a8083063 Iustin Pop
1536 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1537 94bbfece Iustin Pop
    """Get the list of instances.
1538 94bbfece Iustin Pop

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

1541 94bbfece Iustin Pop
    """
1542 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1543 94bbfece Iustin Pop
1544 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1545 a8083063 Iustin Pop
  def GetInstanceList(self):
1546 a8083063 Iustin Pop
    """Get the list of instances.
1547 a8083063 Iustin Pop

1548 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1549 c41eea6e Iustin Pop
        'instance1.example.com']
1550 a8083063 Iustin Pop

1551 a8083063 Iustin Pop
    """
1552 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1553 a8083063 Iustin Pop
1554 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1555 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1556 a8083063 Iustin Pop

1557 a8083063 Iustin Pop
    """
1558 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1559 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1560 a8083063 Iustin Pop
1561 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1562 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1563 94bbfece Iustin Pop

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

1566 94bbfece Iustin Pop
    """
1567 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1568 94bbfece Iustin Pop
      return None
1569 94bbfece Iustin Pop
1570 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1571 94bbfece Iustin Pop
1572 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1573 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1574 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1575 a8083063 Iustin Pop

1576 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1577 a8083063 Iustin Pop
    an instance are taken from the live systems.
1578 a8083063 Iustin Pop

1579 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1580 c41eea6e Iustin Pop
        I{instance1.example.com}
1581 a8083063 Iustin Pop

1582 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1583 c41eea6e Iustin Pop
    @return: the instance object
1584 a8083063 Iustin Pop

1585 a8083063 Iustin Pop
    """
1586 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1587 a8083063 Iustin Pop
1588 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1589 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1590 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1591 2674690b Michael Hanselmann

1592 2674690b Michael Hanselmann
    @rtype: frozenset
1593 2674690b Michael Hanselmann

1594 2674690b Michael Hanselmann
    """
1595 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1596 2674690b Michael Hanselmann
    if not instance:
1597 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1598 2674690b Michael Hanselmann
1599 2674690b Michael Hanselmann
    if primary_only:
1600 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1601 2674690b Michael Hanselmann
    else:
1602 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1603 2674690b Michael Hanselmann
1604 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1605 2674690b Michael Hanselmann
                     for node_name in nodes)
1606 2674690b Michael Hanselmann
1607 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1608 922610c9 Dimitris Aragiorgis
  def GetInstanceNetworks(self, instance_name):
1609 922610c9 Dimitris Aragiorgis
    """Returns set of network UUIDs for instance's nics.
1610 922610c9 Dimitris Aragiorgis

1611 922610c9 Dimitris Aragiorgis
    @rtype: frozenset
1612 922610c9 Dimitris Aragiorgis

1613 922610c9 Dimitris Aragiorgis
    """
1614 922610c9 Dimitris Aragiorgis
    instance = self._UnlockedGetInstanceInfo(instance_name)
1615 922610c9 Dimitris Aragiorgis
    if not instance:
1616 922610c9 Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1617 922610c9 Dimitris Aragiorgis
1618 922610c9 Dimitris Aragiorgis
    networks = set()
1619 922610c9 Dimitris Aragiorgis
    for nic in instance.nics:
1620 922610c9 Dimitris Aragiorgis
      if nic.network:
1621 922610c9 Dimitris Aragiorgis
        networks.add(nic.network)
1622 922610c9 Dimitris Aragiorgis
1623 922610c9 Dimitris Aragiorgis
    return frozenset(networks)
1624 922610c9 Dimitris Aragiorgis
1625 922610c9 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
1626 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1627 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1628 71333cb9 Iustin Pop

1629 71333cb9 Iustin Pop
    @param instances: list of instance names
1630 71333cb9 Iustin Pop
    @rtype: list
1631 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1632 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1633 71333cb9 Iustin Pop
        node, while keeping the original order
1634 71333cb9 Iustin Pop

1635 71333cb9 Iustin Pop
    """
1636 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1637 71333cb9 Iustin Pop
1638 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1639 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1640 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1641 0b2de758 Iustin Pop

1642 0b2de758 Iustin Pop
    @rtype: dict
1643 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1644 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1645 0b2de758 Iustin Pop

1646 0b2de758 Iustin Pop
    """
1647 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1648 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1649 0b2de758 Iustin Pop
    return my_dict
1650 0b2de758 Iustin Pop
1651 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1652 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1653 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1654 cc19798f Michael Hanselmann

1655 cc19798f Michael Hanselmann
    @type filter_fn: callable
1656 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1657 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1658 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1659 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1660 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1661 cc19798f Michael Hanselmann

1662 cc19798f Michael Hanselmann
    """
1663 cc19798f Michael Hanselmann
    return dict((name, inst)
1664 cc19798f Michael Hanselmann
                for (name, inst) in self._config_data.instances.items()
1665 cc19798f Michael Hanselmann
                if filter_fn(inst))
1666 cc19798f Michael Hanselmann
1667 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1668 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1669 a8083063 Iustin Pop
    """Add a node to the configuration.
1670 a8083063 Iustin Pop

1671 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1672 c41eea6e Iustin Pop
    @param node: a Node instance
1673 a8083063 Iustin Pop

1674 a8083063 Iustin Pop
    """
1675 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1676 d8470559 Michael Hanselmann
1677 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1678 430b923c Iustin Pop
1679 b989e85d Iustin Pop
    node.serial_no = 1
1680 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1681 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1682 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1683 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1684 a8083063 Iustin Pop
    self._WriteConfig()
1685 a8083063 Iustin Pop
1686 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1687 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1688 a8083063 Iustin Pop
    """Remove a node from the configuration.
1689 a8083063 Iustin Pop

1690 a8083063 Iustin Pop
    """
1691 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1692 d8470559 Michael Hanselmann
1693 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1694 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1695 a8083063 Iustin Pop
1696 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1697 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1698 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1699 a8083063 Iustin Pop
    self._WriteConfig()
1700 a8083063 Iustin Pop
1701 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1702 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1703 a8083063 Iustin Pop

1704 a8083063 Iustin Pop
    """
1705 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1706 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1707 a8083063 Iustin Pop
1708 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1709 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1710 a8083063 Iustin Pop

1711 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1712 c41eea6e Iustin Pop
    held.
1713 f78ede4e Guido Trotter

1714 c41eea6e Iustin Pop
    @param node_name: the node name, e.g. I{node1.example.com}
1715 a8083063 Iustin Pop

1716 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1717 c41eea6e Iustin Pop
    @return: the node object
1718 a8083063 Iustin Pop

1719 a8083063 Iustin Pop
    """
1720 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1721 a8083063 Iustin Pop
      return None
1722 a8083063 Iustin Pop
1723 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1724 a8083063 Iustin Pop
1725 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1726 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1727 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1728 f78ede4e Guido Trotter

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

1731 c41eea6e Iustin Pop
    @param node_name: the node name, e.g. I{node1.example.com}
1732 c41eea6e Iustin Pop

1733 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1734 c41eea6e Iustin Pop
    @return: the node object
1735 f78ede4e Guido Trotter

1736 f78ede4e Guido Trotter
    """
1737 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1738 f78ede4e Guido Trotter
1739 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1740 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1741 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1742 8bf9e9a5 Iustin Pop

1743 8bf9e9a5 Iustin Pop
    @param node_name: the node name, e.g. I{node1.example.com}
1744 8bf9e9a5 Iustin Pop

1745 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1746 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1747 8bf9e9a5 Iustin Pop

1748 8bf9e9a5 Iustin Pop
    """
1749 8bf9e9a5 Iustin Pop
    pri = []
1750 8bf9e9a5 Iustin Pop
    sec = []
1751 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1752 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1753 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1754 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1755 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1756 8bf9e9a5 Iustin Pop
    return (pri, sec)
1757 8bf9e9a5 Iustin Pop
1758 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1759 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1760 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1761 c71b049c Michael Hanselmann

1762 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1763 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1764 c71b049c Michael Hanselmann
    @rtype: frozenset
1765 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1766 c71b049c Michael Hanselmann

1767 c71b049c Michael Hanselmann
    """
1768 c71b049c Michael Hanselmann
    if primary_only:
1769 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1770 c71b049c Michael Hanselmann
    else:
1771 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1772 c71b049c Michael Hanselmann
1773 c71b049c Michael Hanselmann
    return frozenset(inst.name
1774 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1775 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1776 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1777 c71b049c Michael Hanselmann
1778 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1779 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1780 a8083063 Iustin Pop

1781 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1782 c41eea6e Iustin Pop
    held.
1783 c41eea6e Iustin Pop

1784 c41eea6e Iustin Pop
    @rtype: list
1785 f78ede4e Guido Trotter

1786 a8083063 Iustin Pop
    """
1787 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1788 a8083063 Iustin Pop
1789 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1790 f78ede4e Guido Trotter
  def GetNodeList(self):
1791 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1792 f78ede4e Guido Trotter

1793 f78ede4e Guido Trotter
    """
1794 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1795 f78ede4e Guido Trotter
1796 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1797 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1798 94a02bb5 Iustin Pop

1799 94a02bb5 Iustin Pop
    """
1800 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1801 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1802 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1803 94a02bb5 Iustin Pop
1804 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1805 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1806 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1807 6819dc49 Iustin Pop

1808 6819dc49 Iustin Pop
    """
1809 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1810 6819dc49 Iustin Pop
1811 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1812 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1813 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1814 075b62ca Iustin Pop

1815 075b62ca Iustin Pop
    """
1816 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1817 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1818 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1819 075b62ca Iustin Pop
1820 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1821 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1822 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1823 8bf9e9a5 Iustin Pop

1824 8bf9e9a5 Iustin Pop
    """
1825 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1826 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1827 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1828 8bf9e9a5 Iustin Pop
1829 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1830 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1831 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1832 f5eaa3c1 Iustin Pop

1833 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1834 f5eaa3c1 Iustin Pop
    @rtype: list
1835 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1836 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1837 f5eaa3c1 Iustin Pop
        order
1838 f5eaa3c1 Iustin Pop

1839 f5eaa3c1 Iustin Pop
    """
1840 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1841 f5eaa3c1 Iustin Pop
1842 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1843 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1844 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1845 d65e5776 Iustin Pop

1846 d65e5776 Iustin Pop
    @rtype: dict
1847 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1848 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1849 d65e5776 Iustin Pop

1850 d65e5776 Iustin Pop
    """
1851 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1852 ee14d800 Michael Hanselmann
1853 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1854 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1855 ee14d800 Michael Hanselmann

1856 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1857 ee14d800 Michael Hanselmann

1858 ee14d800 Michael Hanselmann
    """
1859 ee14d800 Michael Hanselmann
    return dict([(node, self._UnlockedGetNodeInfo(node))
1860 ee14d800 Michael Hanselmann
                 for node in self._UnlockedGetNodeList()])
1861 d65e5776 Iustin Pop
1862 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1863 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1864 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1865 9d5b1371 Michael Hanselmann

1866 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1867 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1868 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1869 9d5b1371 Michael Hanselmann

1870 9d5b1371 Michael Hanselmann
    """
1871 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1872 9d5b1371 Michael Hanselmann
1873 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1874 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1875 ec0292f1 Iustin Pop

1876 23f06b2b Iustin Pop
    @type exceptions: list
1877 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1878 ec0292f1 Iustin Pop
    @rtype: tuple
1879 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1880 ec0292f1 Iustin Pop

1881 ec0292f1 Iustin Pop
    """
1882 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1883 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1884 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1885 23f06b2b Iustin Pop
        continue
1886 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1887 ec0292f1 Iustin Pop
        mc_max += 1
1888 ec0292f1 Iustin Pop
      if node.master_candidate:
1889 ec0292f1 Iustin Pop
        mc_now += 1
1890 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1891 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1892 ec0292f1 Iustin Pop
1893 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1894 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1895 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1896 ec0292f1 Iustin Pop

1897 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1898 ec0292f1 Iustin Pop

1899 23f06b2b Iustin Pop
    @type exceptions: list
1900 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1901 ec0292f1 Iustin Pop
    @rtype: tuple
1902 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1903 ec0292f1 Iustin Pop

1904 ec0292f1 Iustin Pop
    """
1905 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1906 ec0292f1 Iustin Pop
1907 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1908 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1909 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1910 ec0292f1 Iustin Pop

1911 44485f49 Guido Trotter
    @type exceptions: list
1912 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1913 ec0292f1 Iustin Pop
    @rtype: list
1914 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1915 ec0292f1 Iustin Pop

1916 ec0292f1 Iustin Pop
    """
1917 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1918 ec0292f1 Iustin Pop
    mod_list = []
1919 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1920 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1921 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1922 ec0292f1 Iustin Pop
      for name in node_list:
1923 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1924 ec0292f1 Iustin Pop
          break
1925 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1926 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1927 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1928 ec0292f1 Iustin Pop
          continue
1929 ee513a66 Iustin Pop
        mod_list.append(node)
1930 ec0292f1 Iustin Pop
        node.master_candidate = True
1931 ec0292f1 Iustin Pop
        node.serial_no += 1
1932 ec0292f1 Iustin Pop
        mc_now += 1
1933 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1934 ec0292f1 Iustin Pop
        # this should not happen
1935 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1936 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1937 ec0292f1 Iustin Pop
      if mod_list:
1938 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1939 ec0292f1 Iustin Pop
        self._WriteConfig()
1940 ec0292f1 Iustin Pop
1941 ec0292f1 Iustin Pop
    return mod_list
1942 ec0292f1 Iustin Pop
1943 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1944 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1945 190e3cb6 Guido Trotter

1946 190e3cb6 Guido Trotter
    """
1947 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1948 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1949 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1950 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1951 190e3cb6 Guido Trotter
      # is not found anymore.
1952 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1953 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1954 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1955 190e3cb6 Guido Trotter
1956 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1957 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1958 190e3cb6 Guido Trotter

1959 190e3cb6 Guido Trotter
    """
1960 f936c153 Iustin Pop
    nodegroup = node.group
1961 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1962 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1963 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1964 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1965 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1966 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1967 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1968 190e3cb6 Guido Trotter
    else:
1969 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1970 190e3cb6 Guido Trotter
1971 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
1972 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
1973 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
1974 54c31fd3 Michael Hanselmann

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

1978 54c31fd3 Michael Hanselmann
    """
1979 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
1980 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
1981 54c31fd3 Michael Hanselmann
1982 54c31fd3 Michael Hanselmann
    resmod = []
1983 54c31fd3 Michael Hanselmann
1984 54c31fd3 Michael Hanselmann
    # Try to resolve names/UUIDs first
1985 54c31fd3 Michael Hanselmann
    for (node_name, new_group_uuid) in mods:
1986 54c31fd3 Michael Hanselmann
      try:
1987 54c31fd3 Michael Hanselmann
        node = nodes[node_name]
1988 54c31fd3 Michael Hanselmann
      except KeyError:
1989 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find node '%s'" % node_name)
1990 54c31fd3 Michael Hanselmann
1991 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
1992 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
1993 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
1994 54c31fd3 Michael Hanselmann
                      node_name, node.group)
1995 54c31fd3 Michael Hanselmann
        continue
1996 54c31fd3 Michael Hanselmann
1997 54c31fd3 Michael Hanselmann
      # Try to find current group of node
1998 54c31fd3 Michael Hanselmann
      try:
1999 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
2000 54c31fd3 Michael Hanselmann
      except KeyError:
2001 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
2002 54c31fd3 Michael Hanselmann
                                        node.group)
2003 54c31fd3 Michael Hanselmann
2004 54c31fd3 Michael Hanselmann
      # Try to find new group for node
2005 54c31fd3 Michael Hanselmann
      try:
2006 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
2007 54c31fd3 Michael Hanselmann
      except KeyError:
2008 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
2009 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
2010 54c31fd3 Michael Hanselmann
2011 54c31fd3 Michael Hanselmann
      assert node.name in old_group.members, \
2012 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
2013 54c31fd3 Michael Hanselmann
         " old group '%s'" % (node.name, old_group.uuid))
2014 54c31fd3 Michael Hanselmann
      assert node.name not in new_group.members, \
2015 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
2016 54c31fd3 Michael Hanselmann
         " its new group '%s'" % (node.name, new_group.uuid))
2017 54c31fd3 Michael Hanselmann
2018 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
2019 54c31fd3 Michael Hanselmann
2020 54c31fd3 Michael Hanselmann
    # Apply changes
2021 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
2022 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
2023 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
2024 54c31fd3 Michael Hanselmann
2025 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
2026 54c31fd3 Michael Hanselmann
2027 54c31fd3 Michael Hanselmann
      # Update members of involved groups
2028 54c31fd3 Michael Hanselmann
      if node.name in old_group.members:
2029 54c31fd3 Michael Hanselmann
        old_group.members.remove(node.name)
2030 54c31fd3 Michael Hanselmann
      if node.name not in new_group.members:
2031 54c31fd3 Michael Hanselmann
        new_group.members.append(node.name)
2032 54c31fd3 Michael Hanselmann
2033 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
2034 54c31fd3 Michael Hanselmann
    now = time.time()
2035 75191077 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142
2036 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
2037 54c31fd3 Michael Hanselmann
      obj.mtime = now
2038 54c31fd3 Michael Hanselmann
2039 54c31fd3 Michael Hanselmann
    # Force ssconf update
2040 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
2041 54c31fd3 Michael Hanselmann
2042 54c31fd3 Michael Hanselmann
    self._WriteConfig()
2043 54c31fd3 Michael Hanselmann
2044 a8083063 Iustin Pop
  def _BumpSerialNo(self):
2045 a8083063 Iustin Pop
    """Bump up the serial number of the config.
2046 a8083063 Iustin Pop

2047 a8083063 Iustin Pop
    """
2048 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
2049 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
2050 a8083063 Iustin Pop
2051 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
2052 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
2053 76d5d3a3 Iustin Pop

2054 76d5d3a3 Iustin Pop
    """
2055 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
2056 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
2057 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
2058 e0519c34 Dimitris Aragiorgis
            self._config_data.networks.values() +
2059 b87a9c5f Christos Stavrakakis
            self._AllDisks() +
2060 b87a9c5f Christos Stavrakakis
            self._AllNICs() +
2061 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
2062 76d5d3a3 Iustin Pop
2063 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
2064 a8083063 Iustin Pop
    """Read the config data from disk.
2065 a8083063 Iustin Pop

2066 a8083063 Iustin Pop
    """
2067 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
2068 13998ef2 Michael Hanselmann
2069 a8083063 Iustin Pop
    try:
2070 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
2071 13998ef2 Michael Hanselmann
    except Exception, err:
2072 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
2073 5b263ed7 Michael Hanselmann
2074 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
2075 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
2076 5b263ed7 Michael Hanselmann
2077 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
2078 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
2079 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
2080 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
2081 90d726a8 Iustin Pop
2082 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
2083 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
2084 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
2085 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
2086 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
2087 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
2088 eb180fe2 Iustin Pop
2089 a8083063 Iustin Pop
    self._config_data = data
2090 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
2091 0779e3aa Iustin Pop
    # ssconf update
2092 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
2093 a8083063 Iustin Pop
2094 45f62156 Bernardo Dal Seno
    # Upgrade configuration if needed
2095 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
2096 76d5d3a3 Iustin Pop
2097 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
2098 bd407597 Iustin Pop
2099 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
2100 45f62156 Bernardo Dal Seno
    """Run any upgrade steps.
2101 76d5d3a3 Iustin Pop

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

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

2112 76d5d3a3 Iustin Pop
    """
2113 45f62156 Bernardo Dal Seno
    # Keep a copy of the persistent part of _config_data to check for changes
2114 45f62156 Bernardo Dal Seno
    # Serialization doesn't guarantee order in dictionaries
2115 45f62156 Bernardo Dal Seno
    oldconf = copy.deepcopy(self._config_data.ToDict())
2116 45f62156 Bernardo Dal Seno
2117 45f62156 Bernardo Dal Seno
    # In-object upgrades
2118 45f62156 Bernardo Dal Seno
    self._config_data.UpgradeConfig()
2119 45f62156 Bernardo Dal Seno
2120 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
2121 76d5d3a3 Iustin Pop
      if item.uuid is None:
2122 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
2123 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
2124 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
2125 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
2126 75cf411a Adeodato Simo
                                            members=[])
2127 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
2128 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
2129 f936c153 Iustin Pop
      if not node.group:
2130 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
2131 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
2132 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
2133 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
2134 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
2135 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
2136 45f62156 Bernardo Dal Seno
2137 45f62156 Bernardo Dal Seno
    modified = (oldconf != self._config_data.ToDict())
2138 76d5d3a3 Iustin Pop
    if modified:
2139 76d5d3a3 Iustin Pop
      self._WriteConfig()
2140 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
2141 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
2142 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
2143 04db1880 Bernardo Dal Seno
    else:
2144 04db1880 Bernardo Dal Seno
      config_errors = self._UnlockedVerifyConfig()
2145 04db1880 Bernardo Dal Seno
      if config_errors:
2146 04db1880 Bernardo Dal Seno
        errmsg = ("Loaded configuration data is not consistent: %s" %
2147 04db1880 Bernardo Dal Seno
                  (utils.CommaJoin(config_errors)))
2148 04db1880 Bernardo Dal Seno
        logging.critical(errmsg)
2149 4fae38c5 Guido Trotter
2150 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
2151 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
2152 a8083063 Iustin Pop

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

2156 a8083063 Iustin Pop
    """
2157 a8083063 Iustin Pop
    if self._offline:
2158 a8083063 Iustin Pop
      return True
2159 a4eae71f Michael Hanselmann
2160 a8083063 Iustin Pop
    bad = False
2161 a8083063 Iustin Pop
2162 6a5b8b4b Iustin Pop
    node_list = []
2163 6a5b8b4b Iustin Pop
    addr_list = []
2164 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
2165 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
2166 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
2167 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
2168 6b294c53 Iustin Pop
    # in between
2169 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
2170 6a5b8b4b Iustin Pop
      if node_name == myhostname:
2171 6a5b8b4b Iustin Pop
        continue
2172 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
2173 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
2174 6a5b8b4b Iustin Pop
        continue
2175 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
2176 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
2177 6b294c53 Iustin Pop
2178 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
2179 415a7304 Michael Hanselmann
    result = \
2180 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
2181 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
2182 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
2183 1b54fc6c Guido Trotter
      if msg:
2184 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
2185 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
2186 1b54fc6c Guido Trotter
        logging.error(msg)
2187 a4eae71f Michael Hanselmann
2188 a4eae71f Michael Hanselmann
        if feedback_fn:
2189 a4eae71f Michael Hanselmann
          feedback_fn(msg)
2190 a4eae71f Michael Hanselmann
2191 a8083063 Iustin Pop
        bad = True
2192 a4eae71f Michael Hanselmann
2193 a8083063 Iustin Pop
    return not bad
2194 a8083063 Iustin Pop
2195 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
2196 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
2197 a8083063 Iustin Pop

2198 a8083063 Iustin Pop
    """
2199 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
2200 a4eae71f Michael Hanselmann
2201 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
2202 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
2203 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
2204 d2231b8c Iustin Pop
    # recovery to the user
2205 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
2206 4a89c54a Iustin Pop
    if config_errors:
2207 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
2208 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
2209 d2231b8c Iustin Pop
      logging.critical(errmsg)
2210 d2231b8c Iustin Pop
      if feedback_fn:
2211 d2231b8c Iustin Pop
        feedback_fn(errmsg)
2212 d2231b8c Iustin Pop
2213 a8083063 Iustin Pop
    if destination is None:
2214 a8083063 Iustin Pop
      destination = self._cfg_file
2215 a8083063 Iustin Pop
    self._BumpSerialNo()
2216 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
2217 13998ef2 Michael Hanselmann
2218 e60c73a1 René Nussbaumer
    getents = self._getents()
2219 bd407597 Iustin Pop
    try:
2220 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
2221 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
2222 bd407597 Iustin Pop
    except errors.LockError:
2223 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
2224 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
2225 bd407597 Iustin Pop
                                      " update")
2226 bd407597 Iustin Pop
    try:
2227 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
2228 bd407597 Iustin Pop
    finally:
2229 bd407597 Iustin Pop
      os.close(fd)
2230 13998ef2 Michael Hanselmann
2231 14e15659 Iustin Pop
    self.write_count += 1
2232 3d3a04bc Iustin Pop
2233 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
2234 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
2235 a8083063 Iustin Pop
2236 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
2237 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
2238 d9a855f1 Michael Hanselmann
      if not self._offline:
2239 b2acdbdc Michael Hanselmann
        result = self._GetRpc(None).call_write_ssconf_files(
2240 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
2241 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
2242 a4eae71f Michael Hanselmann
2243 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
2244 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
2245 e1e75d00 Iustin Pop
          if msg:
2246 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
2247 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
2248 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
2249 a4eae71f Michael Hanselmann
2250 a4eae71f Michael Hanselmann
            if feedback_fn:
2251 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
2252 a4eae71f Michael Hanselmann
2253 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
2254 54d1a06e Michael Hanselmann
2255 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
2256 054596f0 Iustin Pop
    """Return the values needed by ssconf.
2257 054596f0 Iustin Pop

2258 054596f0 Iustin Pop
    @rtype: dict
2259 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2260 054596f0 Iustin Pop
        associated value
2261 054596f0 Iustin Pop

2262 054596f0 Iustin Pop
    """
2263 a3316e4a Iustin Pop
    fn = "\n".join
2264 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
2265 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
2266 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
2267 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
2268 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
2269 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
2270 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
2271 a3316e4a Iustin Pop
2272 81a49123 Iustin Pop
    instance_data = fn(instance_names)
2273 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
2274 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
2275 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
2276 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
2277 8113a52e Luca Bigliardi
                     if node.master_candidate)
2278 a3316e4a Iustin Pop
    node_data = fn(node_names)
2279 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
2280 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
2281 f56618e0 Iustin Pop
2282 054596f0 Iustin Pop
    cluster = self._config_data.cluster
2283 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
2284 4f7a6a10 Iustin Pop
2285 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
2286 4f7a6a10 Iustin Pop
2287 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
2288 0fbae49a Balazs Lecz
2289 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
2290 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
2291 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
2292 6c0a75db Dimitris Aragiorgis
    networks = ["%s %s" % (net.uuid, net.name) for net in
2293 6c0a75db Dimitris Aragiorgis
                self._config_data.networks.values()]
2294 6c0a75db Dimitris Aragiorgis
    networks_data = fn(utils.NiceSort(networks))
2295 6f076453 Guido Trotter
2296 2afc9238 Iustin Pop
    ssconf_values = {
2297 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
2298 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
2299 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
2300 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
2301 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
2302 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
2303 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
2304 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
2305 5a8648eb Andrea Spadaccini
      constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
2306 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
2307 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
2308 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
2309 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
2310 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
2311 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
2312 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
2313 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
2314 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
2315 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
2316 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
2317 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
2318 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
2319 6c0a75db Dimitris Aragiorgis
      constants.SS_NETWORKS: networks_data,
2320 03d1dba2 Michael Hanselmann
      }
2321 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
2322 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
2323 2afc9238 Iustin Pop
    if bad_values:
2324 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
2325 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
2326 2afc9238 Iustin Pop
                                      " values: %s" % err)
2327 2afc9238 Iustin Pop
    return ssconf_values
2328 03d1dba2 Michael Hanselmann
2329 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2330 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
2331 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
2332 d367b66c Manuel Franceschini

2333 d367b66c Manuel Franceschini
    """
2334 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2335 d367b66c Manuel Franceschini
2336 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2337 a8083063 Iustin Pop
  def GetVGName(self):
2338 a8083063 Iustin Pop
    """Return the volume group name.
2339 a8083063 Iustin Pop

2340 a8083063 Iustin Pop
    """
2341 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2342 a8083063 Iustin Pop
2343 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2344 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2345 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2346 89ff8e15 Manuel Franceschini

2347 89ff8e15 Manuel Franceschini
    """
2348 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2349 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2350 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2351 89ff8e15 Manuel Franceschini
2352 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2353 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2354 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2355 9e33896b Luca Bigliardi

2356 9e33896b Luca Bigliardi
    """
2357 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2358 9e33896b Luca Bigliardi
2359 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2360 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2361 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2362 9e33896b Luca Bigliardi

2363 9e33896b Luca Bigliardi
    """
2364 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2365 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2366 9e33896b Luca Bigliardi
    self._WriteConfig()
2367 9e33896b Luca Bigliardi
2368 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2369 a8083063 Iustin Pop
  def GetMACPrefix(self):
2370 a8083063 Iustin Pop
    """Return the mac prefix.
2371 a8083063 Iustin Pop

2372 a8083063 Iustin Pop
    """
2373 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2374 62779dd0 Iustin Pop
2375 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2376 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2377 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2378 62779dd0 Iustin Pop

2379 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2380 c41eea6e Iustin Pop
    @return: the cluster object
2381 62779dd0 Iustin Pop

2382 62779dd0 Iustin Pop
    """
2383 62779dd0 Iustin Pop
    return self._config_data.cluster
2384 e00fb268 Iustin Pop
2385 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2386 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2387 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2388 51cb1581 Luca Bigliardi

2389 51cb1581 Luca Bigliardi
    """
2390 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2391 51cb1581 Luca Bigliardi
2392 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2393 d1547283 Dimitris Aragiorgis
  def Update(self, target, feedback_fn, ec_id=None):
2394 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2395 e00fb268 Iustin Pop

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

2402 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2403 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2404 c41eea6e Iustin Pop
        the cluster
2405 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2406 c41eea6e Iustin Pop

2407 e00fb268 Iustin Pop
    """
2408 e00fb268 Iustin Pop
    if self._config_data is None:
2409 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2410 3ecf6786 Iustin Pop
                                   " cannot save.")
2411 f34901f8 Iustin Pop
    update_serial = False
2412 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2413 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2414 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2415 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2416 f34901f8 Iustin Pop
      update_serial = True
2417 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2418 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2419 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2420 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2421 1e0d3321 Dimitris Aragiorgis
    elif isinstance(target, objects.Network):
2422 1e0d3321 Dimitris Aragiorgis
      test = target in self._config_data.networks.values()
2423 e00fb268 Iustin Pop
    else:
2424 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2425 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2426 e00fb268 Iustin Pop
    if not test:
2427 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2428 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2429 f34901f8 Iustin Pop
    target.serial_no += 1
2430 d693c864 Iustin Pop
    target.mtime = now = time.time()
2431 f34901f8 Iustin Pop
2432 cff4c037 Iustin Pop
    if update_serial:
2433 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2434 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2435 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2436 b989e85d Iustin Pop
2437 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2438 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
2439 61cf6b5e Iustin Pop
2440 d1547283 Dimitris Aragiorgis
    if ec_id is not None:
2441 d1547283 Dimitris Aragiorgis
      # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams
2442 d1547283 Dimitris Aragiorgis
      self._UnlockedCommitTemporaryIps(ec_id)
2443 d1547283 Dimitris Aragiorgis
2444 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2445 73064714 Guido Trotter
2446 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2447 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2448 73064714 Guido Trotter
    """Drop per-execution-context reservations
2449 73064714 Guido Trotter

2450 73064714 Guido Trotter
    """
2451 d8aee57e Iustin Pop
    for rm in self._all_rms:
2452 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)
2453 6c0a75db Dimitris Aragiorgis
2454 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2455 6c0a75db Dimitris Aragiorgis
  def GetAllNetworksInfo(self):
2456 6a94d553 Dimitris Aragiorgis
    """Get configuration info of all the networks.
2457 6c0a75db Dimitris Aragiorgis

2458 6c0a75db Dimitris Aragiorgis
    """
2459 6c0a75db Dimitris Aragiorgis
    return dict(self._config_data.networks)
2460 6c0a75db Dimitris Aragiorgis
2461 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetworkList(self):
2462 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2463 6c0a75db Dimitris Aragiorgis

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

2466 6c0a75db Dimitris Aragiorgis
    """
2467 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks.keys()
2468 6c0a75db Dimitris Aragiorgis
2469 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2470 6c0a75db Dimitris Aragiorgis
  def GetNetworkList(self):
2471 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2472 6c0a75db Dimitris Aragiorgis

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

2475 6c0a75db Dimitris Aragiorgis
    """
2476 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetworkList()
2477 6c0a75db Dimitris Aragiorgis
2478 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2479 6c0a75db Dimitris Aragiorgis
  def GetNetworkNames(self):
2480 6c0a75db Dimitris Aragiorgis
    """Get a list of network names
2481 6c0a75db Dimitris Aragiorgis

2482 6c0a75db Dimitris Aragiorgis
    """
2483 beb81ea5 Dimitris Aragiorgis
    names = [net.name
2484 beb81ea5 Dimitris Aragiorgis
             for net in self._config_data.networks.values()]
2485 6c0a75db Dimitris Aragiorgis
    return names
2486 6c0a75db Dimitris Aragiorgis
2487 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetwork(self, uuid):
2488 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2489 6c0a75db Dimitris Aragiorgis

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

2492 6c0a75db Dimitris Aragiorgis
    """
2493 6c0a75db Dimitris Aragiorgis
    if uuid not in self._config_data.networks:
2494 6c0a75db Dimitris Aragiorgis
      return None
2495 6c0a75db Dimitris Aragiorgis
2496 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks[uuid]
2497 6c0a75db Dimitris Aragiorgis
2498 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2499 6c0a75db Dimitris Aragiorgis
  def GetNetwork(self, uuid):
2500 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2501 6c0a75db Dimitris Aragiorgis

2502 6c0a75db Dimitris Aragiorgis
    It takes the information from the configuration file.
2503 6c0a75db Dimitris Aragiorgis

2504 6c0a75db Dimitris Aragiorgis
    @param uuid: UUID of the network
2505 6c0a75db Dimitris Aragiorgis

2506 6c0a75db Dimitris Aragiorgis
    @rtype: L{objects.Network}
2507 6c0a75db Dimitris Aragiorgis
    @return: the network object
2508 6c0a75db Dimitris Aragiorgis

2509 6c0a75db Dimitris Aragiorgis
    """
2510 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetwork(uuid)
2511 6c0a75db Dimitris Aragiorgis
2512 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2513 6c0a75db Dimitris Aragiorgis
  def AddNetwork(self, net, ec_id, check_uuid=True):
2514 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2515 6c0a75db Dimitris Aragiorgis

2516 6c0a75db Dimitris Aragiorgis
    @type net: L{objects.Network}
2517 6c0a75db Dimitris Aragiorgis
    @param net: the Network object to add
2518 6c0a75db Dimitris Aragiorgis
    @type ec_id: string
2519 6c0a75db Dimitris Aragiorgis
    @param ec_id: unique id for the job to use when creating a missing UUID
2520 6c0a75db Dimitris Aragiorgis

2521 6c0a75db Dimitris Aragiorgis
    """
2522 6c0a75db Dimitris Aragiorgis
    self._UnlockedAddNetwork(net, ec_id, check_uuid)
2523 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2524 6c0a75db Dimitris Aragiorgis
2525 6c0a75db Dimitris Aragiorgis
  def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
2526 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2527 6c0a75db Dimitris Aragiorgis

2528 6c0a75db Dimitris Aragiorgis
    """
2529 6c0a75db Dimitris Aragiorgis
    logging.info("Adding network %s to configuration", net.name)
2530 6c0a75db Dimitris Aragiorgis
2531 6c0a75db Dimitris Aragiorgis
    if check_uuid:
2532 6c0a75db Dimitris Aragiorgis
      self._EnsureUUID(net, ec_id)
2533 6c0a75db Dimitris Aragiorgis
2534 6c0a75db Dimitris Aragiorgis
    net.serial_no = 1
2535 6c0a75db Dimitris Aragiorgis
    self._config_data.networks[net.uuid] = net
2536 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2537 6c0a75db Dimitris Aragiorgis
2538 6c0a75db Dimitris Aragiorgis
  def _UnlockedLookupNetwork(self, target):
2539 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2540 6c0a75db Dimitris Aragiorgis

2541 6c0a75db Dimitris Aragiorgis
    @type target: string
2542 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2543 6c0a75db Dimitris Aragiorgis
    @rtype: string
2544 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2545 6c0a75db Dimitris Aragiorgis
    @raises errors.OpPrereqError: when the target network cannot be found
2546 6c0a75db Dimitris Aragiorgis

2547 6c0a75db Dimitris Aragiorgis
    """
2548 9394f4d1 Dimitris Aragiorgis
    if target is None:
2549 9394f4d1 Dimitris Aragiorgis
      return None
2550 6c0a75db Dimitris Aragiorgis
    if target in self._config_data.networks:
2551 6c0a75db Dimitris Aragiorgis
      return target
2552 6c0a75db Dimitris Aragiorgis
    for net in self._config_data.networks.values():
2553 6c0a75db Dimitris Aragiorgis
      if net.name == target:
2554 6c0a75db Dimitris Aragiorgis
        return net.uuid
2555 1b68f268 Helga Velroyen
    raise errors.OpPrereqError("Network '%s' not found" % target,
2556 1b68f268 Helga Velroyen
                               errors.ECODE_NOENT)
2557 6c0a75db Dimitris Aragiorgis
2558 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2559 6c0a75db Dimitris Aragiorgis
  def LookupNetwork(self, target):
2560 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2561 6c0a75db Dimitris Aragiorgis

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

2564 6c0a75db Dimitris Aragiorgis
    @type target: string
2565 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2566 6c0a75db Dimitris Aragiorgis
    @rtype: string
2567 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2568 6c0a75db Dimitris Aragiorgis

2569 6c0a75db Dimitris Aragiorgis
    """
2570 6c0a75db Dimitris Aragiorgis
    return self._UnlockedLookupNetwork(target)
2571 6c0a75db Dimitris Aragiorgis
2572 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2573 6c0a75db Dimitris Aragiorgis
  def RemoveNetwork(self, network_uuid):
2574 6c0a75db Dimitris Aragiorgis
    """Remove a network from the configuration.
2575 6c0a75db Dimitris Aragiorgis

2576 6c0a75db Dimitris Aragiorgis
    @type network_uuid: string
2577 6c0a75db Dimitris Aragiorgis
    @param network_uuid: the UUID of the network to remove
2578 6c0a75db Dimitris Aragiorgis

2579 6c0a75db Dimitris Aragiorgis
    """
2580 6c0a75db Dimitris Aragiorgis
    logging.info("Removing network %s from configuration", network_uuid)
2581 6c0a75db Dimitris Aragiorgis
2582 6c0a75db Dimitris Aragiorgis
    if network_uuid not in self._config_data.networks:
2583 6c0a75db Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
2584 6c0a75db Dimitris Aragiorgis
2585 6c0a75db Dimitris Aragiorgis
    del self._config_data.networks[network_uuid]
2586 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2587 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2588 ad4a9ae7 Dimitris Aragiorgis
2589 9ccacbc8 Dimitris Aragiorgis
  def _UnlockedGetGroupNetParams(self, net_uuid, node):
2590 ad4a9ae7 Dimitris Aragiorgis
    """Get the netparams (mode, link) of a network.
2591 ad4a9ae7 Dimitris Aragiorgis

2592 ad4a9ae7 Dimitris Aragiorgis
    Get a network's netparams for a given node.
2593 ad4a9ae7 Dimitris Aragiorgis

2594 9ccacbc8 Dimitris Aragiorgis
    @type net_uuid: string
2595 9ccacbc8 Dimitris Aragiorgis
    @param net_uuid: network uuid
2596 ad4a9ae7 Dimitris Aragiorgis
    @type node: string
2597 ad4a9ae7 Dimitris Aragiorgis
    @param node: node name
2598 ad4a9ae7 Dimitris Aragiorgis
    @rtype: dict or None
2599 ad4a9ae7 Dimitris Aragiorgis
    @return: netparams
2600 ad4a9ae7 Dimitris Aragiorgis

2601 ad4a9ae7 Dimitris Aragiorgis
    """
2602 ad4a9ae7 Dimitris Aragiorgis
    node_info = self._UnlockedGetNodeInfo(node)
2603 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2604 ad4a9ae7 Dimitris Aragiorgis
    netparams = nodegroup_info.networks.get(net_uuid, None)
2605 ad4a9ae7 Dimitris Aragiorgis
2606 ad4a9ae7 Dimitris Aragiorgis
    return netparams
2607 ad4a9ae7 Dimitris Aragiorgis
2608 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2609 9ccacbc8 Dimitris Aragiorgis
  def GetGroupNetParams(self, net_uuid, node):
2610 ad4a9ae7 Dimitris Aragiorgis
    """Locking wrapper of _UnlockedGetGroupNetParams()
2611 ad4a9ae7 Dimitris Aragiorgis

2612 ad4a9ae7 Dimitris Aragiorgis
    """
2613 9ccacbc8 Dimitris Aragiorgis
    return self._UnlockedGetGroupNetParams(net_uuid, node)
2614 ad4a9ae7 Dimitris Aragiorgis
2615 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2616 ad4a9ae7 Dimitris Aragiorgis
  def CheckIPInNodeGroup(self, ip, node):
2617 6a94d553 Dimitris Aragiorgis
    """Check IP uniqueness in nodegroup.
2618 6a94d553 Dimitris Aragiorgis

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

2623 ad4a9ae7 Dimitris Aragiorgis
    @type ip: string
2624 ad4a9ae7 Dimitris Aragiorgis
    @param ip: ip address
2625 ad4a9ae7 Dimitris Aragiorgis
    @type node: string
2626 ad4a9ae7 Dimitris Aragiorgis
    @param node: node name
2627 ad4a9ae7 Dimitris Aragiorgis
    @rtype: (string, dict) or (None, None)
2628 ad4a9ae7 Dimitris Aragiorgis
    @return: (network name, netparams)
2629 ad4a9ae7 Dimitris Aragiorgis

2630 ad4a9ae7 Dimitris Aragiorgis
    """
2631 ad4a9ae7 Dimitris Aragiorgis
    if ip is None:
2632 ad4a9ae7 Dimitris Aragiorgis
      return (None, None)
2633 ad4a9ae7 Dimitris Aragiorgis
    node_info = self._UnlockedGetNodeInfo(node)
2634 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2635 ad4a9ae7 Dimitris Aragiorgis
    for net_uuid in nodegroup_info.networks.keys():
2636 ad4a9ae7 Dimitris Aragiorgis
      net_info = self._UnlockedGetNetwork(net_uuid)
2637 ad4a9ae7 Dimitris Aragiorgis
      pool = network.AddressPool(net_info)
2638 beb81ea5 Dimitris Aragiorgis
      if pool.Contains(ip):
2639 ad4a9ae7 Dimitris Aragiorgis
        return (net_info.name, nodegroup_info.networks[net_uuid])
2640 ad4a9ae7 Dimitris Aragiorgis
2641 ad4a9ae7 Dimitris Aragiorgis
    return (None, None)