Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 11e90588

History | View | Annotate | Download (88.5 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

373 ad4a9ae7 Dimitris Aragiorgis
    """
374 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
375 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
376 ad4a9ae7 Dimitris Aragiorgis
377 ad4a9ae7 Dimitris Aragiorgis
    def gen_one():
378 ad4a9ae7 Dimitris Aragiorgis
      try:
379 1f1d3bf2 Dimitris Aragiorgis
        ip = pool.GenerateFree()
380 1f1d3bf2 Dimitris Aragiorgis
      except errors.AddressPoolError:
381 ad4a9ae7 Dimitris Aragiorgis
        raise errors.ReservationError("Cannot generate IP. Network is full")
382 e81eef56 Dimitris Aragiorgis
      return (constants.RESERVE_ACTION, ip, net_uuid)
383 ad4a9ae7 Dimitris Aragiorgis
384 beb81ea5 Dimitris Aragiorgis
    _, address, _ = self._temporary_ips.Generate([], gen_one, ec_id)
385 ad4a9ae7 Dimitris Aragiorgis
    return address
386 ad4a9ae7 Dimitris Aragiorgis
387 e5370111 Dimitris Aragiorgis
  def _UnlockedReserveIp(self, net_uuid, address, ec_id, check=True):
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 e5370111 Dimitris Aragiorgis
      isextreserved = pool.IsReserved(address, external=True)
396 ad4a9ae7 Dimitris Aragiorgis
    except errors.AddressPoolError:
397 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address not in network")
398 ad4a9ae7 Dimitris Aragiorgis
    if isreserved:
399 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address already in use")
400 e5370111 Dimitris Aragiorgis
    if check and isextreserved:
401 e5370111 Dimitris Aragiorgis
      raise errors.ReservationError("IP is externally reserved")
402 ad4a9ae7 Dimitris Aragiorgis
403 e81eef56 Dimitris Aragiorgis
    return self._temporary_ips.Reserve(ec_id,
404 e81eef56 Dimitris Aragiorgis
                                       (constants.RESERVE_ACTION,
405 e81eef56 Dimitris Aragiorgis
                                        address, net_uuid))
406 ad4a9ae7 Dimitris Aragiorgis
407 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
408 e5370111 Dimitris Aragiorgis
  def ReserveIp(self, net_uuid, address, ec_id, check=True):
409 ad4a9ae7 Dimitris Aragiorgis
    """Reserve a given IPv4 address for use by an instance.
410 ad4a9ae7 Dimitris Aragiorgis

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

419 d8aee57e Iustin Pop
    @type lv_name: string
420 d8aee57e Iustin Pop
    @param lv_name: the logical volume name to reserve
421 d8aee57e Iustin Pop

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

433 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
434 f9518d38 Iustin Pop

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

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

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

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

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

483 34e54ebc Iustin Pop
    @type include_temporary: boolean
484 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
485 34e54ebc Iustin Pop
    @rtype: set
486 34e54ebc Iustin Pop
    @return: a set of IDs
487 34e54ebc Iustin Pop

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

501 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
502 923b1523 Iustin Pop
    duplicates.
503 923b1523 Iustin Pop

504 c41eea6e Iustin Pop
    @rtype: string
505 c41eea6e Iustin Pop
    @return: the unique id
506 923b1523 Iustin Pop

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

515 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
516 430b923c Iustin Pop

517 4fae38c5 Guido Trotter
    @type ec_id: string
518 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
519 34d657ba Iustin Pop

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

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

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

540 c41eea6e Iustin Pop
    @rtype: list
541 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
542 c41eea6e Iustin Pop

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

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

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

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

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

870 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
871 4a89c54a Iustin Pop

872 4a89c54a Iustin Pop
    @rtype: list
873 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
874 4a89c54a Iustin Pop
        configuration errors
875 4a89c54a Iustin Pop

876 4a89c54a Iustin Pop
    """
877 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
878 4a89c54a Iustin Pop
879 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
880 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
881 a8083063 Iustin Pop

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

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

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

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

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

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

927 f78ede4e Guido Trotter
    """
928 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
929 f78ede4e Guido Trotter
930 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
931 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
932 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
933 b2fddf63 Iustin Pop

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

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

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

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

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

977 4a89c54a Iustin Pop
    @rtype: (dict, list)
978 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
979 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
980 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
981 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
982 4a89c54a Iustin Pop
        should raise an exception
983 a81c53c9 Iustin Pop

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

1017 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
1018 6d2e83d5 Iustin Pop

1019 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
1020 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
1021 6d2e83d5 Iustin Pop
        an empty list).
1022 6d2e83d5 Iustin Pop

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

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

1039 32388e6d Iustin Pop
    @type instance: string
1040 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
1041 32388e6d Iustin Pop

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

1089 a81c53c9 Iustin Pop
    @type instance: string
1090 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
1091 a81c53c9 Iustin Pop
                     released
1092 a81c53c9 Iustin Pop

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

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

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

1110 61cf6b5e Iustin Pop
    @type instance: string
1111 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
1112 61cf6b5e Iustin Pop
                     released
1113 61cf6b5e Iustin Pop

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

1121 4a8b186a Michael Hanselmann
    @return: Config version
1122 4a8b186a Michael Hanselmann

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

1130 4a8b186a Michael Hanselmann
    @return: Cluster name
1131 4a8b186a Michael Hanselmann

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

1139 4a8b186a Michael Hanselmann
    @return: Master hostname
1140 4a8b186a Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

1202 a8083063 Iustin Pop
    """
1203 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1204 a8083063 Iustin Pop
1205 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1206 a9542a4f Thomas Thrainer
  def GetDsaHostKey(self):
1207 a9542a4f Thomas Thrainer
    """Return the dsa hostkey from the config.
1208 a9542a4f Thomas Thrainer

1209 a9542a4f Thomas Thrainer
    @rtype: string
1210 a9542a4f Thomas Thrainer
    @return: the dsa hostkey
1211 a9542a4f Thomas Thrainer

1212 a9542a4f Thomas Thrainer
    """
1213 a9542a4f Thomas Thrainer
    return self._config_data.cluster.dsahostkeypub
1214 a9542a4f Thomas Thrainer
1215 a9542a4f Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1216 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1217 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1218 bf4af505 Apollon Oikonomopoulos

1219 bf4af505 Apollon Oikonomopoulos
    """
1220 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1221 bf4af505 Apollon Oikonomopoulos
1222 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1223 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1224 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1225 868a98ca Manuel Franceschini

1226 868a98ca Manuel Franceschini
    @return: primary ip family
1227 868a98ca Manuel Franceschini

1228 868a98ca Manuel Franceschini
    """
1229 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1230 868a98ca Manuel Franceschini
1231 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1232 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1233 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1234 c9f4b8e6 Andrea Spadaccini

1235 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1236 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1237 c9f4b8e6 Andrea Spadaccini

1238 c9f4b8e6 Andrea Spadaccini
    """
1239 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1240 5ae4945a Iustin Pop
    result = objects.MasterNetworkParameters(
1241 5ae4945a Iustin Pop
      name=cluster.master_node, ip=cluster.master_ip,
1242 5ae4945a Iustin Pop
      netmask=cluster.master_netmask, netdev=cluster.master_netdev,
1243 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1244 c9f4b8e6 Andrea Spadaccini
1245 f9d20654 Andrea Spadaccini
    return result
1246 f9d20654 Andrea Spadaccini
1247 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1248 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1249 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1250 e11a1b77 Adeodato Simo

1251 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1252 90e99856 Adeodato Simo
    according to their default values.
1253 90e99856 Adeodato Simo

1254 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1255 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1256 e11a1b77 Adeodato Simo
    @type ec_id: string
1257 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1258 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1259 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1260 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1261 e11a1b77 Adeodato Simo
                       configuration already
1262 e11a1b77 Adeodato Simo

1263 e11a1b77 Adeodato Simo
    """
1264 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1265 e11a1b77 Adeodato Simo
    self._WriteConfig()
1266 e11a1b77 Adeodato Simo
1267 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1268 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1269 e11a1b77 Adeodato Simo

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

1300 e11a1b77 Adeodato Simo
    @type group_uuid: string
1301 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1302 e11a1b77 Adeodato Simo

1303 e11a1b77 Adeodato Simo
    """
1304 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1305 e11a1b77 Adeodato Simo
1306 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1307 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1308 e11a1b77 Adeodato Simo
1309 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1310 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1311 0389c42a Stephen Shirley
1312 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1313 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1314 e11a1b77 Adeodato Simo
    self._WriteConfig()
1315 e11a1b77 Adeodato Simo
1316 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1317 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1318 eaa98a04 Guido Trotter

1319 eaa98a04 Guido Trotter
    @type target: string or None
1320 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1321 eaa98a04 Guido Trotter
    @rtype: string
1322 412b3531 Guido Trotter
    @return: nodegroup UUID
1323 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1324 eaa98a04 Guido Trotter

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

1344 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1345 e85d8982 Stephen Shirley

1346 e85d8982 Stephen Shirley
    @type target: string or None
1347 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1348 e85d8982 Stephen Shirley
    @rtype: string
1349 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1350 e85d8982 Stephen Shirley

1351 e85d8982 Stephen Shirley
    """
1352 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1353 e85d8982 Stephen Shirley
1354 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1355 648e4196 Guido Trotter
    """Lookup a node group.
1356 648e4196 Guido Trotter

1357 648e4196 Guido Trotter
    @type uuid: string
1358 648e4196 Guido Trotter
    @param uuid: group UUID
1359 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1360 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1361 648e4196 Guido Trotter

1362 648e4196 Guido Trotter
    """
1363 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1364 648e4196 Guido Trotter
      return None
1365 648e4196 Guido Trotter
1366 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1367 648e4196 Guido Trotter
1368 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1369 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1370 5768e6a6 René Nussbaumer
    """Lookup a node group.
1371 5768e6a6 René Nussbaumer

1372 5768e6a6 René Nussbaumer
    @type uuid: string
1373 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1374 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1375 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1376 5768e6a6 René Nussbaumer

1377 5768e6a6 René Nussbaumer
    """
1378 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1379 5768e6a6 René Nussbaumer
1380 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1381 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1382 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1383 622444e5 Iustin Pop

1384 622444e5 Iustin Pop
    """
1385 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1386 622444e5 Iustin Pop
1387 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1388 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1389 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1390 1ac6f2ad Guido Trotter

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

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

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

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

1420 a8083063 Iustin Pop
    This should be used after creating a new instance.
1421 a8083063 Iustin Pop

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

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

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

1456 430b923c Iustin Pop
    """
1457 430b923c Iustin Pop
    if not item.uuid:
1458 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1459 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1460 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1461 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1462 430b923c Iustin Pop
1463 1d4a4b26 Thomas Thrainer
  def _SetInstanceStatus(self, instance_name, status, disks_active):
1464 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1465 a8083063 Iustin Pop

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

1492 1d4a4b26 Thomas Thrainer
    This also sets the instance disks active flag.
1493 1d4a4b26 Thomas Thrainer

1494 6a408fb2 Iustin Pop
    """
1495 1d4a4b26 Thomas Thrainer
    self._SetInstanceStatus(instance_name, constants.ADMINST_UP, True)
1496 57de31c0 Agata Murawska
1497 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1498 57de31c0 Agata Murawska
  def MarkInstanceOffline(self, instance_name):
1499 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1500 57de31c0 Agata Murawska

1501 1d4a4b26 Thomas Thrainer
    This also clears the instance disks active flag.
1502 1d4a4b26 Thomas Thrainer

1503 57de31c0 Agata Murawska
    """
1504 1d4a4b26 Thomas Thrainer
    self._SetInstanceStatus(instance_name, constants.ADMINST_OFFLINE, False)
1505 6a408fb2 Iustin Pop
1506 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1507 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1508 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1509 a8083063 Iustin Pop

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

1536 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1537 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1538 fc95f88f Iustin Pop
    rename.
1539 fc95f88f Iustin Pop

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

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

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

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

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

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

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

1602 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1603 c41eea6e Iustin Pop
        'instance1.example.com']
1604 a8083063 Iustin Pop

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

1611 a8083063 Iustin Pop
    """
1612 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1613 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1614 a8083063 Iustin Pop
1615 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1616 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1617 94bbfece Iustin Pop

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

1620 94bbfece Iustin Pop
    """
1621 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1622 94bbfece Iustin Pop
      return None
1623 94bbfece Iustin Pop
1624 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1625 94bbfece Iustin Pop
1626 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1627 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1628 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1629 a8083063 Iustin Pop

1630 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1631 a8083063 Iustin Pop
    an instance are taken from the live systems.
1632 a8083063 Iustin Pop

1633 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1634 c41eea6e Iustin Pop
        I{instance1.example.com}
1635 a8083063 Iustin Pop

1636 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1637 c41eea6e Iustin Pop
    @return: the instance object
1638 a8083063 Iustin Pop

1639 a8083063 Iustin Pop
    """
1640 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1641 a8083063 Iustin Pop
1642 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1643 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1644 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1645 2674690b Michael Hanselmann

1646 2674690b Michael Hanselmann
    @rtype: frozenset
1647 2674690b Michael Hanselmann

1648 2674690b Michael Hanselmann
    """
1649 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1650 2674690b Michael Hanselmann
    if not instance:
1651 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1652 2674690b Michael Hanselmann
1653 2674690b Michael Hanselmann
    if primary_only:
1654 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1655 2674690b Michael Hanselmann
    else:
1656 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1657 2674690b Michael Hanselmann
1658 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1659 2674690b Michael Hanselmann
                     for node_name in nodes)
1660 2674690b Michael Hanselmann
1661 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1662 922610c9 Dimitris Aragiorgis
  def GetInstanceNetworks(self, instance_name):
1663 922610c9 Dimitris Aragiorgis
    """Returns set of network UUIDs for instance's nics.
1664 922610c9 Dimitris Aragiorgis

1665 922610c9 Dimitris Aragiorgis
    @rtype: frozenset
1666 922610c9 Dimitris Aragiorgis

1667 922610c9 Dimitris Aragiorgis
    """
1668 922610c9 Dimitris Aragiorgis
    instance = self._UnlockedGetInstanceInfo(instance_name)
1669 922610c9 Dimitris Aragiorgis
    if not instance:
1670 922610c9 Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1671 922610c9 Dimitris Aragiorgis
1672 922610c9 Dimitris Aragiorgis
    networks = set()
1673 922610c9 Dimitris Aragiorgis
    for nic in instance.nics:
1674 922610c9 Dimitris Aragiorgis
      if nic.network:
1675 922610c9 Dimitris Aragiorgis
        networks.add(nic.network)
1676 922610c9 Dimitris Aragiorgis
1677 922610c9 Dimitris Aragiorgis
    return frozenset(networks)
1678 922610c9 Dimitris Aragiorgis
1679 922610c9 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
1680 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1681 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1682 71333cb9 Iustin Pop

1683 71333cb9 Iustin Pop
    @param instances: list of instance names
1684 71333cb9 Iustin Pop
    @rtype: list
1685 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1686 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1687 71333cb9 Iustin Pop
        node, while keeping the original order
1688 71333cb9 Iustin Pop

1689 71333cb9 Iustin Pop
    """
1690 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1691 71333cb9 Iustin Pop
1692 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1693 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1694 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1695 0b2de758 Iustin Pop

1696 0b2de758 Iustin Pop
    @rtype: dict
1697 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1698 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1699 0b2de758 Iustin Pop

1700 0b2de758 Iustin Pop
    """
1701 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1702 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1703 0b2de758 Iustin Pop
    return my_dict
1704 0b2de758 Iustin Pop
1705 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1706 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1707 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1708 cc19798f Michael Hanselmann

1709 cc19798f Michael Hanselmann
    @type filter_fn: callable
1710 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1711 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1712 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1713 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1714 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1715 cc19798f Michael Hanselmann

1716 cc19798f Michael Hanselmann
    """
1717 cc19798f Michael Hanselmann
    return dict((name, inst)
1718 cc19798f Michael Hanselmann
                for (name, inst) in self._config_data.instances.items()
1719 cc19798f Michael Hanselmann
                if filter_fn(inst))
1720 cc19798f Michael Hanselmann
1721 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1722 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1723 a8083063 Iustin Pop
    """Add a node to the configuration.
1724 a8083063 Iustin Pop

1725 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1726 c41eea6e Iustin Pop
    @param node: a Node instance
1727 a8083063 Iustin Pop

1728 a8083063 Iustin Pop
    """
1729 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1730 d8470559 Michael Hanselmann
1731 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1732 430b923c Iustin Pop
1733 b989e85d Iustin Pop
    node.serial_no = 1
1734 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1735 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1736 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1737 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1738 a8083063 Iustin Pop
    self._WriteConfig()
1739 a8083063 Iustin Pop
1740 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1741 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1742 a8083063 Iustin Pop
    """Remove a node from the configuration.
1743 a8083063 Iustin Pop

1744 a8083063 Iustin Pop
    """
1745 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1746 d8470559 Michael Hanselmann
1747 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1748 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1749 a8083063 Iustin Pop
1750 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1751 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1752 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1753 a8083063 Iustin Pop
    self._WriteConfig()
1754 a8083063 Iustin Pop
1755 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1756 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1757 a8083063 Iustin Pop

1758 a8083063 Iustin Pop
    """
1759 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1760 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1761 a8083063 Iustin Pop
1762 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1763 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1764 a8083063 Iustin Pop

1765 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1766 c41eea6e Iustin Pop
    held.
1767 f78ede4e Guido Trotter

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

1770 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1771 c41eea6e Iustin Pop
    @return: the node object
1772 a8083063 Iustin Pop

1773 a8083063 Iustin Pop
    """
1774 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1775 a8083063 Iustin Pop
      return None
1776 a8083063 Iustin Pop
1777 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1778 a8083063 Iustin Pop
1779 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1780 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1781 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1782 f78ede4e Guido Trotter

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

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

1787 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1788 c41eea6e Iustin Pop
    @return: the node object
1789 f78ede4e Guido Trotter

1790 f78ede4e Guido Trotter
    """
1791 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1792 f78ede4e Guido Trotter
1793 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1794 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1795 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1796 8bf9e9a5 Iustin Pop

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

1799 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1800 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1801 8bf9e9a5 Iustin Pop

1802 8bf9e9a5 Iustin Pop
    """
1803 8bf9e9a5 Iustin Pop
    pri = []
1804 8bf9e9a5 Iustin Pop
    sec = []
1805 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1806 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1807 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1808 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1809 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1810 8bf9e9a5 Iustin Pop
    return (pri, sec)
1811 8bf9e9a5 Iustin Pop
1812 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1813 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1814 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1815 c71b049c Michael Hanselmann

1816 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1817 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1818 c71b049c Michael Hanselmann
    @rtype: frozenset
1819 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1820 c71b049c Michael Hanselmann

1821 c71b049c Michael Hanselmann
    """
1822 c71b049c Michael Hanselmann
    if primary_only:
1823 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1824 c71b049c Michael Hanselmann
    else:
1825 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1826 c71b049c Michael Hanselmann
1827 c71b049c Michael Hanselmann
    return frozenset(inst.name
1828 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1829 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1830 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1831 c71b049c Michael Hanselmann
1832 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1833 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1834 a8083063 Iustin Pop

1835 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1836 c41eea6e Iustin Pop
    held.
1837 c41eea6e Iustin Pop

1838 c41eea6e Iustin Pop
    @rtype: list
1839 f78ede4e Guido Trotter

1840 a8083063 Iustin Pop
    """
1841 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1842 a8083063 Iustin Pop
1843 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1844 f78ede4e Guido Trotter
  def GetNodeList(self):
1845 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1846 f78ede4e Guido Trotter

1847 f78ede4e Guido Trotter
    """
1848 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1849 f78ede4e Guido Trotter
1850 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1851 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1852 94a02bb5 Iustin Pop

1853 94a02bb5 Iustin Pop
    """
1854 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1855 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1856 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1857 94a02bb5 Iustin Pop
1858 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1859 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1860 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1861 6819dc49 Iustin Pop

1862 6819dc49 Iustin Pop
    """
1863 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1864 6819dc49 Iustin Pop
1865 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1866 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1867 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1868 075b62ca Iustin Pop

1869 075b62ca Iustin Pop
    """
1870 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1871 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1872 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1873 075b62ca Iustin Pop
1874 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1875 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1876 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1877 8bf9e9a5 Iustin Pop

1878 8bf9e9a5 Iustin Pop
    """
1879 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1880 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1881 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1882 8bf9e9a5 Iustin Pop
1883 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1884 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1885 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1886 f5eaa3c1 Iustin Pop

1887 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1888 f5eaa3c1 Iustin Pop
    @rtype: list
1889 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1890 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1891 f5eaa3c1 Iustin Pop
        order
1892 f5eaa3c1 Iustin Pop

1893 f5eaa3c1 Iustin Pop
    """
1894 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1895 f5eaa3c1 Iustin Pop
1896 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1897 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1898 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1899 d65e5776 Iustin Pop

1900 d65e5776 Iustin Pop
    @rtype: dict
1901 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1902 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1903 d65e5776 Iustin Pop

1904 d65e5776 Iustin Pop
    """
1905 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1906 ee14d800 Michael Hanselmann
1907 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1908 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1909 ee14d800 Michael Hanselmann

1910 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1911 ee14d800 Michael Hanselmann

1912 ee14d800 Michael Hanselmann
    """
1913 ee14d800 Michael Hanselmann
    return dict([(node, self._UnlockedGetNodeInfo(node))
1914 ee14d800 Michael Hanselmann
                 for node in self._UnlockedGetNodeList()])
1915 d65e5776 Iustin Pop
1916 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1917 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1918 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1919 9d5b1371 Michael Hanselmann

1920 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1921 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1922 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1923 9d5b1371 Michael Hanselmann

1924 9d5b1371 Michael Hanselmann
    """
1925 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1926 9d5b1371 Michael Hanselmann
1927 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1928 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1929 ec0292f1 Iustin Pop

1930 23f06b2b Iustin Pop
    @type exceptions: list
1931 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1932 ec0292f1 Iustin Pop
    @rtype: tuple
1933 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1934 ec0292f1 Iustin Pop

1935 ec0292f1 Iustin Pop
    """
1936 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1937 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1938 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1939 23f06b2b Iustin Pop
        continue
1940 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1941 ec0292f1 Iustin Pop
        mc_max += 1
1942 ec0292f1 Iustin Pop
      if node.master_candidate:
1943 ec0292f1 Iustin Pop
        mc_now += 1
1944 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1945 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1946 ec0292f1 Iustin Pop
1947 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1948 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1949 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1950 ec0292f1 Iustin Pop

1951 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1952 ec0292f1 Iustin Pop

1953 23f06b2b Iustin Pop
    @type exceptions: list
1954 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1955 ec0292f1 Iustin Pop
    @rtype: tuple
1956 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1957 ec0292f1 Iustin Pop

1958 ec0292f1 Iustin Pop
    """
1959 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1960 ec0292f1 Iustin Pop
1961 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1962 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1963 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1964 ec0292f1 Iustin Pop

1965 44485f49 Guido Trotter
    @type exceptions: list
1966 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1967 ec0292f1 Iustin Pop
    @rtype: list
1968 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1969 ec0292f1 Iustin Pop

1970 ec0292f1 Iustin Pop
    """
1971 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1972 ec0292f1 Iustin Pop
    mod_list = []
1973 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1974 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1975 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1976 ec0292f1 Iustin Pop
      for name in node_list:
1977 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1978 ec0292f1 Iustin Pop
          break
1979 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1980 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1981 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1982 ec0292f1 Iustin Pop
          continue
1983 ee513a66 Iustin Pop
        mod_list.append(node)
1984 ec0292f1 Iustin Pop
        node.master_candidate = True
1985 ec0292f1 Iustin Pop
        node.serial_no += 1
1986 ec0292f1 Iustin Pop
        mc_now += 1
1987 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1988 ec0292f1 Iustin Pop
        # this should not happen
1989 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1990 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1991 ec0292f1 Iustin Pop
      if mod_list:
1992 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1993 ec0292f1 Iustin Pop
        self._WriteConfig()
1994 ec0292f1 Iustin Pop
1995 ec0292f1 Iustin Pop
    return mod_list
1996 ec0292f1 Iustin Pop
1997 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1998 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1999 190e3cb6 Guido Trotter

2000 190e3cb6 Guido Trotter
    """
2001 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
2002 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
2003 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
2004 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
2005 190e3cb6 Guido Trotter
      # is not found anymore.
2006 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
2007 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
2008 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
2009 190e3cb6 Guido Trotter
2010 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
2011 190e3cb6 Guido Trotter
    """Remove a given node from its group.
2012 190e3cb6 Guido Trotter

2013 190e3cb6 Guido Trotter
    """
2014 f936c153 Iustin Pop
    nodegroup = node.group
2015 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
2016 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
2017 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
2018 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
2019 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
2020 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
2021 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
2022 190e3cb6 Guido Trotter
    else:
2023 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
2024 190e3cb6 Guido Trotter
2025 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
2026 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
2027 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
2028 54c31fd3 Michael Hanselmann

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

2032 54c31fd3 Michael Hanselmann
    """
2033 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
2034 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
2035 54c31fd3 Michael Hanselmann
2036 54c31fd3 Michael Hanselmann
    resmod = []
2037 54c31fd3 Michael Hanselmann
2038 54c31fd3 Michael Hanselmann
    # Try to resolve names/UUIDs first
2039 54c31fd3 Michael Hanselmann
    for (node_name, new_group_uuid) in mods:
2040 54c31fd3 Michael Hanselmann
      try:
2041 54c31fd3 Michael Hanselmann
        node = nodes[node_name]
2042 54c31fd3 Michael Hanselmann
      except KeyError:
2043 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find node '%s'" % node_name)
2044 54c31fd3 Michael Hanselmann
2045 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
2046 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
2047 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
2048 54c31fd3 Michael Hanselmann
                      node_name, node.group)
2049 54c31fd3 Michael Hanselmann
        continue
2050 54c31fd3 Michael Hanselmann
2051 54c31fd3 Michael Hanselmann
      # Try to find current group of node
2052 54c31fd3 Michael Hanselmann
      try:
2053 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
2054 54c31fd3 Michael Hanselmann
      except KeyError:
2055 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
2056 54c31fd3 Michael Hanselmann
                                        node.group)
2057 54c31fd3 Michael Hanselmann
2058 54c31fd3 Michael Hanselmann
      # Try to find new group for node
2059 54c31fd3 Michael Hanselmann
      try:
2060 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
2061 54c31fd3 Michael Hanselmann
      except KeyError:
2062 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
2063 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
2064 54c31fd3 Michael Hanselmann
2065 54c31fd3 Michael Hanselmann
      assert node.name in old_group.members, \
2066 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
2067 54c31fd3 Michael Hanselmann
         " old group '%s'" % (node.name, old_group.uuid))
2068 54c31fd3 Michael Hanselmann
      assert node.name not in new_group.members, \
2069 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
2070 54c31fd3 Michael Hanselmann
         " its new group '%s'" % (node.name, new_group.uuid))
2071 54c31fd3 Michael Hanselmann
2072 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
2073 54c31fd3 Michael Hanselmann
2074 54c31fd3 Michael Hanselmann
    # Apply changes
2075 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
2076 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
2077 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
2078 54c31fd3 Michael Hanselmann
2079 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
2080 54c31fd3 Michael Hanselmann
2081 54c31fd3 Michael Hanselmann
      # Update members of involved groups
2082 54c31fd3 Michael Hanselmann
      if node.name in old_group.members:
2083 54c31fd3 Michael Hanselmann
        old_group.members.remove(node.name)
2084 54c31fd3 Michael Hanselmann
      if node.name not in new_group.members:
2085 54c31fd3 Michael Hanselmann
        new_group.members.append(node.name)
2086 54c31fd3 Michael Hanselmann
2087 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
2088 54c31fd3 Michael Hanselmann
    now = time.time()
2089 75191077 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142
2090 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
2091 54c31fd3 Michael Hanselmann
      obj.mtime = now
2092 54c31fd3 Michael Hanselmann
2093 54c31fd3 Michael Hanselmann
    # Force ssconf update
2094 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
2095 54c31fd3 Michael Hanselmann
2096 54c31fd3 Michael Hanselmann
    self._WriteConfig()
2097 54c31fd3 Michael Hanselmann
2098 a8083063 Iustin Pop
  def _BumpSerialNo(self):
2099 a8083063 Iustin Pop
    """Bump up the serial number of the config.
2100 a8083063 Iustin Pop

2101 a8083063 Iustin Pop
    """
2102 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
2103 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
2104 a8083063 Iustin Pop
2105 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
2106 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
2107 76d5d3a3 Iustin Pop

2108 76d5d3a3 Iustin Pop
    """
2109 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
2110 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
2111 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
2112 e0519c34 Dimitris Aragiorgis
            self._config_data.networks.values() +
2113 b87a9c5f Christos Stavrakakis
            self._AllDisks() +
2114 b87a9c5f Christos Stavrakakis
            self._AllNICs() +
2115 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
2116 76d5d3a3 Iustin Pop
2117 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
2118 a8083063 Iustin Pop
    """Read the config data from disk.
2119 a8083063 Iustin Pop

2120 a8083063 Iustin Pop
    """
2121 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
2122 13998ef2 Michael Hanselmann
2123 a8083063 Iustin Pop
    try:
2124 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
2125 13998ef2 Michael Hanselmann
    except Exception, err:
2126 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
2127 5b263ed7 Michael Hanselmann
2128 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
2129 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
2130 5b263ed7 Michael Hanselmann
2131 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
2132 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
2133 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
2134 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
2135 90d726a8 Iustin Pop
2136 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
2137 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
2138 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
2139 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
2140 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
2141 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
2142 eb180fe2 Iustin Pop
2143 a8083063 Iustin Pop
    self._config_data = data
2144 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
2145 0779e3aa Iustin Pop
    # ssconf update
2146 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
2147 a8083063 Iustin Pop
2148 45f62156 Bernardo Dal Seno
    # Upgrade configuration if needed
2149 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
2150 76d5d3a3 Iustin Pop
2151 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
2152 bd407597 Iustin Pop
2153 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
2154 45f62156 Bernardo Dal Seno
    """Run any upgrade steps.
2155 76d5d3a3 Iustin Pop

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

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

2166 76d5d3a3 Iustin Pop
    """
2167 45f62156 Bernardo Dal Seno
    # Keep a copy of the persistent part of _config_data to check for changes
2168 45f62156 Bernardo Dal Seno
    # Serialization doesn't guarantee order in dictionaries
2169 45f62156 Bernardo Dal Seno
    oldconf = copy.deepcopy(self._config_data.ToDict())
2170 45f62156 Bernardo Dal Seno
2171 45f62156 Bernardo Dal Seno
    # In-object upgrades
2172 45f62156 Bernardo Dal Seno
    self._config_data.UpgradeConfig()
2173 45f62156 Bernardo Dal Seno
2174 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
2175 76d5d3a3 Iustin Pop
      if item.uuid is None:
2176 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
2177 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
2178 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
2179 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
2180 75cf411a Adeodato Simo
                                            members=[])
2181 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
2182 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
2183 f936c153 Iustin Pop
      if not node.group:
2184 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
2185 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
2186 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
2187 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
2188 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
2189 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
2190 45f62156 Bernardo Dal Seno
2191 45f62156 Bernardo Dal Seno
    modified = (oldconf != self._config_data.ToDict())
2192 76d5d3a3 Iustin Pop
    if modified:
2193 76d5d3a3 Iustin Pop
      self._WriteConfig()
2194 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
2195 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
2196 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
2197 04db1880 Bernardo Dal Seno
    else:
2198 04db1880 Bernardo Dal Seno
      config_errors = self._UnlockedVerifyConfig()
2199 04db1880 Bernardo Dal Seno
      if config_errors:
2200 04db1880 Bernardo Dal Seno
        errmsg = ("Loaded configuration data is not consistent: %s" %
2201 04db1880 Bernardo Dal Seno
                  (utils.CommaJoin(config_errors)))
2202 04db1880 Bernardo Dal Seno
        logging.critical(errmsg)
2203 4fae38c5 Guido Trotter
2204 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
2205 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
2206 a8083063 Iustin Pop

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

2210 a8083063 Iustin Pop
    """
2211 a8083063 Iustin Pop
    if self._offline:
2212 a8083063 Iustin Pop
      return True
2213 a4eae71f Michael Hanselmann
2214 a8083063 Iustin Pop
    bad = False
2215 a8083063 Iustin Pop
2216 6a5b8b4b Iustin Pop
    node_list = []
2217 6a5b8b4b Iustin Pop
    addr_list = []
2218 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
2219 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
2220 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
2221 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
2222 6b294c53 Iustin Pop
    # in between
2223 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
2224 6a5b8b4b Iustin Pop
      if node_name == myhostname:
2225 6a5b8b4b Iustin Pop
        continue
2226 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
2227 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
2228 6a5b8b4b Iustin Pop
        continue
2229 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
2230 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
2231 6b294c53 Iustin Pop
2232 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
2233 415a7304 Michael Hanselmann
    result = \
2234 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
2235 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
2236 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
2237 1b54fc6c Guido Trotter
      if msg:
2238 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
2239 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
2240 1b54fc6c Guido Trotter
        logging.error(msg)
2241 a4eae71f Michael Hanselmann
2242 a4eae71f Michael Hanselmann
        if feedback_fn:
2243 a4eae71f Michael Hanselmann
          feedback_fn(msg)
2244 a4eae71f Michael Hanselmann
2245 a8083063 Iustin Pop
        bad = True
2246 a4eae71f Michael Hanselmann
2247 a8083063 Iustin Pop
    return not bad
2248 a8083063 Iustin Pop
2249 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
2250 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
2251 a8083063 Iustin Pop

2252 a8083063 Iustin Pop
    """
2253 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
2254 a4eae71f Michael Hanselmann
2255 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
2256 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
2257 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
2258 d2231b8c Iustin Pop
    # recovery to the user
2259 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
2260 4a89c54a Iustin Pop
    if config_errors:
2261 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
2262 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
2263 d2231b8c Iustin Pop
      logging.critical(errmsg)
2264 d2231b8c Iustin Pop
      if feedback_fn:
2265 d2231b8c Iustin Pop
        feedback_fn(errmsg)
2266 d2231b8c Iustin Pop
2267 a8083063 Iustin Pop
    if destination is None:
2268 a8083063 Iustin Pop
      destination = self._cfg_file
2269 a8083063 Iustin Pop
    self._BumpSerialNo()
2270 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
2271 13998ef2 Michael Hanselmann
2272 e60c73a1 René Nussbaumer
    getents = self._getents()
2273 bd407597 Iustin Pop
    try:
2274 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
2275 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
2276 bd407597 Iustin Pop
    except errors.LockError:
2277 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
2278 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
2279 bd407597 Iustin Pop
                                      " update")
2280 bd407597 Iustin Pop
    try:
2281 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
2282 bd407597 Iustin Pop
    finally:
2283 bd407597 Iustin Pop
      os.close(fd)
2284 13998ef2 Michael Hanselmann
2285 14e15659 Iustin Pop
    self.write_count += 1
2286 3d3a04bc Iustin Pop
2287 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
2288 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
2289 a8083063 Iustin Pop
2290 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
2291 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
2292 d9a855f1 Michael Hanselmann
      if not self._offline:
2293 b2acdbdc Michael Hanselmann
        result = self._GetRpc(None).call_write_ssconf_files(
2294 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
2295 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
2296 a4eae71f Michael Hanselmann
2297 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
2298 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
2299 e1e75d00 Iustin Pop
          if msg:
2300 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
2301 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
2302 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
2303 a4eae71f Michael Hanselmann
2304 a4eae71f Michael Hanselmann
            if feedback_fn:
2305 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
2306 a4eae71f Michael Hanselmann
2307 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
2308 54d1a06e Michael Hanselmann
2309 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
2310 054596f0 Iustin Pop
    """Return the values needed by ssconf.
2311 054596f0 Iustin Pop

2312 054596f0 Iustin Pop
    @rtype: dict
2313 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2314 054596f0 Iustin Pop
        associated value
2315 054596f0 Iustin Pop

2316 054596f0 Iustin Pop
    """
2317 a3316e4a Iustin Pop
    fn = "\n".join
2318 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
2319 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
2320 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
2321 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
2322 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
2323 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
2324 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
2325 a3316e4a Iustin Pop
2326 81a49123 Iustin Pop
    instance_data = fn(instance_names)
2327 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
2328 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
2329 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
2330 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
2331 8113a52e Luca Bigliardi
                     if node.master_candidate)
2332 a3316e4a Iustin Pop
    node_data = fn(node_names)
2333 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
2334 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
2335 f56618e0 Iustin Pop
2336 054596f0 Iustin Pop
    cluster = self._config_data.cluster
2337 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
2338 4f7a6a10 Iustin Pop
2339 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
2340 4f7a6a10 Iustin Pop
2341 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
2342 0fbae49a Balazs Lecz
2343 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
2344 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
2345 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
2346 6c0a75db Dimitris Aragiorgis
    networks = ["%s %s" % (net.uuid, net.name) for net in
2347 6c0a75db Dimitris Aragiorgis
                self._config_data.networks.values()]
2348 6c0a75db Dimitris Aragiorgis
    networks_data = fn(utils.NiceSort(networks))
2349 6f076453 Guido Trotter
2350 2afc9238 Iustin Pop
    ssconf_values = {
2351 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
2352 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
2353 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
2354 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
2355 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
2356 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
2357 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
2358 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
2359 5a8648eb Andrea Spadaccini
      constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
2360 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
2361 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
2362 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
2363 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
2364 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
2365 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
2366 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
2367 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
2368 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
2369 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
2370 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
2371 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
2372 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
2373 6c0a75db Dimitris Aragiorgis
      constants.SS_NETWORKS: networks_data,
2374 03d1dba2 Michael Hanselmann
      }
2375 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
2376 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
2377 2afc9238 Iustin Pop
    if bad_values:
2378 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
2379 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
2380 2afc9238 Iustin Pop
                                      " values: %s" % err)
2381 2afc9238 Iustin Pop
    return ssconf_values
2382 03d1dba2 Michael Hanselmann
2383 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2384 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
2385 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
2386 d367b66c Manuel Franceschini

2387 d367b66c Manuel Franceschini
    """
2388 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2389 d367b66c Manuel Franceschini
2390 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2391 a8083063 Iustin Pop
  def GetVGName(self):
2392 a8083063 Iustin Pop
    """Return the volume group name.
2393 a8083063 Iustin Pop

2394 a8083063 Iustin Pop
    """
2395 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2396 a8083063 Iustin Pop
2397 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2398 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2399 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2400 89ff8e15 Manuel Franceschini

2401 89ff8e15 Manuel Franceschini
    """
2402 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2403 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2404 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2405 89ff8e15 Manuel Franceschini
2406 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2407 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2408 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2409 9e33896b Luca Bigliardi

2410 9e33896b Luca Bigliardi
    """
2411 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2412 9e33896b Luca Bigliardi
2413 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2414 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2415 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2416 9e33896b Luca Bigliardi

2417 9e33896b Luca Bigliardi
    """
2418 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2419 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2420 9e33896b Luca Bigliardi
    self._WriteConfig()
2421 9e33896b Luca Bigliardi
2422 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2423 a8083063 Iustin Pop
  def GetMACPrefix(self):
2424 a8083063 Iustin Pop
    """Return the mac prefix.
2425 a8083063 Iustin Pop

2426 a8083063 Iustin Pop
    """
2427 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2428 62779dd0 Iustin Pop
2429 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2430 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2431 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2432 62779dd0 Iustin Pop

2433 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2434 c41eea6e Iustin Pop
    @return: the cluster object
2435 62779dd0 Iustin Pop

2436 62779dd0 Iustin Pop
    """
2437 62779dd0 Iustin Pop
    return self._config_data.cluster
2438 e00fb268 Iustin Pop
2439 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2440 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2441 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2442 51cb1581 Luca Bigliardi

2443 51cb1581 Luca Bigliardi
    """
2444 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2445 51cb1581 Luca Bigliardi
2446 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2447 d1547283 Dimitris Aragiorgis
  def Update(self, target, feedback_fn, ec_id=None):
2448 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2449 e00fb268 Iustin Pop

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

2456 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2457 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2458 c41eea6e Iustin Pop
        the cluster
2459 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2460 c41eea6e Iustin Pop

2461 e00fb268 Iustin Pop
    """
2462 e00fb268 Iustin Pop
    if self._config_data is None:
2463 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2464 3ecf6786 Iustin Pop
                                   " cannot save.")
2465 f34901f8 Iustin Pop
    update_serial = False
2466 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2467 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2468 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2469 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2470 f34901f8 Iustin Pop
      update_serial = True
2471 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2472 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2473 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2474 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2475 1e0d3321 Dimitris Aragiorgis
    elif isinstance(target, objects.Network):
2476 1e0d3321 Dimitris Aragiorgis
      test = target in self._config_data.networks.values()
2477 e00fb268 Iustin Pop
    else:
2478 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2479 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2480 e00fb268 Iustin Pop
    if not test:
2481 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2482 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2483 f34901f8 Iustin Pop
    target.serial_no += 1
2484 d693c864 Iustin Pop
    target.mtime = now = time.time()
2485 f34901f8 Iustin Pop
2486 cff4c037 Iustin Pop
    if update_serial:
2487 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2488 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2489 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2490 b989e85d Iustin Pop
2491 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2492 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
2493 61cf6b5e Iustin Pop
2494 d1547283 Dimitris Aragiorgis
    if ec_id is not None:
2495 d1547283 Dimitris Aragiorgis
      # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams
2496 d1547283 Dimitris Aragiorgis
      self._UnlockedCommitTemporaryIps(ec_id)
2497 d1547283 Dimitris Aragiorgis
2498 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2499 73064714 Guido Trotter
2500 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2501 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2502 73064714 Guido Trotter
    """Drop per-execution-context reservations
2503 73064714 Guido Trotter

2504 73064714 Guido Trotter
    """
2505 d8aee57e Iustin Pop
    for rm in self._all_rms:
2506 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)
2507 6c0a75db Dimitris Aragiorgis
2508 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2509 6c0a75db Dimitris Aragiorgis
  def GetAllNetworksInfo(self):
2510 6a94d553 Dimitris Aragiorgis
    """Get configuration info of all the networks.
2511 6c0a75db Dimitris Aragiorgis

2512 6c0a75db Dimitris Aragiorgis
    """
2513 6c0a75db Dimitris Aragiorgis
    return dict(self._config_data.networks)
2514 6c0a75db Dimitris Aragiorgis
2515 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetworkList(self):
2516 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2517 6c0a75db Dimitris Aragiorgis

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

2520 6c0a75db Dimitris Aragiorgis
    """
2521 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks.keys()
2522 6c0a75db Dimitris Aragiorgis
2523 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2524 6c0a75db Dimitris Aragiorgis
  def GetNetworkList(self):
2525 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2526 6c0a75db Dimitris Aragiorgis

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

2529 6c0a75db Dimitris Aragiorgis
    """
2530 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetworkList()
2531 6c0a75db Dimitris Aragiorgis
2532 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2533 6c0a75db Dimitris Aragiorgis
  def GetNetworkNames(self):
2534 6c0a75db Dimitris Aragiorgis
    """Get a list of network names
2535 6c0a75db Dimitris Aragiorgis

2536 6c0a75db Dimitris Aragiorgis
    """
2537 beb81ea5 Dimitris Aragiorgis
    names = [net.name
2538 beb81ea5 Dimitris Aragiorgis
             for net in self._config_data.networks.values()]
2539 6c0a75db Dimitris Aragiorgis
    return names
2540 6c0a75db Dimitris Aragiorgis
2541 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetwork(self, uuid):
2542 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2543 6c0a75db Dimitris Aragiorgis

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

2546 6c0a75db Dimitris Aragiorgis
    """
2547 6c0a75db Dimitris Aragiorgis
    if uuid not in self._config_data.networks:
2548 6c0a75db Dimitris Aragiorgis
      return None
2549 6c0a75db Dimitris Aragiorgis
2550 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks[uuid]
2551 6c0a75db Dimitris Aragiorgis
2552 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2553 6c0a75db Dimitris Aragiorgis
  def GetNetwork(self, uuid):
2554 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2555 6c0a75db Dimitris Aragiorgis

2556 6c0a75db Dimitris Aragiorgis
    It takes the information from the configuration file.
2557 6c0a75db Dimitris Aragiorgis

2558 6c0a75db Dimitris Aragiorgis
    @param uuid: UUID of the network
2559 6c0a75db Dimitris Aragiorgis

2560 6c0a75db Dimitris Aragiorgis
    @rtype: L{objects.Network}
2561 6c0a75db Dimitris Aragiorgis
    @return: the network object
2562 6c0a75db Dimitris Aragiorgis

2563 6c0a75db Dimitris Aragiorgis
    """
2564 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetwork(uuid)
2565 6c0a75db Dimitris Aragiorgis
2566 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2567 6c0a75db Dimitris Aragiorgis
  def AddNetwork(self, net, ec_id, check_uuid=True):
2568 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2569 6c0a75db Dimitris Aragiorgis

2570 6c0a75db Dimitris Aragiorgis
    @type net: L{objects.Network}
2571 6c0a75db Dimitris Aragiorgis
    @param net: the Network object to add
2572 6c0a75db Dimitris Aragiorgis
    @type ec_id: string
2573 6c0a75db Dimitris Aragiorgis
    @param ec_id: unique id for the job to use when creating a missing UUID
2574 6c0a75db Dimitris Aragiorgis

2575 6c0a75db Dimitris Aragiorgis
    """
2576 6c0a75db Dimitris Aragiorgis
    self._UnlockedAddNetwork(net, ec_id, check_uuid)
2577 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2578 6c0a75db Dimitris Aragiorgis
2579 6c0a75db Dimitris Aragiorgis
  def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
2580 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2581 6c0a75db Dimitris Aragiorgis

2582 6c0a75db Dimitris Aragiorgis
    """
2583 6c0a75db Dimitris Aragiorgis
    logging.info("Adding network %s to configuration", net.name)
2584 6c0a75db Dimitris Aragiorgis
2585 6c0a75db Dimitris Aragiorgis
    if check_uuid:
2586 6c0a75db Dimitris Aragiorgis
      self._EnsureUUID(net, ec_id)
2587 6c0a75db Dimitris Aragiorgis
2588 6c0a75db Dimitris Aragiorgis
    net.serial_no = 1
2589 22ff02a7 Christos Stavrakakis
    net.ctime = net.mtime = time.time()
2590 6c0a75db Dimitris Aragiorgis
    self._config_data.networks[net.uuid] = net
2591 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2592 6c0a75db Dimitris Aragiorgis
2593 6c0a75db Dimitris Aragiorgis
  def _UnlockedLookupNetwork(self, target):
2594 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2595 6c0a75db Dimitris Aragiorgis

2596 6c0a75db Dimitris Aragiorgis
    @type target: string
2597 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2598 6c0a75db Dimitris Aragiorgis
    @rtype: string
2599 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2600 6c0a75db Dimitris Aragiorgis
    @raises errors.OpPrereqError: when the target network cannot be found
2601 6c0a75db Dimitris Aragiorgis

2602 6c0a75db Dimitris Aragiorgis
    """
2603 9394f4d1 Dimitris Aragiorgis
    if target is None:
2604 9394f4d1 Dimitris Aragiorgis
      return None
2605 6c0a75db Dimitris Aragiorgis
    if target in self._config_data.networks:
2606 6c0a75db Dimitris Aragiorgis
      return target
2607 6c0a75db Dimitris Aragiorgis
    for net in self._config_data.networks.values():
2608 6c0a75db Dimitris Aragiorgis
      if net.name == target:
2609 6c0a75db Dimitris Aragiorgis
        return net.uuid
2610 1b68f268 Helga Velroyen
    raise errors.OpPrereqError("Network '%s' not found" % target,
2611 1b68f268 Helga Velroyen
                               errors.ECODE_NOENT)
2612 6c0a75db Dimitris Aragiorgis
2613 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2614 6c0a75db Dimitris Aragiorgis
  def LookupNetwork(self, target):
2615 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2616 6c0a75db Dimitris Aragiorgis

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

2619 6c0a75db Dimitris Aragiorgis
    @type target: string
2620 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2621 6c0a75db Dimitris Aragiorgis
    @rtype: string
2622 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2623 6c0a75db Dimitris Aragiorgis

2624 6c0a75db Dimitris Aragiorgis
    """
2625 6c0a75db Dimitris Aragiorgis
    return self._UnlockedLookupNetwork(target)
2626 6c0a75db Dimitris Aragiorgis
2627 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2628 6c0a75db Dimitris Aragiorgis
  def RemoveNetwork(self, network_uuid):
2629 6c0a75db Dimitris Aragiorgis
    """Remove a network from the configuration.
2630 6c0a75db Dimitris Aragiorgis

2631 6c0a75db Dimitris Aragiorgis
    @type network_uuid: string
2632 6c0a75db Dimitris Aragiorgis
    @param network_uuid: the UUID of the network to remove
2633 6c0a75db Dimitris Aragiorgis

2634 6c0a75db Dimitris Aragiorgis
    """
2635 6c0a75db Dimitris Aragiorgis
    logging.info("Removing network %s from configuration", network_uuid)
2636 6c0a75db Dimitris Aragiorgis
2637 6c0a75db Dimitris Aragiorgis
    if network_uuid not in self._config_data.networks:
2638 6c0a75db Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
2639 6c0a75db Dimitris Aragiorgis
2640 6c0a75db Dimitris Aragiorgis
    del self._config_data.networks[network_uuid]
2641 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2642 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2643 ad4a9ae7 Dimitris Aragiorgis
2644 9ccacbc8 Dimitris Aragiorgis
  def _UnlockedGetGroupNetParams(self, net_uuid, node):
2645 ad4a9ae7 Dimitris Aragiorgis
    """Get the netparams (mode, link) of a network.
2646 ad4a9ae7 Dimitris Aragiorgis

2647 ad4a9ae7 Dimitris Aragiorgis
    Get a network's netparams for a given node.
2648 ad4a9ae7 Dimitris Aragiorgis

2649 9ccacbc8 Dimitris Aragiorgis
    @type net_uuid: string
2650 9ccacbc8 Dimitris Aragiorgis
    @param net_uuid: network uuid
2651 ad4a9ae7 Dimitris Aragiorgis
    @type node: string
2652 ad4a9ae7 Dimitris Aragiorgis
    @param node: node name
2653 ad4a9ae7 Dimitris Aragiorgis
    @rtype: dict or None
2654 ad4a9ae7 Dimitris Aragiorgis
    @return: netparams
2655 ad4a9ae7 Dimitris Aragiorgis

2656 ad4a9ae7 Dimitris Aragiorgis
    """
2657 ad4a9ae7 Dimitris Aragiorgis
    node_info = self._UnlockedGetNodeInfo(node)
2658 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2659 ad4a9ae7 Dimitris Aragiorgis
    netparams = nodegroup_info.networks.get(net_uuid, None)
2660 ad4a9ae7 Dimitris Aragiorgis
2661 ad4a9ae7 Dimitris Aragiorgis
    return netparams
2662 ad4a9ae7 Dimitris Aragiorgis
2663 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2664 9ccacbc8 Dimitris Aragiorgis
  def GetGroupNetParams(self, net_uuid, node):
2665 ad4a9ae7 Dimitris Aragiorgis
    """Locking wrapper of _UnlockedGetGroupNetParams()
2666 ad4a9ae7 Dimitris Aragiorgis

2667 ad4a9ae7 Dimitris Aragiorgis
    """
2668 9ccacbc8 Dimitris Aragiorgis
    return self._UnlockedGetGroupNetParams(net_uuid, node)
2669 ad4a9ae7 Dimitris Aragiorgis
2670 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2671 ad4a9ae7 Dimitris Aragiorgis
  def CheckIPInNodeGroup(self, ip, node):
2672 6a94d553 Dimitris Aragiorgis
    """Check IP uniqueness in nodegroup.
2673 6a94d553 Dimitris Aragiorgis

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

2678 ad4a9ae7 Dimitris Aragiorgis
    @type ip: string
2679 ad4a9ae7 Dimitris Aragiorgis
    @param ip: ip address
2680 ad4a9ae7 Dimitris Aragiorgis
    @type node: string
2681 ad4a9ae7 Dimitris Aragiorgis
    @param node: node name
2682 ad4a9ae7 Dimitris Aragiorgis
    @rtype: (string, dict) or (None, None)
2683 ad4a9ae7 Dimitris Aragiorgis
    @return: (network name, netparams)
2684 ad4a9ae7 Dimitris Aragiorgis

2685 ad4a9ae7 Dimitris Aragiorgis
    """
2686 ad4a9ae7 Dimitris Aragiorgis
    if ip is None:
2687 ad4a9ae7 Dimitris Aragiorgis
      return (None, None)
2688 ad4a9ae7 Dimitris Aragiorgis
    node_info = self._UnlockedGetNodeInfo(node)
2689 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2690 ad4a9ae7 Dimitris Aragiorgis
    for net_uuid in nodegroup_info.networks.keys():
2691 ad4a9ae7 Dimitris Aragiorgis
      net_info = self._UnlockedGetNetwork(net_uuid)
2692 ad4a9ae7 Dimitris Aragiorgis
      pool = network.AddressPool(net_info)
2693 beb81ea5 Dimitris Aragiorgis
      if pool.Contains(ip):
2694 ad4a9ae7 Dimitris Aragiorgis
        return (net_info.name, nodegroup_info.networks[net_uuid])
2695 ad4a9ae7 Dimitris Aragiorgis
2696 ad4a9ae7 Dimitris Aragiorgis
    return (None, None)