Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 63c73073

History | View | Annotate | Download (84.9 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 12378fe3 Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 a8083063 Iustin Pop
import os
38 a8083063 Iustin Pop
import random
39 d8470559 Michael Hanselmann
import logging
40 d693c864 Iustin Pop
import time
41 54c31fd3 Michael Hanselmann
import itertools
42 a8083063 Iustin Pop
43 a8083063 Iustin Pop
from ganeti import errors
44 f78ede4e Guido Trotter
from ganeti import locking
45 a8083063 Iustin Pop
from ganeti import utils
46 a8083063 Iustin Pop
from ganeti import constants
47 a8083063 Iustin Pop
from ganeti import rpc
48 a8083063 Iustin Pop
from ganeti import objects
49 8d14b30d Iustin Pop
from ganeti import serializer
50 0fbae49a Balazs Lecz
from ganeti import uidpool
51 a744b676 Manuel Franceschini
from ganeti import netutils
52 e60c73a1 René Nussbaumer
from ganeti import runtime
53 57407093 Michael Hanselmann
from ganeti import pathutils
54 6c0a75db Dimitris Aragiorgis
from ganeti import network
55 243cdbcc Michael Hanselmann
56 243cdbcc Michael Hanselmann
57 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
58 f78ede4e Guido Trotter
59 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
60 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
61 4fae38c5 Guido Trotter
62 f78ede4e Guido Trotter
63 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
64 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
65 c41eea6e Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

393 ad4a9ae7 Dimitris Aragiorgis
    """
394 ad4a9ae7 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
395 ad4a9ae7 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
396 ad4a9ae7 Dimitris Aragiorgis
    try:
397 ad4a9ae7 Dimitris Aragiorgis
      isreserved = pool.IsReserved(address)
398 ad4a9ae7 Dimitris Aragiorgis
    except errors.AddressPoolError:
399 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address not in network")
400 ad4a9ae7 Dimitris Aragiorgis
    if isreserved:
401 ad4a9ae7 Dimitris Aragiorgis
      raise errors.ReservationError("IP address already in use")
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 ad4a9ae7 Dimitris Aragiorgis
  def ReserveIp(self, net, address, ec_id):
409 ad4a9ae7 Dimitris Aragiorgis
    """Reserve a given IPv4 address for use by an instance.
410 ad4a9ae7 Dimitris Aragiorgis

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

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

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

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

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

444 923b1523 Iustin Pop
    """
445 923b1523 Iustin Pop
    lvnames = set()
446 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
447 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
448 923b1523 Iustin Pop
      for lv_list in node_data.values():
449 923b1523 Iustin Pop
        lvnames.update(lv_list)
450 923b1523 Iustin Pop
    return lvnames
451 923b1523 Iustin Pop
452 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
453 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
454 34e54ebc Iustin Pop

455 34e54ebc Iustin Pop
    @type include_temporary: boolean
456 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
457 34e54ebc Iustin Pop
    @rtype: set
458 34e54ebc Iustin Pop
    @return: a set of IDs
459 34e54ebc Iustin Pop

460 34e54ebc Iustin Pop
    """
461 34e54ebc Iustin Pop
    existing = set()
462 34e54ebc Iustin Pop
    if include_temporary:
463 4fae38c5 Guido Trotter
      existing.update(self._temporary_ids.GetReserved())
464 34e54ebc Iustin Pop
    existing.update(self._AllLVs())
465 34e54ebc Iustin Pop
    existing.update(self._config_data.instances.keys())
466 34e54ebc Iustin Pop
    existing.update(self._config_data.nodes.keys())
467 76d5d3a3 Iustin Pop
    existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
468 34e54ebc Iustin Pop
    return existing
469 34e54ebc Iustin Pop
470 4fae38c5 Guido Trotter
  def _GenerateUniqueID(self, ec_id):
471 430b923c Iustin Pop
    """Generate an unique UUID.
472 923b1523 Iustin Pop

473 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
474 923b1523 Iustin Pop
    duplicates.
475 923b1523 Iustin Pop

476 c41eea6e Iustin Pop
    @rtype: string
477 c41eea6e Iustin Pop
    @return: the unique id
478 923b1523 Iustin Pop

479 923b1523 Iustin Pop
    """
480 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
481 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
482 923b1523 Iustin Pop
483 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
484 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
485 430b923c Iustin Pop
    """Generate an unique ID.
486 430b923c Iustin Pop

487 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
488 430b923c Iustin Pop

489 4fae38c5 Guido Trotter
    @type ec_id: string
490 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
491 34d657ba Iustin Pop

492 34d657ba Iustin Pop
    """
493 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
494 34d657ba Iustin Pop
495 a8083063 Iustin Pop
  def _AllMACs(self):
496 a8083063 Iustin Pop
    """Return all MACs present in the config.
497 a8083063 Iustin Pop

498 c41eea6e Iustin Pop
    @rtype: list
499 c41eea6e Iustin Pop
    @return: the list of all MACs
500 c41eea6e Iustin Pop

501 a8083063 Iustin Pop
    """
502 a8083063 Iustin Pop
    result = []
503 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
504 a8083063 Iustin Pop
      for nic in instance.nics:
505 a8083063 Iustin Pop
        result.append(nic.mac)
506 a8083063 Iustin Pop
507 a8083063 Iustin Pop
    return result
508 a8083063 Iustin Pop
509 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
510 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
511 f9518d38 Iustin Pop

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

515 f9518d38 Iustin Pop
    """
516 f9518d38 Iustin Pop
    def helper(disk, result):
517 f9518d38 Iustin Pop
      """Recursively gather secrets from this disk."""
518 f9518d38 Iustin Pop
      if disk.dev_type == constants.DT_DRBD8:
519 f9518d38 Iustin Pop
        result.append(disk.logical_id[5])
520 f9518d38 Iustin Pop
      if disk.children:
521 f9518d38 Iustin Pop
        for child in disk.children:
522 f9518d38 Iustin Pop
          helper(child, result)
523 f9518d38 Iustin Pop
524 f9518d38 Iustin Pop
    result = []
525 f9518d38 Iustin Pop
    for instance in self._config_data.instances.values():
526 f9518d38 Iustin Pop
      for disk in instance.disks:
527 f9518d38 Iustin Pop
        helper(disk, result)
528 f9518d38 Iustin Pop
529 f9518d38 Iustin Pop
    return result
530 f9518d38 Iustin Pop
531 4b98ac29 Iustin Pop
  def _CheckDiskIDs(self, disk, l_ids, p_ids):
532 4b98ac29 Iustin Pop
    """Compute duplicate disk IDs
533 4b98ac29 Iustin Pop

534 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
535 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
536 4b98ac29 Iustin Pop
    @type l_ids: list
537 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
538 4b98ac29 Iustin Pop
    @type p_ids: list
539 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
540 4b98ac29 Iustin Pop
    @rtype: list
541 4b98ac29 Iustin Pop
    @return: a list of error messages
542 4b98ac29 Iustin Pop

543 4b98ac29 Iustin Pop
    """
544 4b98ac29 Iustin Pop
    result = []
545 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
546 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
547 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
548 25ae22e4 Iustin Pop
      else:
549 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
550 25ae22e4 Iustin Pop
    if disk.physical_id is not None:
551 25ae22e4 Iustin Pop
      if disk.physical_id in p_ids:
552 25ae22e4 Iustin Pop
        result.append("duplicate physical id %s" % str(disk.physical_id))
553 25ae22e4 Iustin Pop
      else:
554 25ae22e4 Iustin Pop
        p_ids.append(disk.physical_id)
555 4b98ac29 Iustin Pop
556 4b98ac29 Iustin Pop
    if disk.children:
557 4b98ac29 Iustin Pop
      for child in disk.children:
558 4b98ac29 Iustin Pop
        result.extend(self._CheckDiskIDs(child, l_ids, p_ids))
559 4b98ac29 Iustin Pop
    return result
560 4b98ac29 Iustin Pop
561 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
562 a8efbb40 Iustin Pop
    """Verify function.
563 a8efbb40 Iustin Pop

564 4a89c54a Iustin Pop
    @rtype: list
565 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
566 4a89c54a Iustin Pop
        configuration errors
567 4a89c54a Iustin Pop

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

821 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
822 4a89c54a Iustin Pop

823 4a89c54a Iustin Pop
    @rtype: list
824 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
825 4a89c54a Iustin Pop
        configuration errors
826 4a89c54a Iustin Pop

827 4a89c54a Iustin Pop
    """
828 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
829 4a89c54a Iustin Pop
830 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
831 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
832 a8083063 Iustin Pop

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

835 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
836 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
837 a8083063 Iustin Pop
    node.
838 a8083063 Iustin Pop

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

841 a8083063 Iustin Pop
    """
842 a8083063 Iustin Pop
    if disk.children:
843 a8083063 Iustin Pop
      for child in disk.children:
844 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
845 a8083063 Iustin Pop
846 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
847 a8083063 Iustin Pop
      return
848 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
849 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
850 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
851 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
852 3ecf6786 Iustin Pop
                                        node_name)
853 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
854 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
855 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
856 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
857 a8083063 Iustin Pop
                                        " for %s" % str(disk))
858 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
859 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
860 a8083063 Iustin Pop
      if pnode == node_name:
861 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
862 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
863 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
864 a8083063 Iustin Pop
    else:
865 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
866 a8083063 Iustin Pop
    return
867 a8083063 Iustin Pop
868 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
869 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
870 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
871 f78ede4e Guido Trotter

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

874 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
875 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
876 f78ede4e Guido Trotter
    node.
877 f78ede4e Guido Trotter

878 f78ede4e Guido Trotter
    """
879 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
880 f78ede4e Guido Trotter
881 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
882 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
883 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
884 b2fddf63 Iustin Pop

885 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
886 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
887 3b3b1bca Dimitris Aragiorgis
        configuration is stable
888 3b3b1bca Dimitris Aragiorgis

889 b2fddf63 Iustin Pop
    """
890 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
891 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
892 264bb3c5 Michael Hanselmann
893 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
894 264bb3c5 Michael Hanselmann
895 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
896 b2fddf63 Iustin Pop
  def GetPortList(self):
897 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
898 264bb3c5 Michael Hanselmann

899 264bb3c5 Michael Hanselmann
    """
900 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
901 264bb3c5 Michael Hanselmann
902 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
903 a8083063 Iustin Pop
  def AllocatePort(self):
904 a8083063 Iustin Pop
    """Allocate a port.
905 a8083063 Iustin Pop

906 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
907 b2fddf63 Iustin Pop
    default port range (and in this case we increase
908 b2fddf63 Iustin Pop
    highest_used_port).
909 a8083063 Iustin Pop

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

928 4a89c54a Iustin Pop
    @rtype: (dict, list)
929 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
930 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
931 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
932 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
933 4a89c54a Iustin Pop
        should raise an exception
934 a81c53c9 Iustin Pop

935 a81c53c9 Iustin Pop
    """
936 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
937 4a89c54a Iustin Pop
      duplicates = []
938 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
939 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
940 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
941 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
942 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
943 a81c53c9 Iustin Pop
          if port in used[node]:
944 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
945 4a89c54a Iustin Pop
          else:
946 4a89c54a Iustin Pop
            used[node][port] = instance_name
947 a81c53c9 Iustin Pop
      if disk.children:
948 a81c53c9 Iustin Pop
        for child in disk.children:
949 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
950 4a89c54a Iustin Pop
      return duplicates
951 a81c53c9 Iustin Pop
952 4a89c54a Iustin Pop
    duplicates = []
953 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
954 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
955 79b26a7a Iustin Pop
      for disk in instance.disks:
956 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
957 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
958 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
959 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
960 4a89c54a Iustin Pop
      else:
961 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
962 4a89c54a Iustin Pop
    return my_dict, duplicates
963 a81c53c9 Iustin Pop
964 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
965 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
966 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
967 6d2e83d5 Iustin Pop

968 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
969 6d2e83d5 Iustin Pop

970 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
971 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
972 6d2e83d5 Iustin Pop
        an empty list).
973 6d2e83d5 Iustin Pop

974 6d2e83d5 Iustin Pop
    """
975 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
976 4a89c54a Iustin Pop
    if duplicates:
977 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
978 4a89c54a Iustin Pop
                                      str(duplicates))
979 4a89c54a Iustin Pop
    return d_map
980 6d2e83d5 Iustin Pop
981 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
982 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
983 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
984 a81c53c9 Iustin Pop

985 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
986 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
987 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
988 a81c53c9 Iustin Pop
    order as the passed nodes.
989 a81c53c9 Iustin Pop

990 32388e6d Iustin Pop
    @type instance: string
991 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
992 32388e6d Iustin Pop

993 a81c53c9 Iustin Pop
    """
994 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
995 4a89c54a Iustin Pop
           "Invalid argument '%s' passed to AllocateDRBDMinor" % instance
996 32388e6d Iustin Pop
997 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
998 4a89c54a Iustin Pop
    if duplicates:
999 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
1000 4a89c54a Iustin Pop
                                      str(duplicates))
1001 a81c53c9 Iustin Pop
    result = []
1002 a81c53c9 Iustin Pop
    for nname in nodes:
1003 a81c53c9 Iustin Pop
      ndata = d_map[nname]
1004 a81c53c9 Iustin Pop
      if not ndata:
1005 a81c53c9 Iustin Pop
        # no minors used, we can start at 0
1006 a81c53c9 Iustin Pop
        result.append(0)
1007 a81c53c9 Iustin Pop
        ndata[0] = instance
1008 d48663e4 Iustin Pop
        self._temporary_drbds[(nname, 0)] = instance
1009 a81c53c9 Iustin Pop
        continue
1010 a81c53c9 Iustin Pop
      keys = ndata.keys()
1011 a81c53c9 Iustin Pop
      keys.sort()
1012 a81c53c9 Iustin Pop
      ffree = utils.FirstFree(keys)
1013 a81c53c9 Iustin Pop
      if ffree is None:
1014 a81c53c9 Iustin Pop
        # return the next minor
1015 a81c53c9 Iustin Pop
        # TODO: implement high-limit check
1016 a81c53c9 Iustin Pop
        minor = keys[-1] + 1
1017 a81c53c9 Iustin Pop
      else:
1018 a81c53c9 Iustin Pop
        minor = ffree
1019 4a89c54a Iustin Pop
      # double-check minor against current instances
1020 4a89c54a Iustin Pop
      assert minor not in d_map[nname], \
1021 4a89c54a Iustin Pop
             ("Attempt to reuse allocated DRBD minor %d on node %s,"
1022 4a89c54a Iustin Pop
              " already allocated to instance %s" %
1023 4a89c54a Iustin Pop
              (minor, nname, d_map[nname][minor]))
1024 a81c53c9 Iustin Pop
      ndata[minor] = instance
1025 4a89c54a Iustin Pop
      # double-check minor against reservation
1026 4a89c54a Iustin Pop
      r_key = (nname, minor)
1027 4a89c54a Iustin Pop
      assert r_key not in self._temporary_drbds, \
1028 4a89c54a Iustin Pop
             ("Attempt to reuse reserved DRBD minor %d on node %s,"
1029 4a89c54a Iustin Pop
              " reserved for instance %s" %
1030 4a89c54a Iustin Pop
              (minor, nname, self._temporary_drbds[r_key]))
1031 4a89c54a Iustin Pop
      self._temporary_drbds[r_key] = instance
1032 4a89c54a Iustin Pop
      result.append(minor)
1033 a81c53c9 Iustin Pop
    logging.debug("Request to allocate drbd minors, input: %s, returning %s",
1034 a81c53c9 Iustin Pop
                  nodes, result)
1035 a81c53c9 Iustin Pop
    return result
1036 a81c53c9 Iustin Pop
1037 61cf6b5e Iustin Pop
  def _UnlockedReleaseDRBDMinors(self, instance):
1038 a81c53c9 Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
1039 a81c53c9 Iustin Pop

1040 a81c53c9 Iustin Pop
    @type instance: string
1041 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
1042 a81c53c9 Iustin Pop
                     released
1043 a81c53c9 Iustin Pop

1044 a81c53c9 Iustin Pop
    """
1045 32388e6d Iustin Pop
    assert isinstance(instance, basestring), \
1046 32388e6d Iustin Pop
           "Invalid argument passed to ReleaseDRBDMinors"
1047 a81c53c9 Iustin Pop
    for key, name in self._temporary_drbds.items():
1048 a81c53c9 Iustin Pop
      if name == instance:
1049 a81c53c9 Iustin Pop
        del self._temporary_drbds[key]
1050 a81c53c9 Iustin Pop
1051 61cf6b5e Iustin Pop
  @locking.ssynchronized(_config_lock)
1052 61cf6b5e Iustin Pop
  def ReleaseDRBDMinors(self, instance):
1053 61cf6b5e Iustin Pop
    """Release temporary drbd minors allocated for a given instance.
1054 61cf6b5e Iustin Pop

1055 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
1056 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
1057 61cf6b5e Iustin Pop
    functions.
1058 61cf6b5e Iustin Pop

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

1061 61cf6b5e Iustin Pop
    @type instance: string
1062 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
1063 61cf6b5e Iustin Pop
                     released
1064 61cf6b5e Iustin Pop

1065 61cf6b5e Iustin Pop
    """
1066 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
1067 61cf6b5e Iustin Pop
1068 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1069 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
1070 4a8b186a Michael Hanselmann
    """Get the configuration version.
1071 4a8b186a Michael Hanselmann

1072 4a8b186a Michael Hanselmann
    @return: Config version
1073 4a8b186a Michael Hanselmann

1074 4a8b186a Michael Hanselmann
    """
1075 4a8b186a Michael Hanselmann
    return self._config_data.version
1076 4a8b186a Michael Hanselmann
1077 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1078 4a8b186a Michael Hanselmann
  def GetClusterName(self):
1079 4a8b186a Michael Hanselmann
    """Get cluster name.
1080 4a8b186a Michael Hanselmann

1081 4a8b186a Michael Hanselmann
    @return: Cluster name
1082 4a8b186a Michael Hanselmann

1083 4a8b186a Michael Hanselmann
    """
1084 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
1085 4a8b186a Michael Hanselmann
1086 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1087 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
1088 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
1089 4a8b186a Michael Hanselmann

1090 4a8b186a Michael Hanselmann
    @return: Master hostname
1091 4a8b186a Michael Hanselmann

1092 4a8b186a Michael Hanselmann
    """
1093 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
1094 4a8b186a Michael Hanselmann
1095 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1096 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
1097 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
1098 4a8b186a Michael Hanselmann

1099 4a8b186a Michael Hanselmann
    @return: Master IP
1100 4a8b186a Michael Hanselmann

1101 4a8b186a Michael Hanselmann
    """
1102 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
1103 4a8b186a Michael Hanselmann
1104 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1105 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
1106 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
1107 4a8b186a Michael Hanselmann

1108 4a8b186a Michael Hanselmann
    """
1109 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
1110 4a8b186a Michael Hanselmann
1111 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1112 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
1113 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
1114 5a8648eb Andrea Spadaccini

1115 5a8648eb Andrea Spadaccini
    """
1116 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
1117 5a8648eb Andrea Spadaccini
1118 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1119 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
1120 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
1121 33be7576 Andrea Spadaccini

1122 33be7576 Andrea Spadaccini
    """
1123 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
1124 33be7576 Andrea Spadaccini
1125 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1126 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
1127 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
1128 4a8b186a Michael Hanselmann

1129 4a8b186a Michael Hanselmann
    """
1130 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
1131 4a8b186a Michael Hanselmann
1132 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1133 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
1134 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
1135 4b97f902 Apollon Oikonomopoulos

1136 4b97f902 Apollon Oikonomopoulos
    """
1137 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1138 4b97f902 Apollon Oikonomopoulos
1139 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1140 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1141 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1142 4a8b186a Michael Hanselmann

1143 4a8b186a Michael Hanselmann
    """
1144 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
1145 4a8b186a Michael Hanselmann
1146 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1147 a8083063 Iustin Pop
  def GetHostKey(self):
1148 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1149 a8083063 Iustin Pop

1150 c41eea6e Iustin Pop
    @rtype: string
1151 c41eea6e Iustin Pop
    @return: the rsa hostkey
1152 a8083063 Iustin Pop

1153 a8083063 Iustin Pop
    """
1154 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1155 a8083063 Iustin Pop
1156 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1157 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1158 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1159 bf4af505 Apollon Oikonomopoulos

1160 bf4af505 Apollon Oikonomopoulos
    """
1161 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1162 bf4af505 Apollon Oikonomopoulos
1163 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1164 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1165 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1166 868a98ca Manuel Franceschini

1167 868a98ca Manuel Franceschini
    @return: primary ip family
1168 868a98ca Manuel Franceschini

1169 868a98ca Manuel Franceschini
    """
1170 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1171 868a98ca Manuel Franceschini
1172 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1173 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1174 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1175 c9f4b8e6 Andrea Spadaccini

1176 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1177 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1178 c9f4b8e6 Andrea Spadaccini

1179 c9f4b8e6 Andrea Spadaccini
    """
1180 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1181 5ae4945a Iustin Pop
    result = objects.MasterNetworkParameters(
1182 5ae4945a Iustin Pop
      name=cluster.master_node, ip=cluster.master_ip,
1183 5ae4945a Iustin Pop
      netmask=cluster.master_netmask, netdev=cluster.master_netdev,
1184 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1185 c9f4b8e6 Andrea Spadaccini
1186 f9d20654 Andrea Spadaccini
    return result
1187 f9d20654 Andrea Spadaccini
1188 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1189 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1190 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1191 e11a1b77 Adeodato Simo

1192 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1193 90e99856 Adeodato Simo
    according to their default values.
1194 90e99856 Adeodato Simo

1195 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1196 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1197 e11a1b77 Adeodato Simo
    @type ec_id: string
1198 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1199 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1200 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1201 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1202 e11a1b77 Adeodato Simo
                       configuration already
1203 e11a1b77 Adeodato Simo

1204 e11a1b77 Adeodato Simo
    """
1205 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1206 e11a1b77 Adeodato Simo
    self._WriteConfig()
1207 e11a1b77 Adeodato Simo
1208 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1209 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1210 e11a1b77 Adeodato Simo

1211 e11a1b77 Adeodato Simo
    """
1212 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
1213 e11a1b77 Adeodato Simo
1214 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
1215 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
1216 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
1217 e11a1b77 Adeodato Simo
    if check_uuid:
1218 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
1219 e11a1b77 Adeodato Simo
1220 18ffc0fe Stephen Shirley
    try:
1221 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
1222 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
1223 18ffc0fe Stephen Shirley
      pass
1224 18ffc0fe Stephen Shirley
    else:
1225 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
1226 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
1227 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
1228 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
1229 18ffc0fe Stephen Shirley
1230 e11a1b77 Adeodato Simo
    group.serial_no = 1
1231 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
1232 90e99856 Adeodato Simo
    group.UpgradeConfig()
1233 e11a1b77 Adeodato Simo
1234 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
1235 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1236 e11a1b77 Adeodato Simo
1237 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1238 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
1239 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
1240 e11a1b77 Adeodato Simo

1241 e11a1b77 Adeodato Simo
    @type group_uuid: string
1242 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1243 e11a1b77 Adeodato Simo

1244 e11a1b77 Adeodato Simo
    """
1245 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1246 e11a1b77 Adeodato Simo
1247 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1248 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1249 e11a1b77 Adeodato Simo
1250 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1251 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1252 0389c42a Stephen Shirley
1253 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1254 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1255 e11a1b77 Adeodato Simo
    self._WriteConfig()
1256 e11a1b77 Adeodato Simo
1257 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1258 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1259 eaa98a04 Guido Trotter

1260 eaa98a04 Guido Trotter
    @type target: string or None
1261 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1262 eaa98a04 Guido Trotter
    @rtype: string
1263 412b3531 Guido Trotter
    @return: nodegroup UUID
1264 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1265 eaa98a04 Guido Trotter

1266 eaa98a04 Guido Trotter
    """
1267 eaa98a04 Guido Trotter
    if target is None:
1268 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1269 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1270 2ed0e208 Iustin Pop
                                   " group must be specified explicitly.")
1271 eaa98a04 Guido Trotter
      else:
1272 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1273 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1274 eaa98a04 Guido Trotter
      return target
1275 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1276 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1277 eaa98a04 Guido Trotter
        return nodegroup.uuid
1278 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1279 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1280 eaa98a04 Guido Trotter
1281 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1282 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1283 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1284 e85d8982 Stephen Shirley

1285 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1286 e85d8982 Stephen Shirley

1287 e85d8982 Stephen Shirley
    @type target: string or None
1288 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1289 e85d8982 Stephen Shirley
    @rtype: string
1290 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1291 e85d8982 Stephen Shirley

1292 e85d8982 Stephen Shirley
    """
1293 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1294 e85d8982 Stephen Shirley
1295 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1296 648e4196 Guido Trotter
    """Lookup a node group.
1297 648e4196 Guido Trotter

1298 648e4196 Guido Trotter
    @type uuid: string
1299 648e4196 Guido Trotter
    @param uuid: group UUID
1300 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1301 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1302 648e4196 Guido Trotter

1303 648e4196 Guido Trotter
    """
1304 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1305 648e4196 Guido Trotter
      return None
1306 648e4196 Guido Trotter
1307 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1308 648e4196 Guido Trotter
1309 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1310 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1311 5768e6a6 René Nussbaumer
    """Lookup a node group.
1312 5768e6a6 René Nussbaumer

1313 5768e6a6 René Nussbaumer
    @type uuid: string
1314 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1315 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1316 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1317 5768e6a6 René Nussbaumer

1318 5768e6a6 René Nussbaumer
    """
1319 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1320 5768e6a6 René Nussbaumer
1321 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1322 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1323 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1324 622444e5 Iustin Pop

1325 622444e5 Iustin Pop
    """
1326 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1327 622444e5 Iustin Pop
1328 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1329 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1330 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1331 1ac6f2ad Guido Trotter

1332 1ac6f2ad Guido Trotter
    """
1333 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1334 1ac6f2ad Guido Trotter
1335 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1336 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1337 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1338 dac81741 Michael Hanselmann

1339 dac81741 Michael Hanselmann
    """
1340 dac81741 Michael Hanselmann
    ngfn = lambda node_name: self._UnlockedGetNodeInfo(node_name).group
1341 dac81741 Michael Hanselmann
    return frozenset(member_name
1342 dac81741 Michael Hanselmann
                     for node_name in nodes
1343 dac81741 Michael Hanselmann
                     for member_name in
1344 dac81741 Michael Hanselmann
                       self._UnlockedGetNodeGroup(ngfn(node_name)).members)
1345 dac81741 Michael Hanselmann
1346 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1347 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1348 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1349 080fbeea Michael Hanselmann

1350 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1351 080fbeea Michael Hanselmann
    @rtype: list
1352 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1353 080fbeea Michael Hanselmann

1354 080fbeea Michael Hanselmann
    """
1355 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1356 080fbeea Michael Hanselmann
1357 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1358 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1359 a8083063 Iustin Pop
    """Add an instance to the config.
1360 a8083063 Iustin Pop

1361 a8083063 Iustin Pop
    This should be used after creating a new instance.
1362 a8083063 Iustin Pop

1363 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1364 c41eea6e Iustin Pop
    @param instance: the instance object
1365 c41eea6e Iustin Pop

1366 a8083063 Iustin Pop
    """
1367 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1368 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1369 a8083063 Iustin Pop
1370 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1371 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1372 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1373 923b1523 Iustin Pop
1374 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1375 e4640214 Guido Trotter
    for nic in instance.nics:
1376 e4640214 Guido Trotter
      if nic.mac in all_macs:
1377 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1378 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1379 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1380 430b923c Iustin Pop
1381 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1382 e4640214 Guido Trotter
1383 b989e85d Iustin Pop
    instance.serial_no = 1
1384 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1385 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1386 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1387 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1388 e8e079f3 Dimitris Aragiorgis
    self._UnlockedCommitTemporaryIps(ec_id)
1389 a8083063 Iustin Pop
    self._WriteConfig()
1390 a8083063 Iustin Pop
1391 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1392 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1393 430b923c Iustin Pop

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

1397 430b923c Iustin Pop
    """
1398 430b923c Iustin Pop
    if not item.uuid:
1399 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1400 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1401 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1402 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1403 430b923c Iustin Pop
1404 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1405 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1406 a8083063 Iustin Pop

1407 a8083063 Iustin Pop
    """
1408 2e04d454 Agata Murawska
    assert status in constants.ADMINST_ALL, \
1409 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1410 a8083063 Iustin Pop
1411 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1412 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1413 3ecf6786 Iustin Pop
                                      instance_name)
1414 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1415 9ca8a7c5 Agata Murawska
    if instance.admin_state != status:
1416 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1417 b989e85d Iustin Pop
      instance.serial_no += 1
1418 d693c864 Iustin Pop
      instance.mtime = time.time()
1419 455a3445 Iustin Pop
      self._WriteConfig()
1420 a8083063 Iustin Pop
1421 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1422 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1423 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1424 6a408fb2 Iustin Pop

1425 6a408fb2 Iustin Pop
    """
1426 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_UP)
1427 57de31c0 Agata Murawska
1428 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1429 57de31c0 Agata Murawska
  def MarkInstanceOffline(self, instance_name):
1430 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1431 57de31c0 Agata Murawska

1432 57de31c0 Agata Murawska
    """
1433 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_OFFLINE)
1434 6a408fb2 Iustin Pop
1435 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1436 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1437 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1438 a8083063 Iustin Pop

1439 a8083063 Iustin Pop
    """
1440 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1441 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1442 f396ad8c Vangelis Koukis
1443 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1444 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1445 f396ad8c Vangelis Koukis
    inst = self._config_data.instances[instance_name]
1446 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1447 f396ad8c Vangelis Koukis
    if network_port is not None:
1448 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1449 f396ad8c Vangelis Koukis
1450 ced51149 Dimitris Aragiorgis
    instance = self._UnlockedGetInstanceInfo(instance_name)
1451 ced51149 Dimitris Aragiorgis
1452 ced51149 Dimitris Aragiorgis
    for nic in instance.nics:
1453 ced51149 Dimitris Aragiorgis
      if nic.network is not None and nic.ip is not None:
1454 ced51149 Dimitris Aragiorgis
        net_uuid = self._UnlockedLookupNetwork(nic.network)
1455 ced51149 Dimitris Aragiorgis
        if net_uuid:
1456 ced51149 Dimitris Aragiorgis
          # Return all IP addresses to the respective address pools
1457 e81eef56 Dimitris Aragiorgis
          self._UnlockedCommitIp(constants.RELEASE_ACTION, net_uuid, nic.ip)
1458 ced51149 Dimitris Aragiorgis
1459 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1460 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1461 a8083063 Iustin Pop
    self._WriteConfig()
1462 a8083063 Iustin Pop
1463 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1464 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1465 fc95f88f Iustin Pop
    """Rename an instance.
1466 fc95f88f Iustin Pop

1467 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1468 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1469 fc95f88f Iustin Pop
    rename.
1470 fc95f88f Iustin Pop

1471 fc95f88f Iustin Pop
    """
1472 fc95f88f Iustin Pop
    if old_name not in self._config_data.instances:
1473 fc95f88f Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % old_name)
1474 ea642319 Michael Hanselmann
1475 ea642319 Michael Hanselmann
    # Operate on a copy to not loose instance object in case of a failure
1476 ea642319 Michael Hanselmann
    inst = self._config_data.instances[old_name].Copy()
1477 fc95f88f Iustin Pop
    inst.name = new_name
1478 b23c4333 Manuel Franceschini
1479 ea642319 Michael Hanselmann
    for (idx, disk) in enumerate(inst.disks):
1480 b23c4333 Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
1481 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1482 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1483 ea642319 Michael Hanselmann
        disk.logical_id = (disk.logical_id[0],
1484 ea642319 Michael Hanselmann
                           utils.PathJoin(file_storage_dir, inst.name,
1485 ea642319 Michael Hanselmann
                                          "disk%s" % idx))
1486 ea642319 Michael Hanselmann
        disk.physical_id = disk.logical_id
1487 ea642319 Michael Hanselmann
1488 ea642319 Michael Hanselmann
    # Actually replace instance object
1489 ea642319 Michael Hanselmann
    del self._config_data.instances[old_name]
1490 ea642319 Michael Hanselmann
    self._config_data.instances[inst.name] = inst
1491 b23c4333 Manuel Franceschini
1492 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1493 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1494 1fc34c48 Michael Hanselmann
1495 fc95f88f Iustin Pop
    self._WriteConfig()
1496 fc95f88f Iustin Pop
1497 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1498 a8083063 Iustin Pop
  def MarkInstanceDown(self, instance_name):
1499 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1500 a8083063 Iustin Pop

1501 a8083063 Iustin Pop
    """
1502 2e04d454 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_DOWN)
1503 a8083063 Iustin Pop
1504 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1505 94bbfece Iustin Pop
    """Get the list of instances.
1506 94bbfece Iustin Pop

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

1509 94bbfece Iustin Pop
    """
1510 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1511 94bbfece Iustin Pop
1512 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1513 a8083063 Iustin Pop
  def GetInstanceList(self):
1514 a8083063 Iustin Pop
    """Get the list of instances.
1515 a8083063 Iustin Pop

1516 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1517 c41eea6e Iustin Pop
        'instance1.example.com']
1518 a8083063 Iustin Pop

1519 a8083063 Iustin Pop
    """
1520 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1521 a8083063 Iustin Pop
1522 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1523 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1524 a8083063 Iustin Pop

1525 a8083063 Iustin Pop
    """
1526 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1527 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1528 a8083063 Iustin Pop
1529 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1530 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1531 94bbfece Iustin Pop

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

1534 94bbfece Iustin Pop
    """
1535 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1536 94bbfece Iustin Pop
      return None
1537 94bbfece Iustin Pop
1538 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1539 94bbfece Iustin Pop
1540 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1541 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1542 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1543 a8083063 Iustin Pop

1544 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1545 a8083063 Iustin Pop
    an instance are taken from the live systems.
1546 a8083063 Iustin Pop

1547 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1548 c41eea6e Iustin Pop
        I{instance1.example.com}
1549 a8083063 Iustin Pop

1550 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1551 c41eea6e Iustin Pop
    @return: the instance object
1552 a8083063 Iustin Pop

1553 a8083063 Iustin Pop
    """
1554 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1555 a8083063 Iustin Pop
1556 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1557 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1558 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1559 2674690b Michael Hanselmann

1560 2674690b Michael Hanselmann
    @rtype: frozenset
1561 2674690b Michael Hanselmann

1562 2674690b Michael Hanselmann
    """
1563 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1564 2674690b Michael Hanselmann
    if not instance:
1565 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1566 2674690b Michael Hanselmann
1567 2674690b Michael Hanselmann
    if primary_only:
1568 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1569 2674690b Michael Hanselmann
    else:
1570 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1571 2674690b Michael Hanselmann
1572 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1573 2674690b Michael Hanselmann
                     for node_name in nodes)
1574 2674690b Michael Hanselmann
1575 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1576 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1577 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1578 71333cb9 Iustin Pop

1579 71333cb9 Iustin Pop
    @param instances: list of instance names
1580 71333cb9 Iustin Pop
    @rtype: list
1581 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1582 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1583 71333cb9 Iustin Pop
        node, while keeping the original order
1584 71333cb9 Iustin Pop

1585 71333cb9 Iustin Pop
    """
1586 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1587 71333cb9 Iustin Pop
1588 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1589 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1590 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1591 0b2de758 Iustin Pop

1592 0b2de758 Iustin Pop
    @rtype: dict
1593 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1594 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1595 0b2de758 Iustin Pop

1596 0b2de758 Iustin Pop
    """
1597 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1598 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1599 0b2de758 Iustin Pop
    return my_dict
1600 0b2de758 Iustin Pop
1601 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1602 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1603 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1604 cc19798f Michael Hanselmann

1605 cc19798f Michael Hanselmann
    @type filter_fn: callable
1606 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1607 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1608 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1609 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1610 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1611 cc19798f Michael Hanselmann

1612 cc19798f Michael Hanselmann
    """
1613 cc19798f Michael Hanselmann
    return dict((name, inst)
1614 cc19798f Michael Hanselmann
                for (name, inst) in self._config_data.instances.items()
1615 cc19798f Michael Hanselmann
                if filter_fn(inst))
1616 cc19798f Michael Hanselmann
1617 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1618 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1619 a8083063 Iustin Pop
    """Add a node to the configuration.
1620 a8083063 Iustin Pop

1621 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1622 c41eea6e Iustin Pop
    @param node: a Node instance
1623 a8083063 Iustin Pop

1624 a8083063 Iustin Pop
    """
1625 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1626 d8470559 Michael Hanselmann
1627 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1628 430b923c Iustin Pop
1629 b989e85d Iustin Pop
    node.serial_no = 1
1630 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1631 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1632 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1633 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1634 a8083063 Iustin Pop
    self._WriteConfig()
1635 a8083063 Iustin Pop
1636 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1637 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1638 a8083063 Iustin Pop
    """Remove a node from the configuration.
1639 a8083063 Iustin Pop

1640 a8083063 Iustin Pop
    """
1641 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1642 d8470559 Michael Hanselmann
1643 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1644 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1645 a8083063 Iustin Pop
1646 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1647 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1648 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1649 a8083063 Iustin Pop
    self._WriteConfig()
1650 a8083063 Iustin Pop
1651 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1652 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1653 a8083063 Iustin Pop

1654 a8083063 Iustin Pop
    """
1655 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1656 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1657 a8083063 Iustin Pop
1658 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1659 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1660 a8083063 Iustin Pop

1661 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1662 c41eea6e Iustin Pop
    held.
1663 f78ede4e Guido Trotter

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

1666 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1667 c41eea6e Iustin Pop
    @return: the node object
1668 a8083063 Iustin Pop

1669 a8083063 Iustin Pop
    """
1670 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1671 a8083063 Iustin Pop
      return None
1672 a8083063 Iustin Pop
1673 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1674 a8083063 Iustin Pop
1675 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1676 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1677 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1678 f78ede4e Guido Trotter

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

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

1683 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1684 c41eea6e Iustin Pop
    @return: the node object
1685 f78ede4e Guido Trotter

1686 f78ede4e Guido Trotter
    """
1687 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1688 f78ede4e Guido Trotter
1689 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1690 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1691 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1692 8bf9e9a5 Iustin Pop

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

1695 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1696 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1697 8bf9e9a5 Iustin Pop

1698 8bf9e9a5 Iustin Pop
    """
1699 8bf9e9a5 Iustin Pop
    pri = []
1700 8bf9e9a5 Iustin Pop
    sec = []
1701 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1702 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1703 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1704 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1705 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1706 8bf9e9a5 Iustin Pop
    return (pri, sec)
1707 8bf9e9a5 Iustin Pop
1708 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1709 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1710 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1711 c71b049c Michael Hanselmann

1712 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1713 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1714 c71b049c Michael Hanselmann
    @rtype: frozenset
1715 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1716 c71b049c Michael Hanselmann

1717 c71b049c Michael Hanselmann
    """
1718 c71b049c Michael Hanselmann
    if primary_only:
1719 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1720 c71b049c Michael Hanselmann
    else:
1721 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1722 c71b049c Michael Hanselmann
1723 c71b049c Michael Hanselmann
    return frozenset(inst.name
1724 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1725 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1726 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1727 c71b049c Michael Hanselmann
1728 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1729 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1730 a8083063 Iustin Pop

1731 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1732 c41eea6e Iustin Pop
    held.
1733 c41eea6e Iustin Pop

1734 c41eea6e Iustin Pop
    @rtype: list
1735 f78ede4e Guido Trotter

1736 a8083063 Iustin Pop
    """
1737 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1738 a8083063 Iustin Pop
1739 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1740 f78ede4e Guido Trotter
  def GetNodeList(self):
1741 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1742 f78ede4e Guido Trotter

1743 f78ede4e Guido Trotter
    """
1744 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1745 f78ede4e Guido Trotter
1746 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1747 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1748 94a02bb5 Iustin Pop

1749 94a02bb5 Iustin Pop
    """
1750 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1751 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1752 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1753 94a02bb5 Iustin Pop
1754 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1755 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1756 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1757 6819dc49 Iustin Pop

1758 6819dc49 Iustin Pop
    """
1759 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1760 6819dc49 Iustin Pop
1761 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1762 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1763 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1764 075b62ca Iustin Pop

1765 075b62ca Iustin Pop
    """
1766 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1767 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1768 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1769 075b62ca Iustin Pop
1770 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1771 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1772 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1773 8bf9e9a5 Iustin Pop

1774 8bf9e9a5 Iustin Pop
    """
1775 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1776 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1777 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1778 8bf9e9a5 Iustin Pop
1779 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1780 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1781 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1782 f5eaa3c1 Iustin Pop

1783 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1784 f5eaa3c1 Iustin Pop
    @rtype: list
1785 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1786 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1787 f5eaa3c1 Iustin Pop
        order
1788 f5eaa3c1 Iustin Pop

1789 f5eaa3c1 Iustin Pop
    """
1790 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1791 f5eaa3c1 Iustin Pop
1792 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1793 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1794 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1795 d65e5776 Iustin Pop

1796 d65e5776 Iustin Pop
    @rtype: dict
1797 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1798 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1799 d65e5776 Iustin Pop

1800 d65e5776 Iustin Pop
    """
1801 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1802 ee14d800 Michael Hanselmann
1803 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1804 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1805 ee14d800 Michael Hanselmann

1806 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1807 ee14d800 Michael Hanselmann

1808 ee14d800 Michael Hanselmann
    """
1809 ee14d800 Michael Hanselmann
    return dict([(node, self._UnlockedGetNodeInfo(node))
1810 ee14d800 Michael Hanselmann
                 for node in self._UnlockedGetNodeList()])
1811 d65e5776 Iustin Pop
1812 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1813 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1814 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1815 9d5b1371 Michael Hanselmann

1816 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1817 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1818 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1819 9d5b1371 Michael Hanselmann

1820 9d5b1371 Michael Hanselmann
    """
1821 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1822 9d5b1371 Michael Hanselmann
1823 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1824 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1825 ec0292f1 Iustin Pop

1826 23f06b2b Iustin Pop
    @type exceptions: list
1827 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1828 ec0292f1 Iustin Pop
    @rtype: tuple
1829 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1830 ec0292f1 Iustin Pop

1831 ec0292f1 Iustin Pop
    """
1832 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1833 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1834 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1835 23f06b2b Iustin Pop
        continue
1836 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1837 ec0292f1 Iustin Pop
        mc_max += 1
1838 ec0292f1 Iustin Pop
      if node.master_candidate:
1839 ec0292f1 Iustin Pop
        mc_now += 1
1840 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1841 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1842 ec0292f1 Iustin Pop
1843 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1844 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1845 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1846 ec0292f1 Iustin Pop

1847 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1848 ec0292f1 Iustin Pop

1849 23f06b2b Iustin Pop
    @type exceptions: list
1850 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1851 ec0292f1 Iustin Pop
    @rtype: tuple
1852 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1853 ec0292f1 Iustin Pop

1854 ec0292f1 Iustin Pop
    """
1855 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1856 ec0292f1 Iustin Pop
1857 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1858 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1859 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1860 ec0292f1 Iustin Pop

1861 44485f49 Guido Trotter
    @type exceptions: list
1862 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1863 ec0292f1 Iustin Pop
    @rtype: list
1864 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1865 ec0292f1 Iustin Pop

1866 ec0292f1 Iustin Pop
    """
1867 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1868 ec0292f1 Iustin Pop
    mod_list = []
1869 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1870 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1871 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1872 ec0292f1 Iustin Pop
      for name in node_list:
1873 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1874 ec0292f1 Iustin Pop
          break
1875 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1876 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1877 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1878 ec0292f1 Iustin Pop
          continue
1879 ee513a66 Iustin Pop
        mod_list.append(node)
1880 ec0292f1 Iustin Pop
        node.master_candidate = True
1881 ec0292f1 Iustin Pop
        node.serial_no += 1
1882 ec0292f1 Iustin Pop
        mc_now += 1
1883 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1884 ec0292f1 Iustin Pop
        # this should not happen
1885 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1886 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1887 ec0292f1 Iustin Pop
      if mod_list:
1888 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1889 ec0292f1 Iustin Pop
        self._WriteConfig()
1890 ec0292f1 Iustin Pop
1891 ec0292f1 Iustin Pop
    return mod_list
1892 ec0292f1 Iustin Pop
1893 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1894 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1895 190e3cb6 Guido Trotter

1896 190e3cb6 Guido Trotter
    """
1897 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1898 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1899 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1900 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1901 190e3cb6 Guido Trotter
      # is not found anymore.
1902 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1903 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1904 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1905 190e3cb6 Guido Trotter
1906 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1907 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1908 190e3cb6 Guido Trotter

1909 190e3cb6 Guido Trotter
    """
1910 f936c153 Iustin Pop
    nodegroup = node.group
1911 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1912 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1913 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1914 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1915 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1916 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1917 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1918 190e3cb6 Guido Trotter
    else:
1919 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1920 190e3cb6 Guido Trotter
1921 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
1922 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
1923 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
1924 54c31fd3 Michael Hanselmann

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

1928 54c31fd3 Michael Hanselmann
    """
1929 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
1930 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
1931 54c31fd3 Michael Hanselmann
1932 54c31fd3 Michael Hanselmann
    resmod = []
1933 54c31fd3 Michael Hanselmann
1934 54c31fd3 Michael Hanselmann
    # Try to resolve names/UUIDs first
1935 54c31fd3 Michael Hanselmann
    for (node_name, new_group_uuid) in mods:
1936 54c31fd3 Michael Hanselmann
      try:
1937 54c31fd3 Michael Hanselmann
        node = nodes[node_name]
1938 54c31fd3 Michael Hanselmann
      except KeyError:
1939 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find node '%s'" % node_name)
1940 54c31fd3 Michael Hanselmann
1941 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
1942 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
1943 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
1944 54c31fd3 Michael Hanselmann
                      node_name, node.group)
1945 54c31fd3 Michael Hanselmann
        continue
1946 54c31fd3 Michael Hanselmann
1947 54c31fd3 Michael Hanselmann
      # Try to find current group of node
1948 54c31fd3 Michael Hanselmann
      try:
1949 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
1950 54c31fd3 Michael Hanselmann
      except KeyError:
1951 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
1952 54c31fd3 Michael Hanselmann
                                        node.group)
1953 54c31fd3 Michael Hanselmann
1954 54c31fd3 Michael Hanselmann
      # Try to find new group for node
1955 54c31fd3 Michael Hanselmann
      try:
1956 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
1957 54c31fd3 Michael Hanselmann
      except KeyError:
1958 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
1959 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
1960 54c31fd3 Michael Hanselmann
1961 54c31fd3 Michael Hanselmann
      assert node.name in old_group.members, \
1962 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
1963 54c31fd3 Michael Hanselmann
         " old group '%s'" % (node.name, old_group.uuid))
1964 54c31fd3 Michael Hanselmann
      assert node.name not in new_group.members, \
1965 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
1966 54c31fd3 Michael Hanselmann
         " its new group '%s'" % (node.name, new_group.uuid))
1967 54c31fd3 Michael Hanselmann
1968 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
1969 54c31fd3 Michael Hanselmann
1970 54c31fd3 Michael Hanselmann
    # Apply changes
1971 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
1972 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
1973 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
1974 54c31fd3 Michael Hanselmann
1975 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
1976 54c31fd3 Michael Hanselmann
1977 54c31fd3 Michael Hanselmann
      # Update members of involved groups
1978 54c31fd3 Michael Hanselmann
      if node.name in old_group.members:
1979 54c31fd3 Michael Hanselmann
        old_group.members.remove(node.name)
1980 54c31fd3 Michael Hanselmann
      if node.name not in new_group.members:
1981 54c31fd3 Michael Hanselmann
        new_group.members.append(node.name)
1982 54c31fd3 Michael Hanselmann
1983 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
1984 54c31fd3 Michael Hanselmann
    now = time.time()
1985 75191077 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142
1986 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
1987 54c31fd3 Michael Hanselmann
      obj.mtime = now
1988 54c31fd3 Michael Hanselmann
1989 54c31fd3 Michael Hanselmann
    # Force ssconf update
1990 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1991 54c31fd3 Michael Hanselmann
1992 54c31fd3 Michael Hanselmann
    self._WriteConfig()
1993 54c31fd3 Michael Hanselmann
1994 a8083063 Iustin Pop
  def _BumpSerialNo(self):
1995 a8083063 Iustin Pop
    """Bump up the serial number of the config.
1996 a8083063 Iustin Pop

1997 a8083063 Iustin Pop
    """
1998 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
1999 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
2000 a8083063 Iustin Pop
2001 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
2002 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
2003 76d5d3a3 Iustin Pop

2004 76d5d3a3 Iustin Pop
    """
2005 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
2006 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
2007 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
2008 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
2009 76d5d3a3 Iustin Pop
2010 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
2011 a8083063 Iustin Pop
    """Read the config data from disk.
2012 a8083063 Iustin Pop

2013 a8083063 Iustin Pop
    """
2014 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
2015 13998ef2 Michael Hanselmann
2016 a8083063 Iustin Pop
    try:
2017 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
2018 13998ef2 Michael Hanselmann
    except Exception, err:
2019 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
2020 5b263ed7 Michael Hanselmann
2021 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
2022 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
2023 5b263ed7 Michael Hanselmann
2024 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
2025 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
2026 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
2027 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
2028 90d726a8 Iustin Pop
2029 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
2030 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
2031 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
2032 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
2033 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
2034 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
2035 eb180fe2 Iustin Pop
2036 90d726a8 Iustin Pop
    # Upgrade configuration if needed
2037 90d726a8 Iustin Pop
    data.UpgradeConfig()
2038 90d726a8 Iustin Pop
2039 a8083063 Iustin Pop
    self._config_data = data
2040 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
2041 0779e3aa Iustin Pop
    # ssconf update
2042 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
2043 a8083063 Iustin Pop
2044 76d5d3a3 Iustin Pop
    # And finally run our (custom) config upgrade sequence
2045 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
2046 76d5d3a3 Iustin Pop
2047 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
2048 bd407597 Iustin Pop
2049 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
2050 76d5d3a3 Iustin Pop
    """Run upgrade steps that cannot be done purely in the objects.
2051 76d5d3a3 Iustin Pop

2052 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
2053 76d5d3a3 Iustin Pop
    whole configuration, etc.
2054 76d5d3a3 Iustin Pop

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

2061 76d5d3a3 Iustin Pop
    """
2062 76d5d3a3 Iustin Pop
    modified = False
2063 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
2064 76d5d3a3 Iustin Pop
      if item.uuid is None:
2065 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
2066 76d5d3a3 Iustin Pop
        modified = True
2067 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
2068 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
2069 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
2070 75cf411a Adeodato Simo
                                            members=[])
2071 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
2072 f9e81396 Guido Trotter
      modified = True
2073 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
2074 f936c153 Iustin Pop
      if not node.group:
2075 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
2076 190e3cb6 Guido Trotter
        modified = True
2077 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
2078 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
2079 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
2080 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
2081 f936c153 Iustin Pop
      self._UnlockedAddNodeToGroup(node.name, node.group)
2082 76d5d3a3 Iustin Pop
    if modified:
2083 76d5d3a3 Iustin Pop
      self._WriteConfig()
2084 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
2085 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
2086 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
2087 4fae38c5 Guido Trotter
2088 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
2089 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
2090 a8083063 Iustin Pop

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

2094 a8083063 Iustin Pop
    """
2095 a8083063 Iustin Pop
    if self._offline:
2096 a8083063 Iustin Pop
      return True
2097 a4eae71f Michael Hanselmann
2098 a8083063 Iustin Pop
    bad = False
2099 a8083063 Iustin Pop
2100 6a5b8b4b Iustin Pop
    node_list = []
2101 6a5b8b4b Iustin Pop
    addr_list = []
2102 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
2103 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
2104 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
2105 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
2106 6b294c53 Iustin Pop
    # in between
2107 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
2108 6a5b8b4b Iustin Pop
      if node_name == myhostname:
2109 6a5b8b4b Iustin Pop
        continue
2110 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
2111 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
2112 6a5b8b4b Iustin Pop
        continue
2113 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
2114 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
2115 6b294c53 Iustin Pop
2116 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
2117 415a7304 Michael Hanselmann
    result = \
2118 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
2119 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
2120 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
2121 1b54fc6c Guido Trotter
      if msg:
2122 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
2123 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
2124 1b54fc6c Guido Trotter
        logging.error(msg)
2125 a4eae71f Michael Hanselmann
2126 a4eae71f Michael Hanselmann
        if feedback_fn:
2127 a4eae71f Michael Hanselmann
          feedback_fn(msg)
2128 a4eae71f Michael Hanselmann
2129 a8083063 Iustin Pop
        bad = True
2130 a4eae71f Michael Hanselmann
2131 a8083063 Iustin Pop
    return not bad
2132 a8083063 Iustin Pop
2133 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
2134 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
2135 a8083063 Iustin Pop

2136 a8083063 Iustin Pop
    """
2137 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
2138 a4eae71f Michael Hanselmann
2139 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
2140 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
2141 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
2142 d2231b8c Iustin Pop
    # recovery to the user
2143 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
2144 4a89c54a Iustin Pop
    if config_errors:
2145 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
2146 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
2147 d2231b8c Iustin Pop
      logging.critical(errmsg)
2148 d2231b8c Iustin Pop
      if feedback_fn:
2149 d2231b8c Iustin Pop
        feedback_fn(errmsg)
2150 d2231b8c Iustin Pop
2151 a8083063 Iustin Pop
    if destination is None:
2152 a8083063 Iustin Pop
      destination = self._cfg_file
2153 a8083063 Iustin Pop
    self._BumpSerialNo()
2154 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
2155 13998ef2 Michael Hanselmann
2156 e60c73a1 René Nussbaumer
    getents = self._getents()
2157 bd407597 Iustin Pop
    try:
2158 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
2159 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
2160 bd407597 Iustin Pop
    except errors.LockError:
2161 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
2162 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
2163 bd407597 Iustin Pop
                                      " update")
2164 bd407597 Iustin Pop
    try:
2165 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
2166 bd407597 Iustin Pop
    finally:
2167 bd407597 Iustin Pop
      os.close(fd)
2168 13998ef2 Michael Hanselmann
2169 14e15659 Iustin Pop
    self.write_count += 1
2170 3d3a04bc Iustin Pop
2171 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
2172 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
2173 a8083063 Iustin Pop
2174 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
2175 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
2176 d9a855f1 Michael Hanselmann
      if not self._offline:
2177 b2acdbdc Michael Hanselmann
        result = self._GetRpc(None).call_write_ssconf_files(
2178 6819dc49 Iustin Pop
          self._UnlockedGetOnlineNodeList(),
2179 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
2180 a4eae71f Michael Hanselmann
2181 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
2182 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
2183 e1e75d00 Iustin Pop
          if msg:
2184 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
2185 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
2186 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
2187 a4eae71f Michael Hanselmann
2188 a4eae71f Michael Hanselmann
            if feedback_fn:
2189 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
2190 a4eae71f Michael Hanselmann
2191 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
2192 54d1a06e Michael Hanselmann
2193 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
2194 054596f0 Iustin Pop
    """Return the values needed by ssconf.
2195 054596f0 Iustin Pop

2196 054596f0 Iustin Pop
    @rtype: dict
2197 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2198 054596f0 Iustin Pop
        associated value
2199 054596f0 Iustin Pop

2200 054596f0 Iustin Pop
    """
2201 a3316e4a Iustin Pop
    fn = "\n".join
2202 81a49123 Iustin Pop
    instance_names = utils.NiceSort(self._UnlockedGetInstanceList())
2203 a3316e4a Iustin Pop
    node_names = utils.NiceSort(self._UnlockedGetNodeList())
2204 a3316e4a Iustin Pop
    node_info = [self._UnlockedGetNodeInfo(name) for name in node_names]
2205 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
2206 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
2207 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
2208 5909fb97 Luca Bigliardi
                    for ninfo in node_info]
2209 a3316e4a Iustin Pop
2210 81a49123 Iustin Pop
    instance_data = fn(instance_names)
2211 a3316e4a Iustin Pop
    off_data = fn(node.name for node in node_info if node.offline)
2212 81a49123 Iustin Pop
    on_data = fn(node.name for node in node_info if not node.offline)
2213 a3316e4a Iustin Pop
    mc_data = fn(node.name for node in node_info if node.master_candidate)
2214 8113a52e Luca Bigliardi
    mc_ips_data = fn(node.primary_ip for node in node_info
2215 8113a52e Luca Bigliardi
                     if node.master_candidate)
2216 a3316e4a Iustin Pop
    node_data = fn(node_names)
2217 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
2218 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
2219 f56618e0 Iustin Pop
2220 054596f0 Iustin Pop
    cluster = self._config_data.cluster
2221 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
2222 4f7a6a10 Iustin Pop
2223 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
2224 4f7a6a10 Iustin Pop
2225 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
2226 0fbae49a Balazs Lecz
2227 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
2228 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
2229 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
2230 6c0a75db Dimitris Aragiorgis
    networks = ["%s %s" % (net.uuid, net.name) for net in
2231 6c0a75db Dimitris Aragiorgis
                self._config_data.networks.values()]
2232 6c0a75db Dimitris Aragiorgis
    networks_data = fn(utils.NiceSort(networks))
2233 6f076453 Guido Trotter
2234 2afc9238 Iustin Pop
    ssconf_values = {
2235 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
2236 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
2237 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
2238 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
2239 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
2240 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
2241 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
2242 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
2243 5a8648eb Andrea Spadaccini
      constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
2244 054596f0 Iustin Pop
      constants.SS_MASTER_NODE: cluster.master_node,
2245 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
2246 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
2247 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
2248 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
2249 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
2250 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
2251 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
2252 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
2253 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
2254 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
2255 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
2256 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
2257 6c0a75db Dimitris Aragiorgis
      constants.SS_NETWORKS: networks_data,
2258 03d1dba2 Michael Hanselmann
      }
2259 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
2260 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
2261 2afc9238 Iustin Pop
    if bad_values:
2262 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
2263 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
2264 2afc9238 Iustin Pop
                                      " values: %s" % err)
2265 2afc9238 Iustin Pop
    return ssconf_values
2266 03d1dba2 Michael Hanselmann
2267 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2268 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
2269 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
2270 d367b66c Manuel Franceschini

2271 d367b66c Manuel Franceschini
    """
2272 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2273 d367b66c Manuel Franceschini
2274 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2275 a8083063 Iustin Pop
  def GetVGName(self):
2276 a8083063 Iustin Pop
    """Return the volume group name.
2277 a8083063 Iustin Pop

2278 a8083063 Iustin Pop
    """
2279 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2280 a8083063 Iustin Pop
2281 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2282 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2283 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2284 89ff8e15 Manuel Franceschini

2285 89ff8e15 Manuel Franceschini
    """
2286 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2287 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2288 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2289 89ff8e15 Manuel Franceschini
2290 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2291 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2292 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2293 9e33896b Luca Bigliardi

2294 9e33896b Luca Bigliardi
    """
2295 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2296 9e33896b Luca Bigliardi
2297 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2298 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2299 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2300 9e33896b Luca Bigliardi

2301 9e33896b Luca Bigliardi
    """
2302 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2303 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2304 9e33896b Luca Bigliardi
    self._WriteConfig()
2305 9e33896b Luca Bigliardi
2306 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2307 a8083063 Iustin Pop
  def GetMACPrefix(self):
2308 a8083063 Iustin Pop
    """Return the mac prefix.
2309 a8083063 Iustin Pop

2310 a8083063 Iustin Pop
    """
2311 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2312 62779dd0 Iustin Pop
2313 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2314 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2315 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2316 62779dd0 Iustin Pop

2317 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2318 c41eea6e Iustin Pop
    @return: the cluster object
2319 62779dd0 Iustin Pop

2320 62779dd0 Iustin Pop
    """
2321 62779dd0 Iustin Pop
    return self._config_data.cluster
2322 e00fb268 Iustin Pop
2323 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2324 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2325 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2326 51cb1581 Luca Bigliardi

2327 51cb1581 Luca Bigliardi
    """
2328 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2329 51cb1581 Luca Bigliardi
2330 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2331 d1547283 Dimitris Aragiorgis
  def Update(self, target, feedback_fn, ec_id=None):
2332 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2333 e00fb268 Iustin Pop

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

2340 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2341 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2342 c41eea6e Iustin Pop
        the cluster
2343 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2344 c41eea6e Iustin Pop

2345 e00fb268 Iustin Pop
    """
2346 e00fb268 Iustin Pop
    if self._config_data is None:
2347 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2348 3ecf6786 Iustin Pop
                                   " cannot save.")
2349 f34901f8 Iustin Pop
    update_serial = False
2350 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2351 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2352 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2353 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2354 f34901f8 Iustin Pop
      update_serial = True
2355 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2356 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2357 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2358 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2359 1e0d3321 Dimitris Aragiorgis
    elif isinstance(target, objects.Network):
2360 1e0d3321 Dimitris Aragiorgis
      test = target in self._config_data.networks.values()
2361 e00fb268 Iustin Pop
    else:
2362 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2363 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2364 e00fb268 Iustin Pop
    if not test:
2365 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2366 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2367 f34901f8 Iustin Pop
    target.serial_no += 1
2368 d693c864 Iustin Pop
    target.mtime = now = time.time()
2369 f34901f8 Iustin Pop
2370 cff4c037 Iustin Pop
    if update_serial:
2371 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2372 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2373 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2374 b989e85d Iustin Pop
2375 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2376 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
2377 61cf6b5e Iustin Pop
2378 d1547283 Dimitris Aragiorgis
    if ec_id is not None:
2379 d1547283 Dimitris Aragiorgis
      # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams
2380 d1547283 Dimitris Aragiorgis
      self._UnlockedCommitTemporaryIps(ec_id)
2381 d1547283 Dimitris Aragiorgis
2382 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2383 73064714 Guido Trotter
2384 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2385 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2386 73064714 Guido Trotter
    """Drop per-execution-context reservations
2387 73064714 Guido Trotter

2388 73064714 Guido Trotter
    """
2389 d8aee57e Iustin Pop
    for rm in self._all_rms:
2390 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)
2391 6c0a75db Dimitris Aragiorgis
2392 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2393 6c0a75db Dimitris Aragiorgis
  def GetAllNetworksInfo(self):
2394 6a94d553 Dimitris Aragiorgis
    """Get configuration info of all the networks.
2395 6c0a75db Dimitris Aragiorgis

2396 6c0a75db Dimitris Aragiorgis
    """
2397 6c0a75db Dimitris Aragiorgis
    return dict(self._config_data.networks)
2398 6c0a75db Dimitris Aragiorgis
2399 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetworkList(self):
2400 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2401 6c0a75db Dimitris Aragiorgis

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

2404 6c0a75db Dimitris Aragiorgis
    """
2405 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks.keys()
2406 6c0a75db Dimitris Aragiorgis
2407 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2408 6c0a75db Dimitris Aragiorgis
  def GetNetworkList(self):
2409 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2410 6c0a75db Dimitris Aragiorgis

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

2413 6c0a75db Dimitris Aragiorgis
    """
2414 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetworkList()
2415 6c0a75db Dimitris Aragiorgis
2416 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2417 6c0a75db Dimitris Aragiorgis
  def GetNetworkNames(self):
2418 6c0a75db Dimitris Aragiorgis
    """Get a list of network names
2419 6c0a75db Dimitris Aragiorgis

2420 6c0a75db Dimitris Aragiorgis
    """
2421 beb81ea5 Dimitris Aragiorgis
    names = [net.name
2422 beb81ea5 Dimitris Aragiorgis
             for net in self._config_data.networks.values()]
2423 6c0a75db Dimitris Aragiorgis
    return names
2424 6c0a75db Dimitris Aragiorgis
2425 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetwork(self, uuid):
2426 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2427 6c0a75db Dimitris Aragiorgis

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

2430 6c0a75db Dimitris Aragiorgis
    """
2431 6c0a75db Dimitris Aragiorgis
    if uuid not in self._config_data.networks:
2432 6c0a75db Dimitris Aragiorgis
      return None
2433 6c0a75db Dimitris Aragiorgis
2434 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks[uuid]
2435 6c0a75db Dimitris Aragiorgis
2436 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2437 6c0a75db Dimitris Aragiorgis
  def GetNetwork(self, uuid):
2438 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2439 6c0a75db Dimitris Aragiorgis

2440 6c0a75db Dimitris Aragiorgis
    It takes the information from the configuration file.
2441 6c0a75db Dimitris Aragiorgis

2442 6c0a75db Dimitris Aragiorgis
    @param uuid: UUID of the network
2443 6c0a75db Dimitris Aragiorgis

2444 6c0a75db Dimitris Aragiorgis
    @rtype: L{objects.Network}
2445 6c0a75db Dimitris Aragiorgis
    @return: the network object
2446 6c0a75db Dimitris Aragiorgis

2447 6c0a75db Dimitris Aragiorgis
    """
2448 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetwork(uuid)
2449 6c0a75db Dimitris Aragiorgis
2450 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2451 6c0a75db Dimitris Aragiorgis
  def AddNetwork(self, net, ec_id, check_uuid=True):
2452 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2453 6c0a75db Dimitris Aragiorgis

2454 6c0a75db Dimitris Aragiorgis
    @type net: L{objects.Network}
2455 6c0a75db Dimitris Aragiorgis
    @param net: the Network object to add
2456 6c0a75db Dimitris Aragiorgis
    @type ec_id: string
2457 6c0a75db Dimitris Aragiorgis
    @param ec_id: unique id for the job to use when creating a missing UUID
2458 6c0a75db Dimitris Aragiorgis

2459 6c0a75db Dimitris Aragiorgis
    """
2460 6c0a75db Dimitris Aragiorgis
    self._UnlockedAddNetwork(net, ec_id, check_uuid)
2461 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2462 6c0a75db Dimitris Aragiorgis
2463 6c0a75db Dimitris Aragiorgis
  def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
2464 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2465 6c0a75db Dimitris Aragiorgis

2466 6c0a75db Dimitris Aragiorgis
    """
2467 6c0a75db Dimitris Aragiorgis
    logging.info("Adding network %s to configuration", net.name)
2468 6c0a75db Dimitris Aragiorgis
2469 6c0a75db Dimitris Aragiorgis
    if check_uuid:
2470 6c0a75db Dimitris Aragiorgis
      self._EnsureUUID(net, ec_id)
2471 6c0a75db Dimitris Aragiorgis
2472 6c0a75db Dimitris Aragiorgis
    existing_uuid = self._UnlockedLookupNetwork(net.name)
2473 6c0a75db Dimitris Aragiorgis
    if existing_uuid:
2474 6c0a75db Dimitris Aragiorgis
      raise errors.OpPrereqError("Desired network name '%s' already"
2475 6c0a75db Dimitris Aragiorgis
                                 " exists as a network (UUID: %s)" %
2476 6c0a75db Dimitris Aragiorgis
                                 (net.name, existing_uuid),
2477 6c0a75db Dimitris Aragiorgis
                                 errors.ECODE_EXISTS)
2478 6c0a75db Dimitris Aragiorgis
    net.serial_no = 1
2479 6c0a75db Dimitris Aragiorgis
    self._config_data.networks[net.uuid] = net
2480 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2481 6c0a75db Dimitris Aragiorgis
2482 6c0a75db Dimitris Aragiorgis
  def _UnlockedLookupNetwork(self, target):
2483 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2484 6c0a75db Dimitris Aragiorgis

2485 6c0a75db Dimitris Aragiorgis
    @type target: string
2486 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2487 6c0a75db Dimitris Aragiorgis
    @rtype: string
2488 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2489 6c0a75db Dimitris Aragiorgis
    @raises errors.OpPrereqError: when the target network cannot be found
2490 6c0a75db Dimitris Aragiorgis

2491 6c0a75db Dimitris Aragiorgis
    """
2492 6c0a75db Dimitris Aragiorgis
    if target in self._config_data.networks:
2493 6c0a75db Dimitris Aragiorgis
      return target
2494 6c0a75db Dimitris Aragiorgis
    for net in self._config_data.networks.values():
2495 6c0a75db Dimitris Aragiorgis
      if net.name == target:
2496 6c0a75db Dimitris Aragiorgis
        return net.uuid
2497 6c0a75db Dimitris Aragiorgis
    return None
2498 6c0a75db Dimitris Aragiorgis
2499 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2500 6c0a75db Dimitris Aragiorgis
  def LookupNetwork(self, target):
2501 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2502 6c0a75db Dimitris Aragiorgis

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

2505 6c0a75db Dimitris Aragiorgis
    @type target: string
2506 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2507 6c0a75db Dimitris Aragiorgis
    @rtype: string
2508 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2509 6c0a75db Dimitris Aragiorgis

2510 6c0a75db Dimitris Aragiorgis
    """
2511 6c0a75db Dimitris Aragiorgis
    return self._UnlockedLookupNetwork(target)
2512 6c0a75db Dimitris Aragiorgis
2513 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2514 6c0a75db Dimitris Aragiorgis
  def RemoveNetwork(self, network_uuid):
2515 6c0a75db Dimitris Aragiorgis
    """Remove a network from the configuration.
2516 6c0a75db Dimitris Aragiorgis

2517 6c0a75db Dimitris Aragiorgis
    @type network_uuid: string
2518 6c0a75db Dimitris Aragiorgis
    @param network_uuid: the UUID of the network to remove
2519 6c0a75db Dimitris Aragiorgis

2520 6c0a75db Dimitris Aragiorgis
    """
2521 6c0a75db Dimitris Aragiorgis
    logging.info("Removing network %s from configuration", network_uuid)
2522 6c0a75db Dimitris Aragiorgis
2523 6c0a75db Dimitris Aragiorgis
    if network_uuid not in self._config_data.networks:
2524 6c0a75db Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
2525 6c0a75db Dimitris Aragiorgis
2526 6c0a75db Dimitris Aragiorgis
    del self._config_data.networks[network_uuid]
2527 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2528 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2529 ad4a9ae7 Dimitris Aragiorgis
2530 ad4a9ae7 Dimitris Aragiorgis
  def _UnlockedGetGroupNetParams(self, net, node):
2531 ad4a9ae7 Dimitris Aragiorgis
    """Get the netparams (mode, link) of a network.
2532 ad4a9ae7 Dimitris Aragiorgis

2533 ad4a9ae7 Dimitris Aragiorgis
    Get a network's netparams for a given node.
2534 ad4a9ae7 Dimitris Aragiorgis

2535 ad4a9ae7 Dimitris Aragiorgis
    @type net: string
2536 ad4a9ae7 Dimitris Aragiorgis
    @param net: network name
2537 ad4a9ae7 Dimitris Aragiorgis
    @type node: string
2538 ad4a9ae7 Dimitris Aragiorgis
    @param node: node name
2539 ad4a9ae7 Dimitris Aragiorgis
    @rtype: dict or None
2540 ad4a9ae7 Dimitris Aragiorgis
    @return: netparams
2541 ad4a9ae7 Dimitris Aragiorgis

2542 ad4a9ae7 Dimitris Aragiorgis
    """
2543 ad4a9ae7 Dimitris Aragiorgis
    net_uuid = self._UnlockedLookupNetwork(net)
2544 ad4a9ae7 Dimitris Aragiorgis
    if net_uuid is None:
2545 ad4a9ae7 Dimitris Aragiorgis
      return None
2546 ad4a9ae7 Dimitris Aragiorgis
2547 ad4a9ae7 Dimitris Aragiorgis
    node_info = self._UnlockedGetNodeInfo(node)
2548 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2549 ad4a9ae7 Dimitris Aragiorgis
    netparams = nodegroup_info.networks.get(net_uuid, None)
2550 ad4a9ae7 Dimitris Aragiorgis
2551 ad4a9ae7 Dimitris Aragiorgis
    return netparams
2552 ad4a9ae7 Dimitris Aragiorgis
2553 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2554 ad4a9ae7 Dimitris Aragiorgis
  def GetGroupNetParams(self, net, node):
2555 ad4a9ae7 Dimitris Aragiorgis
    """Locking wrapper of _UnlockedGetGroupNetParams()
2556 ad4a9ae7 Dimitris Aragiorgis

2557 ad4a9ae7 Dimitris Aragiorgis
    """
2558 ad4a9ae7 Dimitris Aragiorgis
    return self._UnlockedGetGroupNetParams(net, node)
2559 ad4a9ae7 Dimitris Aragiorgis
2560 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2561 ad4a9ae7 Dimitris Aragiorgis
  def CheckIPInNodeGroup(self, ip, node):
2562 6a94d553 Dimitris Aragiorgis
    """Check IP uniqueness in nodegroup.
2563 6a94d553 Dimitris Aragiorgis

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

2568 ad4a9ae7 Dimitris Aragiorgis
    @type ip: string
2569 ad4a9ae7 Dimitris Aragiorgis
    @param ip: ip address
2570 ad4a9ae7 Dimitris Aragiorgis
    @type node: string
2571 ad4a9ae7 Dimitris Aragiorgis
    @param node: node name
2572 ad4a9ae7 Dimitris Aragiorgis
    @rtype: (string, dict) or (None, None)
2573 ad4a9ae7 Dimitris Aragiorgis
    @return: (network name, netparams)
2574 ad4a9ae7 Dimitris Aragiorgis

2575 ad4a9ae7 Dimitris Aragiorgis
    """
2576 ad4a9ae7 Dimitris Aragiorgis
    if ip is None:
2577 ad4a9ae7 Dimitris Aragiorgis
      return (None, None)
2578 ad4a9ae7 Dimitris Aragiorgis
    node_info = self._UnlockedGetNodeInfo(node)
2579 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2580 ad4a9ae7 Dimitris Aragiorgis
    for net_uuid in nodegroup_info.networks.keys():
2581 ad4a9ae7 Dimitris Aragiorgis
      net_info = self._UnlockedGetNetwork(net_uuid)
2582 ad4a9ae7 Dimitris Aragiorgis
      pool = network.AddressPool(net_info)
2583 beb81ea5 Dimitris Aragiorgis
      if pool.Contains(ip):
2584 ad4a9ae7 Dimitris Aragiorgis
        return (net_info.name, nodegroup_info.networks[net_uuid])
2585 ad4a9ae7 Dimitris Aragiorgis
2586 ad4a9ae7 Dimitris Aragiorgis
    return (None, None)