Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 326d8273

History | View | Annotate | Download (84.5 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 f2837050 Dimitris Aragiorgis
from ganeti import network
54 243cdbcc Michael Hanselmann
55 243cdbcc Michael Hanselmann
56 7f93570a Iustin Pop
_config_lock = locking.SharedLock("ConfigWriter")
57 f78ede4e Guido Trotter
58 4fae38c5 Guido Trotter
# job id used for resource management at config upgrade time
59 8d9c3bef Michael Hanselmann
_UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
60 4fae38c5 Guido Trotter
61 f78ede4e Guido Trotter
62 5b263ed7 Michael Hanselmann
def _ValidateConfig(data):
63 c41eea6e Iustin Pop
  """Verifies that a configuration objects looks valid.
64 c41eea6e Iustin Pop

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

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

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

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

81 013da361 Guido Trotter
  """
82 013da361 Guido Trotter
  def __init__(self):
83 013da361 Guido Trotter
    self._ec_reserved = {}
84 013da361 Guido Trotter
85 013da361 Guido Trotter
  def Reserved(self, resource):
86 a7359d91 David Knowles
    for holder_reserved in self._ec_reserved.values():
87 013da361 Guido Trotter
      if resource in holder_reserved:
88 013da361 Guido Trotter
        return True
89 013da361 Guido Trotter
    return False
90 013da361 Guido Trotter
91 013da361 Guido Trotter
  def Reserve(self, ec_id, resource):
92 013da361 Guido Trotter
    if self.Reserved(resource):
93 28a7318f Iustin Pop
      raise errors.ReservationError("Duplicate reservation for resource '%s'"
94 28a7318f Iustin Pop
                                    % str(resource))
95 013da361 Guido Trotter
    if ec_id not in self._ec_reserved:
96 013da361 Guido Trotter
      self._ec_reserved[ec_id] = set([resource])
97 013da361 Guido Trotter
    else:
98 013da361 Guido Trotter
      self._ec_reserved[ec_id].add(resource)
99 013da361 Guido Trotter
100 013da361 Guido Trotter
  def DropECReservations(self, ec_id):
101 013da361 Guido Trotter
    if ec_id in self._ec_reserved:
102 013da361 Guido Trotter
      del self._ec_reserved[ec_id]
103 013da361 Guido Trotter
104 013da361 Guido Trotter
  def GetReserved(self):
105 013da361 Guido Trotter
    all_reserved = set()
106 013da361 Guido Trotter
    for holder_reserved in self._ec_reserved.values():
107 013da361 Guido Trotter
      all_reserved.update(holder_reserved)
108 013da361 Guido Trotter
    return all_reserved
109 013da361 Guido Trotter
110 0ca10882 Dimitris Aragiorgis
  def GetECReserved(self, ec_id):
111 0ca10882 Dimitris Aragiorgis
    ec_reserved = set()
112 0ca10882 Dimitris Aragiorgis
    if ec_id in self._ec_reserved:
113 0ca10882 Dimitris Aragiorgis
      ec_reserved.update(self._ec_reserved[ec_id])
114 0ca10882 Dimitris Aragiorgis
    return ec_reserved
115 0ca10882 Dimitris Aragiorgis
116 0ca10882 Dimitris Aragiorgis
117 013da361 Guido Trotter
  def Generate(self, existing, generate_one_fn, ec_id):
118 013da361 Guido Trotter
    """Generate a new resource of this type
119 013da361 Guido Trotter

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

140 fe698b38 Michael Hanselmann
  """
141 fe698b38 Michael Hanselmann
  return utils.MatchNameComponent(short_name, names, case_sensitive=False)
142 fe698b38 Michael Hanselmann
143 fe698b38 Michael Hanselmann
144 82c54b5b Michael Hanselmann
def _CheckInstanceDiskIvNames(disks):
145 82c54b5b Michael Hanselmann
  """Checks if instance's disks' C{iv_name} attributes are in order.
146 82c54b5b Michael Hanselmann

147 82c54b5b Michael Hanselmann
  @type disks: list of L{objects.Disk}
148 82c54b5b Michael Hanselmann
  @param disks: List of disks
149 82c54b5b Michael Hanselmann
  @rtype: list of tuples; (int, string, string)
150 82c54b5b Michael Hanselmann
  @return: List of wrongly named disks, each tuple contains disk index,
151 82c54b5b Michael Hanselmann
    expected and actual name
152 82c54b5b Michael Hanselmann

153 82c54b5b Michael Hanselmann
  """
154 82c54b5b Michael Hanselmann
  result = []
155 82c54b5b Michael Hanselmann
156 82c54b5b Michael Hanselmann
  for (idx, disk) in enumerate(disks):
157 82c54b5b Michael Hanselmann
    exp_iv_name = "disk/%s" % idx
158 82c54b5b Michael Hanselmann
    if disk.iv_name != exp_iv_name:
159 82c54b5b Michael Hanselmann
      result.append((idx, exp_iv_name, disk.iv_name))
160 82c54b5b Michael Hanselmann
161 82c54b5b Michael Hanselmann
  return result
162 82c54b5b Michael Hanselmann
163 82c54b5b Michael Hanselmann
164 a8083063 Iustin Pop
class ConfigWriter:
165 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
166 a8083063 Iustin Pop

167 d8aee57e Iustin Pop
  @ivar _temporary_lvs: reservation manager for temporary LVs
168 d8aee57e Iustin Pop
  @ivar _all_rms: a list of all temporary reservation managers
169 d8aee57e Iustin Pop

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

204 b2acdbdc Michael Hanselmann
    """
205 b2acdbdc Michael Hanselmann
    return rpc.ConfigRunner(self._context, address_list)
206 b2acdbdc Michael Hanselmann
207 b2acdbdc Michael Hanselmann
  def SetContext(self, context):
208 b2acdbdc Michael Hanselmann
    """Sets Ganeti context.
209 b2acdbdc Michael Hanselmann

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

218 a8083063 Iustin Pop
    """
219 a8083063 Iustin Pop
    return os.path.exists(constants.CLUSTER_CONF_FILE)
220 a8083063 Iustin Pop
221 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
222 5768e6a6 René Nussbaumer
  def GetNdParams(self, node):
223 5768e6a6 René Nussbaumer
    """Get the node params populated with cluster defaults.
224 5768e6a6 René Nussbaumer

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

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

237 8a147bba René Nussbaumer
    @type instance: L{objects.Instance}
238 8a147bba René Nussbaumer
    @param instance: The instance we want to know the params for
239 8a147bba René Nussbaumer
    @return: A dict with the filled in disk params
240 8a147bba René Nussbaumer

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

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

254 99ccf8b9 René Nussbaumer
    """
255 99ccf8b9 René Nussbaumer
    return self._UnlockedGetGroupDiskParams(group)
256 99ccf8b9 René Nussbaumer
257 99ccf8b9 René Nussbaumer
  def _UnlockedGetGroupDiskParams(self, group):
258 99ccf8b9 René Nussbaumer
    """Get the disk params populated with inherit chain down to node-group.
259 99ccf8b9 René Nussbaumer

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

264 99ccf8b9 René Nussbaumer
    """
265 99ccf8b9 René Nussbaumer
    return self._config_data.cluster.SimpleFillDP(group.diskparams)
266 8a147bba René Nussbaumer
267 aa16d5a8 Dimitris Aragiorgis
  def _UnlockedGetNetworkMACPrefix(self, net):
268 aa16d5a8 Dimitris Aragiorgis
    """Return the network mac prefix if it exists or the cluster level default.
269 aa16d5a8 Dimitris Aragiorgis

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

286 aa16d5a8 Dimitris Aragiorgis
    """
287 aa16d5a8 Dimitris Aragiorgis
    if not prefix:
288 aa16d5a8 Dimitris Aragiorgis
      prefix = self._config_data.cluster.mac_prefix
289 aa16d5a8 Dimitris Aragiorgis
    def GenMac():
290 aa16d5a8 Dimitris Aragiorgis
      byte1 = random.randrange(0, 256)
291 aa16d5a8 Dimitris Aragiorgis
      byte2 = random.randrange(0, 256)
292 aa16d5a8 Dimitris Aragiorgis
      byte3 = random.randrange(0, 256)
293 aa16d5a8 Dimitris Aragiorgis
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
294 aa16d5a8 Dimitris Aragiorgis
      return mac
295 aa16d5a8 Dimitris Aragiorgis
    return GenMac
296 aa16d5a8 Dimitris Aragiorgis
297 8a147bba René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
298 24ea7a54 Dimitris Aragiorgis
  def GenerateMAC(self, net, ec_id):
299 a8083063 Iustin Pop
    """Generate a MAC for an instance.
300 a8083063 Iustin Pop

301 a8083063 Iustin Pop
    This should check the current instances for duplicates.
302 a8083063 Iustin Pop

303 a8083063 Iustin Pop
    """
304 36b66e6e Guido Trotter
    existing = self._AllMACs()
305 aa16d5a8 Dimitris Aragiorgis
    prefix = self._UnlockedGetNetworkMACPrefix(net)
306 aa16d5a8 Dimitris Aragiorgis
    gen_mac = self._GenerateOneMAC(prefix)
307 24ea7a54 Dimitris Aragiorgis
    return self._temporary_ids.Generate(existing, gen_mac, ec_id)
308 a8083063 Iustin Pop
309 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
310 36b66e6e Guido Trotter
  def ReserveMAC(self, mac, ec_id):
311 36b66e6e Guido Trotter
    """Reserve a MAC for an instance.
312 1862d460 Alexander Schreiber

313 1862d460 Alexander Schreiber
    This only checks instances managed by this cluster, it does not
314 1862d460 Alexander Schreiber
    check for potential collisions elsewhere.
315 1862d460 Alexander Schreiber

316 1862d460 Alexander Schreiber
    """
317 1862d460 Alexander Schreiber
    all_macs = self._AllMACs()
318 36b66e6e Guido Trotter
    if mac in all_macs:
319 36b66e6e Guido Trotter
      raise errors.ReservationError("mac already in use")
320 36b66e6e Guido Trotter
    else:
321 8785b71b Apollon Oikonomopoulos
      self._temporary_macs.Reserve(ec_id, mac)
322 1862d460 Alexander Schreiber
323 0ca10882 Dimitris Aragiorgis
  def _UnlockedCommitTemporaryIps(self, ec_id):
324 0ca10882 Dimitris Aragiorgis
    """Commit all reserved IP address to their respective pools
325 0ca10882 Dimitris Aragiorgis

326 0ca10882 Dimitris Aragiorgis
    """
327 0ca10882 Dimitris Aragiorgis
    for action, address, net_uuid in self._temporary_ips.GetECReserved(ec_id):
328 0ca10882 Dimitris Aragiorgis
      self._UnlockedCommitIp(action, net_uuid, address)
329 0ca10882 Dimitris Aragiorgis
330 0ca10882 Dimitris Aragiorgis
  def _UnlockedCommitIp(self, action, net_uuid, address):
331 0ca10882 Dimitris Aragiorgis
    """Commit a reserved IP address to an IP pool.
332 0ca10882 Dimitris Aragiorgis

333 0ca10882 Dimitris Aragiorgis
    The IP address is taken from the network's IP pool and marked as reserved.
334 0ca10882 Dimitris Aragiorgis

335 0ca10882 Dimitris Aragiorgis
    """
336 0ca10882 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
337 0ca10882 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
338 e3fd65a4 Dimitris Aragiorgis
    if action == constants.RESERVE_ACTION:
339 0ca10882 Dimitris Aragiorgis
      pool.Reserve(address)
340 e3fd65a4 Dimitris Aragiorgis
    elif action == constants.RELEASE_ACTION:
341 0ca10882 Dimitris Aragiorgis
      pool.Release(address)
342 0ca10882 Dimitris Aragiorgis
343 0ca10882 Dimitris Aragiorgis
  def _UnlockedReleaseIp(self, net_uuid, address, ec_id):
344 0ca10882 Dimitris Aragiorgis
    """Give a specific IP address back to an IP pool.
345 0ca10882 Dimitris Aragiorgis

346 0ca10882 Dimitris Aragiorgis
    The IP address is returned to the IP pool designated by pool_id and marked
347 0ca10882 Dimitris Aragiorgis
    as reserved.
348 0ca10882 Dimitris Aragiorgis

349 0ca10882 Dimitris Aragiorgis
    """
350 e3fd65a4 Dimitris Aragiorgis
    self._temporary_ips.Reserve(ec_id,
351 e3fd65a4 Dimitris Aragiorgis
                                (constants.RELEASE_ACTION, address, net_uuid))
352 0ca10882 Dimitris Aragiorgis
353 0ca10882 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
354 0ca10882 Dimitris Aragiorgis
  def ReleaseIp(self, network, address, ec_id):
355 0ca10882 Dimitris Aragiorgis
    """Give a specified IP address back to an IP pool.
356 0ca10882 Dimitris Aragiorgis

357 0ca10882 Dimitris Aragiorgis
    This is just a wrapper around _UnlockedReleaseIp.
358 0ca10882 Dimitris Aragiorgis

359 0ca10882 Dimitris Aragiorgis
    """
360 0ca10882 Dimitris Aragiorgis
    net_uuid = self._UnlockedLookupNetwork(network)
361 0ca10882 Dimitris Aragiorgis
    if net_uuid:
362 0ca10882 Dimitris Aragiorgis
      self._UnlockedReleaseIp(net_uuid, address, ec_id)
363 0ca10882 Dimitris Aragiorgis
364 0ca10882 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
365 0ca10882 Dimitris Aragiorgis
  def GenerateIp(self, net, ec_id):
366 0ca10882 Dimitris Aragiorgis
    """Find a free IPv4 address for an instance.
367 0ca10882 Dimitris Aragiorgis

368 0ca10882 Dimitris Aragiorgis
    """
369 0ca10882 Dimitris Aragiorgis
    net_uuid = self._UnlockedLookupNetwork(net)
370 0ca10882 Dimitris Aragiorgis
    nobj = self._UnlockedGetNetwork(net_uuid)
371 0ca10882 Dimitris Aragiorgis
    pool = network.AddressPool(nobj)
372 0ca10882 Dimitris Aragiorgis
373 0ca10882 Dimitris Aragiorgis
    def gen_one():
374 0ca10882 Dimitris Aragiorgis
      try:
375 326d8273 Dimitris Aragiorgis
        ip = pool.GenerateFree()
376 326d8273 Dimitris Aragiorgis
      except errors.AddressPoolError:
377 0ca10882 Dimitris Aragiorgis
        raise errors.ReservationError("Cannot generate IP. Network is full")
378 e3fd65a4 Dimitris Aragiorgis
      return (constants.RESERVE_ACTION, ip, net_uuid)
379 0ca10882 Dimitris Aragiorgis
380 0ca10882 Dimitris Aragiorgis
    _ ,address, _ = self._temporary_ips.Generate([], gen_one, ec_id)
381 0ca10882 Dimitris Aragiorgis
    return address
382 0ca10882 Dimitris Aragiorgis
383 0ca10882 Dimitris Aragiorgis
  def _UnlockedReserveIp(self, net_uuid, address, ec_id):
384 0ca10882 Dimitris Aragiorgis
    """Reserve a given IPv4 address for use by an instance.
385 0ca10882 Dimitris Aragiorgis

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

405 0ca10882 Dimitris Aragiorgis
    """
406 0ca10882 Dimitris Aragiorgis
    net_uuid = self._UnlockedLookupNetwork(net)
407 0ca10882 Dimitris Aragiorgis
    if net_uuid:
408 0ca10882 Dimitris Aragiorgis
      return self._UnlockedReserveIp(net_uuid, address, ec_id)
409 0ca10882 Dimitris Aragiorgis
410 f9518d38 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
411 d8aee57e Iustin Pop
  def ReserveLV(self, lv_name, ec_id):
412 d8aee57e Iustin Pop
    """Reserve an VG/LV pair for an instance.
413 d8aee57e Iustin Pop

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

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

428 f9518d38 Iustin Pop
    This checks the current disks for duplicates.
429 f9518d38 Iustin Pop

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

438 923b1523 Iustin Pop
    """
439 923b1523 Iustin Pop
    lvnames = set()
440 923b1523 Iustin Pop
    for instance in self._config_data.instances.values():
441 923b1523 Iustin Pop
      node_data = instance.MapLVsByNode()
442 923b1523 Iustin Pop
      for lv_list in node_data.values():
443 923b1523 Iustin Pop
        lvnames.update(lv_list)
444 923b1523 Iustin Pop
    return lvnames
445 923b1523 Iustin Pop
446 34e54ebc Iustin Pop
  def _AllIDs(self, include_temporary):
447 34e54ebc Iustin Pop
    """Compute the list of all UUIDs and names we have.
448 34e54ebc Iustin Pop

449 34e54ebc Iustin Pop
    @type include_temporary: boolean
450 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
451 34e54ebc Iustin Pop
    @rtype: set
452 34e54ebc Iustin Pop
    @return: a set of IDs
453 34e54ebc Iustin Pop

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

467 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
468 923b1523 Iustin Pop
    duplicates.
469 923b1523 Iustin Pop

470 c41eea6e Iustin Pop
    @rtype: string
471 c41eea6e Iustin Pop
    @return: the unique id
472 923b1523 Iustin Pop

473 923b1523 Iustin Pop
    """
474 4fae38c5 Guido Trotter
    existing = self._AllIDs(include_temporary=False)
475 4fae38c5 Guido Trotter
    return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
476 923b1523 Iustin Pop
477 430b923c Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
478 4fae38c5 Guido Trotter
  def GenerateUniqueID(self, ec_id):
479 430b923c Iustin Pop
    """Generate an unique ID.
480 430b923c Iustin Pop

481 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
482 430b923c Iustin Pop

483 4fae38c5 Guido Trotter
    @type ec_id: string
484 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
485 34d657ba Iustin Pop

486 34d657ba Iustin Pop
    """
487 4fae38c5 Guido Trotter
    return self._GenerateUniqueID(ec_id)
488 34d657ba Iustin Pop
489 a8083063 Iustin Pop
  def _AllMACs(self):
490 a8083063 Iustin Pop
    """Return all MACs present in the config.
491 a8083063 Iustin Pop

492 c41eea6e Iustin Pop
    @rtype: list
493 c41eea6e Iustin Pop
    @return: the list of all MACs
494 c41eea6e Iustin Pop

495 a8083063 Iustin Pop
    """
496 a8083063 Iustin Pop
    result = []
497 a8083063 Iustin Pop
    for instance in self._config_data.instances.values():
498 a8083063 Iustin Pop
      for nic in instance.nics:
499 a8083063 Iustin Pop
        result.append(nic.mac)
500 a8083063 Iustin Pop
501 a8083063 Iustin Pop
    return result
502 a8083063 Iustin Pop
503 f9518d38 Iustin Pop
  def _AllDRBDSecrets(self):
504 f9518d38 Iustin Pop
    """Return all DRBD secrets present in the config.
505 f9518d38 Iustin Pop

506 c41eea6e Iustin Pop
    @rtype: list
507 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
508 c41eea6e Iustin Pop

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

528 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
529 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
530 4b98ac29 Iustin Pop
    @type l_ids: list
531 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
532 4b98ac29 Iustin Pop
    @type p_ids: list
533 4b98ac29 Iustin Pop
    @param p_ids: list of current physical ids
534 4b98ac29 Iustin Pop
    @rtype: list
535 4b98ac29 Iustin Pop
    @return: a list of error messages
536 4b98ac29 Iustin Pop

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

558 4a89c54a Iustin Pop
    @rtype: list
559 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
560 4a89c54a Iustin Pop
        configuration errors
561 4a89c54a Iustin Pop

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

815 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
816 4a89c54a Iustin Pop

817 4a89c54a Iustin Pop
    @rtype: list
818 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
819 4a89c54a Iustin Pop
        configuration errors
820 4a89c54a Iustin Pop

821 4a89c54a Iustin Pop
    """
822 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
823 4a89c54a Iustin Pop
824 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
825 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
826 a8083063 Iustin Pop

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

829 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
830 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
831 a8083063 Iustin Pop
    node.
832 a8083063 Iustin Pop

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

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

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

868 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
869 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
870 f78ede4e Guido Trotter
    node.
871 f78ede4e Guido Trotter

872 f78ede4e Guido Trotter
    """
873 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
874 f78ede4e Guido Trotter
875 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
876 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
877 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
878 b2fddf63 Iustin Pop

879 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
880 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
881 3b3b1bca Dimitris Aragiorgis
        configuration is stable
882 3b3b1bca Dimitris Aragiorgis

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

893 264bb3c5 Michael Hanselmann
    """
894 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
895 264bb3c5 Michael Hanselmann
896 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
897 a8083063 Iustin Pop
  def AllocatePort(self):
898 a8083063 Iustin Pop
    """Allocate a port.
899 a8083063 Iustin Pop

900 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
901 b2fddf63 Iustin Pop
    default port range (and in this case we increase
902 b2fddf63 Iustin Pop
    highest_used_port).
903 a8083063 Iustin Pop

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

922 4a89c54a Iustin Pop
    @rtype: (dict, list)
923 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
924 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
925 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
926 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
927 4a89c54a Iustin Pop
        should raise an exception
928 a81c53c9 Iustin Pop

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

962 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
963 6d2e83d5 Iustin Pop

964 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
965 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
966 6d2e83d5 Iustin Pop
        an empty list).
967 6d2e83d5 Iustin Pop

968 6d2e83d5 Iustin Pop
    """
969 4a89c54a Iustin Pop
    d_map, duplicates = self._UnlockedComputeDRBDMap()
970 4a89c54a Iustin Pop
    if duplicates:
971 4a89c54a Iustin Pop
      raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
972 4a89c54a Iustin Pop
                                      str(duplicates))
973 4a89c54a Iustin Pop
    return d_map
974 6d2e83d5 Iustin Pop
975 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
976 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
977 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
978 a81c53c9 Iustin Pop

979 a81c53c9 Iustin Pop
    The free minor will be automatically computed from the existing
980 a81c53c9 Iustin Pop
    devices. A node can be given multiple times in order to allocate
981 a81c53c9 Iustin Pop
    multiple minors. The result is the list of minors, in the same
982 a81c53c9 Iustin Pop
    order as the passed nodes.
983 a81c53c9 Iustin Pop

984 32388e6d Iustin Pop
    @type instance: string
985 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
986 32388e6d Iustin Pop

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

1034 a81c53c9 Iustin Pop
    @type instance: string
1035 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
1036 a81c53c9 Iustin Pop
                     released
1037 a81c53c9 Iustin Pop

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

1049 61cf6b5e Iustin Pop
    This should be called on the error paths, on the success paths
1050 61cf6b5e Iustin Pop
    it's automatically called by the ConfigWriter add and update
1051 61cf6b5e Iustin Pop
    functions.
1052 61cf6b5e Iustin Pop

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

1055 61cf6b5e Iustin Pop
    @type instance: string
1056 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
1057 61cf6b5e Iustin Pop
                     released
1058 61cf6b5e Iustin Pop

1059 61cf6b5e Iustin Pop
    """
1060 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
1061 61cf6b5e Iustin Pop
1062 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1063 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
1064 4a8b186a Michael Hanselmann
    """Get the configuration version.
1065 4a8b186a Michael Hanselmann

1066 4a8b186a Michael Hanselmann
    @return: Config version
1067 4a8b186a Michael Hanselmann

1068 4a8b186a Michael Hanselmann
    """
1069 4a8b186a Michael Hanselmann
    return self._config_data.version
1070 4a8b186a Michael Hanselmann
1071 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1072 4a8b186a Michael Hanselmann
  def GetClusterName(self):
1073 4a8b186a Michael Hanselmann
    """Get cluster name.
1074 4a8b186a Michael Hanselmann

1075 4a8b186a Michael Hanselmann
    @return: Cluster name
1076 4a8b186a Michael Hanselmann

1077 4a8b186a Michael Hanselmann
    """
1078 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
1079 4a8b186a Michael Hanselmann
1080 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1081 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
1082 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
1083 4a8b186a Michael Hanselmann

1084 4a8b186a Michael Hanselmann
    @return: Master hostname
1085 4a8b186a Michael Hanselmann

1086 4a8b186a Michael Hanselmann
    """
1087 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
1088 4a8b186a Michael Hanselmann
1089 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1090 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
1091 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
1092 4a8b186a Michael Hanselmann

1093 4a8b186a Michael Hanselmann
    @return: Master IP
1094 4a8b186a Michael Hanselmann

1095 4a8b186a Michael Hanselmann
    """
1096 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
1097 4a8b186a Michael Hanselmann
1098 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1099 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
1100 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
1101 4a8b186a Michael Hanselmann

1102 4a8b186a Michael Hanselmann
    """
1103 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
1104 4a8b186a Michael Hanselmann
1105 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1106 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
1107 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
1108 5a8648eb Andrea Spadaccini

1109 5a8648eb Andrea Spadaccini
    """
1110 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
1111 5a8648eb Andrea Spadaccini
1112 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1113 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
1114 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
1115 33be7576 Andrea Spadaccini

1116 33be7576 Andrea Spadaccini
    """
1117 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
1118 33be7576 Andrea Spadaccini
1119 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1120 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
1121 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
1122 4a8b186a Michael Hanselmann

1123 4a8b186a Michael Hanselmann
    """
1124 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
1125 4a8b186a Michael Hanselmann
1126 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1127 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
1128 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
1129 4b97f902 Apollon Oikonomopoulos

1130 4b97f902 Apollon Oikonomopoulos
    """
1131 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1132 4b97f902 Apollon Oikonomopoulos
1133 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1134 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1135 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1136 4a8b186a Michael Hanselmann

1137 4a8b186a Michael Hanselmann
    """
1138 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
1139 4a8b186a Michael Hanselmann
1140 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1141 a8083063 Iustin Pop
  def GetHostKey(self):
1142 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1143 a8083063 Iustin Pop

1144 c41eea6e Iustin Pop
    @rtype: string
1145 c41eea6e Iustin Pop
    @return: the rsa hostkey
1146 a8083063 Iustin Pop

1147 a8083063 Iustin Pop
    """
1148 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1149 a8083063 Iustin Pop
1150 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1151 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1152 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1153 bf4af505 Apollon Oikonomopoulos

1154 bf4af505 Apollon Oikonomopoulos
    """
1155 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1156 bf4af505 Apollon Oikonomopoulos
1157 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1158 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1159 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1160 868a98ca Manuel Franceschini

1161 868a98ca Manuel Franceschini
    @return: primary ip family
1162 868a98ca Manuel Franceschini

1163 868a98ca Manuel Franceschini
    """
1164 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1165 868a98ca Manuel Franceschini
1166 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1167 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1168 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1169 c9f4b8e6 Andrea Spadaccini

1170 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1171 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1172 c9f4b8e6 Andrea Spadaccini

1173 c9f4b8e6 Andrea Spadaccini
    """
1174 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1175 c79198a0 Andrea Spadaccini
    result = objects.MasterNetworkParameters(name=cluster.master_node,
1176 c79198a0 Andrea Spadaccini
      ip=cluster.master_ip,
1177 c79198a0 Andrea Spadaccini
      netmask=cluster.master_netmask,
1178 c79198a0 Andrea Spadaccini
      netdev=cluster.master_netdev,
1179 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1180 c9f4b8e6 Andrea Spadaccini
1181 f9d20654 Andrea Spadaccini
    return result
1182 f9d20654 Andrea Spadaccini
1183 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1184 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1185 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1186 e11a1b77 Adeodato Simo

1187 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1188 90e99856 Adeodato Simo
    according to their default values.
1189 90e99856 Adeodato Simo

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

1199 e11a1b77 Adeodato Simo
    """
1200 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1201 e11a1b77 Adeodato Simo
    self._WriteConfig()
1202 e11a1b77 Adeodato Simo
1203 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1204 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1205 e11a1b77 Adeodato Simo

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

1236 e11a1b77 Adeodato Simo
    @type group_uuid: string
1237 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1238 e11a1b77 Adeodato Simo

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

1255 eaa98a04 Guido Trotter
    @type target: string or None
1256 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1257 eaa98a04 Guido Trotter
    @rtype: string
1258 412b3531 Guido Trotter
    @return: nodegroup UUID
1259 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1260 eaa98a04 Guido Trotter

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

1280 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1281 e85d8982 Stephen Shirley

1282 e85d8982 Stephen Shirley
    @type target: string or None
1283 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1284 e85d8982 Stephen Shirley
    @rtype: string
1285 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1286 e85d8982 Stephen Shirley

1287 e85d8982 Stephen Shirley
    """
1288 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1289 e85d8982 Stephen Shirley
1290 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1291 648e4196 Guido Trotter
    """Lookup a node group.
1292 648e4196 Guido Trotter

1293 648e4196 Guido Trotter
    @type uuid: string
1294 648e4196 Guido Trotter
    @param uuid: group UUID
1295 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1296 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1297 648e4196 Guido Trotter

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

1308 5768e6a6 René Nussbaumer
    @type uuid: string
1309 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1310 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1311 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1312 5768e6a6 René Nussbaumer

1313 5768e6a6 René Nussbaumer
    """
1314 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1315 5768e6a6 René Nussbaumer
1316 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1317 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1318 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1319 622444e5 Iustin Pop

1320 622444e5 Iustin Pop
    """
1321 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1322 622444e5 Iustin Pop
1323 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1324 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1325 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1326 1ac6f2ad Guido Trotter

1327 1ac6f2ad Guido Trotter
    """
1328 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1329 1ac6f2ad Guido Trotter
1330 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1331 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1332 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1333 dac81741 Michael Hanselmann

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

1345 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1346 080fbeea Michael Hanselmann
    @rtype: list
1347 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1348 080fbeea Michael Hanselmann

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

1356 a8083063 Iustin Pop
    This should be used after creating a new instance.
1357 a8083063 Iustin Pop

1358 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1359 c41eea6e Iustin Pop
    @param instance: the instance object
1360 c41eea6e Iustin Pop

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

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

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

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

1420 6a408fb2 Iustin Pop
    """
1421 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_UP)
1422 57de31c0 Agata Murawska
1423 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1424 57de31c0 Agata Murawska
  def MarkInstanceOffline(self, instance_name):
1425 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1426 57de31c0 Agata Murawska

1427 57de31c0 Agata Murawska
    """
1428 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_OFFLINE)
1429 6a408fb2 Iustin Pop
1430 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1431 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1432 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1433 a8083063 Iustin Pop

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

1463 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1464 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1465 fc95f88f Iustin Pop
    rename.
1466 fc95f88f Iustin Pop

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

1497 a8083063 Iustin Pop
    """
1498 2e04d454 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_DOWN)
1499 a8083063 Iustin Pop
1500 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1501 94bbfece Iustin Pop
    """Get the list of instances.
1502 94bbfece Iustin Pop

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

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

1512 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1513 c41eea6e Iustin Pop
        'instance1.example.com']
1514 a8083063 Iustin Pop

1515 a8083063 Iustin Pop
    """
1516 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1517 a8083063 Iustin Pop
1518 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1519 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1520 a8083063 Iustin Pop

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

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

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

1540 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1541 a8083063 Iustin Pop
    an instance are taken from the live systems.
1542 a8083063 Iustin Pop

1543 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1544 c41eea6e Iustin Pop
        I{instance1.example.com}
1545 a8083063 Iustin Pop

1546 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1547 c41eea6e Iustin Pop
    @return: the instance object
1548 a8083063 Iustin Pop

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

1556 2674690b Michael Hanselmann
    @rtype: frozenset
1557 2674690b Michael Hanselmann

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

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

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

1588 0b2de758 Iustin Pop
    @rtype: dict
1589 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1590 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1591 0b2de758 Iustin Pop

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

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

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

1617 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1618 c41eea6e Iustin Pop
    @param node: a Node instance
1619 a8083063 Iustin Pop

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

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

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

1657 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1658 c41eea6e Iustin Pop
    held.
1659 f78ede4e Guido Trotter

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

1662 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1663 c41eea6e Iustin Pop
    @return: the node object
1664 a8083063 Iustin Pop

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

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

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

1679 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1680 c41eea6e Iustin Pop
    @return: the node object
1681 f78ede4e Guido Trotter

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

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

1691 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1692 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1693 8bf9e9a5 Iustin Pop

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

1708 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1709 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1710 c71b049c Michael Hanselmann
    @rtype: frozenset
1711 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1712 c71b049c Michael Hanselmann

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

1727 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1728 c41eea6e Iustin Pop
    held.
1729 c41eea6e Iustin Pop

1730 c41eea6e Iustin Pop
    @rtype: list
1731 f78ede4e Guido Trotter

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

1739 f78ede4e Guido Trotter
    """
1740 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1741 f78ede4e Guido Trotter
1742 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1743 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1744 94a02bb5 Iustin Pop

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

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

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

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

1779 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1780 f5eaa3c1 Iustin Pop
    @rtype: list
1781 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1782 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1783 f5eaa3c1 Iustin Pop
        order
1784 f5eaa3c1 Iustin Pop

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

1792 d65e5776 Iustin Pop
    @rtype: dict
1793 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1794 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1795 d65e5776 Iustin Pop

1796 d65e5776 Iustin Pop
    """
1797 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1798 ee14d800 Michael Hanselmann
1799 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1800 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1801 ee14d800 Michael Hanselmann

1802 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1803 ee14d800 Michael Hanselmann

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

1812 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1813 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1814 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1815 9d5b1371 Michael Hanselmann

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

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

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

1843 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1844 ec0292f1 Iustin Pop

1845 23f06b2b Iustin Pop
    @type exceptions: list
1846 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1847 ec0292f1 Iustin Pop
    @rtype: tuple
1848 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1849 ec0292f1 Iustin Pop

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

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

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

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

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

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

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

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

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

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

2048 76d5d3a3 Iustin Pop
    This is because some data elements need uniqueness across the
2049 76d5d3a3 Iustin Pop
    whole configuration, etc.
2050 76d5d3a3 Iustin Pop

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

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

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

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

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

2192 054596f0 Iustin Pop
    @rtype: dict
2193 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2194 054596f0 Iustin Pop
        associated value
2195 054596f0 Iustin Pop

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

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

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

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

2290 9e33896b Luca Bigliardi
    """
2291 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2292 9e33896b Luca Bigliardi
2293 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2294 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2295 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2296 9e33896b Luca Bigliardi

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

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

2313 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2314 c41eea6e Iustin Pop
    @return: the cluster object
2315 62779dd0 Iustin Pop

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

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

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

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

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

2384 73064714 Guido Trotter
    """
2385 d8aee57e Iustin Pop
    for rm in self._all_rms:
2386 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)
2387 f2837050 Dimitris Aragiorgis
2388 f2837050 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2389 f2837050 Dimitris Aragiorgis
  def GetAllNetworksInfo(self):
2390 f2837050 Dimitris Aragiorgis
    """Get the configuration of all networks
2391 f2837050 Dimitris Aragiorgis

2392 f2837050 Dimitris Aragiorgis
    """
2393 f2837050 Dimitris Aragiorgis
    return dict(self._config_data.networks)
2394 f2837050 Dimitris Aragiorgis
2395 f2837050 Dimitris Aragiorgis
  def _UnlockedGetNetworkList(self):
2396 f2837050 Dimitris Aragiorgis
    """Get the list of networks.
2397 f2837050 Dimitris Aragiorgis

2398 f2837050 Dimitris Aragiorgis
    This function is for internal use, when the config lock is already held.
2399 f2837050 Dimitris Aragiorgis

2400 f2837050 Dimitris Aragiorgis
    """
2401 f2837050 Dimitris Aragiorgis
    return self._config_data.networks.keys()
2402 f2837050 Dimitris Aragiorgis
2403 f2837050 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2404 f2837050 Dimitris Aragiorgis
  def GetNetworkList(self):
2405 f2837050 Dimitris Aragiorgis
    """Get the list of networks.
2406 f2837050 Dimitris Aragiorgis

2407 f2837050 Dimitris Aragiorgis
    @return: array of networks, ex. ["main", "vlan100", "200]
2408 f2837050 Dimitris Aragiorgis

2409 f2837050 Dimitris Aragiorgis
    """
2410 f2837050 Dimitris Aragiorgis
    return self._UnlockedGetNetworkList()
2411 f2837050 Dimitris Aragiorgis
2412 f2837050 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2413 f2837050 Dimitris Aragiorgis
  def GetNetworkNames(self):
2414 f2837050 Dimitris Aragiorgis
    """Get a list of network names
2415 f2837050 Dimitris Aragiorgis

2416 f2837050 Dimitris Aragiorgis
    """
2417 f2837050 Dimitris Aragiorgis
    names = [network.name
2418 f2837050 Dimitris Aragiorgis
             for network in self._config_data.networks.values()]
2419 f2837050 Dimitris Aragiorgis
    return names
2420 f2837050 Dimitris Aragiorgis
2421 f2837050 Dimitris Aragiorgis
  def _UnlockedGetNetwork(self, uuid):
2422 f2837050 Dimitris Aragiorgis
    """Returns information about a network.
2423 f2837050 Dimitris Aragiorgis

2424 f2837050 Dimitris Aragiorgis
    This function is for internal use, when the config lock is already held.
2425 f2837050 Dimitris Aragiorgis

2426 f2837050 Dimitris Aragiorgis
    """
2427 f2837050 Dimitris Aragiorgis
    if uuid not in self._config_data.networks:
2428 f2837050 Dimitris Aragiorgis
      return None
2429 f2837050 Dimitris Aragiorgis
2430 f2837050 Dimitris Aragiorgis
    return self._config_data.networks[uuid]
2431 f2837050 Dimitris Aragiorgis
2432 f2837050 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2433 f2837050 Dimitris Aragiorgis
  def GetNetwork(self, uuid):
2434 f2837050 Dimitris Aragiorgis
    """Returns information about a network.
2435 f2837050 Dimitris Aragiorgis

2436 f2837050 Dimitris Aragiorgis
    It takes the information from the configuration file.
2437 f2837050 Dimitris Aragiorgis

2438 f2837050 Dimitris Aragiorgis
    @param uuid: UUID of the network
2439 f2837050 Dimitris Aragiorgis

2440 f2837050 Dimitris Aragiorgis
    @rtype: L{objects.Network}
2441 f2837050 Dimitris Aragiorgis
    @return: the network object
2442 f2837050 Dimitris Aragiorgis

2443 f2837050 Dimitris Aragiorgis
    """
2444 f2837050 Dimitris Aragiorgis
    return self._UnlockedGetNetwork(uuid)
2445 f2837050 Dimitris Aragiorgis
2446 f2837050 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2447 f2837050 Dimitris Aragiorgis
  def AddNetwork(self, net, ec_id, check_uuid=True):
2448 f2837050 Dimitris Aragiorgis
    """Add a network to the configuration.
2449 f2837050 Dimitris Aragiorgis

2450 f2837050 Dimitris Aragiorgis
    @type net: L{objects.Network}
2451 f2837050 Dimitris Aragiorgis
    @param net: the Network object to add
2452 f2837050 Dimitris Aragiorgis
    @type ec_id: string
2453 f2837050 Dimitris Aragiorgis
    @param ec_id: unique id for the job to use when creating a missing UUID
2454 f2837050 Dimitris Aragiorgis

2455 f2837050 Dimitris Aragiorgis
    """
2456 f2837050 Dimitris Aragiorgis
    self._UnlockedAddNetwork(net, ec_id, check_uuid)
2457 f2837050 Dimitris Aragiorgis
    self._WriteConfig()
2458 f2837050 Dimitris Aragiorgis
2459 f2837050 Dimitris Aragiorgis
  def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
2460 f2837050 Dimitris Aragiorgis
    """Add a network to the configuration.
2461 f2837050 Dimitris Aragiorgis

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

2481 f2837050 Dimitris Aragiorgis
    @type target: string
2482 f2837050 Dimitris Aragiorgis
    @param target: network name or UUID
2483 f2837050 Dimitris Aragiorgis
    @rtype: string
2484 f2837050 Dimitris Aragiorgis
    @return: network UUID
2485 f2837050 Dimitris Aragiorgis
    @raises errors.OpPrereqError: when the target network cannot be found
2486 f2837050 Dimitris Aragiorgis

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

2499 f2837050 Dimitris Aragiorgis
    This function is just a wrapper over L{_UnlockedLookupNetwork}.
2500 f2837050 Dimitris Aragiorgis

2501 f2837050 Dimitris Aragiorgis
    @type target: string
2502 f2837050 Dimitris Aragiorgis
    @param target: network name or UUID
2503 f2837050 Dimitris Aragiorgis
    @rtype: string
2504 f2837050 Dimitris Aragiorgis
    @return: network UUID
2505 f2837050 Dimitris Aragiorgis

2506 f2837050 Dimitris Aragiorgis
    """
2507 f2837050 Dimitris Aragiorgis
    return self._UnlockedLookupNetwork(target)
2508 f2837050 Dimitris Aragiorgis
2509 f2837050 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2510 f2837050 Dimitris Aragiorgis
  def RemoveNetwork(self, network_uuid):
2511 f2837050 Dimitris Aragiorgis
    """Remove a network from the configuration.
2512 f2837050 Dimitris Aragiorgis

2513 f2837050 Dimitris Aragiorgis
    @type network_uuid: string
2514 f2837050 Dimitris Aragiorgis
    @param network_uuid: the UUID of the network to remove
2515 f2837050 Dimitris Aragiorgis

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

2529 0ca10882 Dimitris Aragiorgis
    Get a network's netparams for a given node.
2530 0ca10882 Dimitris Aragiorgis

2531 0ca10882 Dimitris Aragiorgis
    @type net: string
2532 0ca10882 Dimitris Aragiorgis
    @param net: network name
2533 0ca10882 Dimitris Aragiorgis
    @type node: string
2534 0ca10882 Dimitris Aragiorgis
    @param node: node name
2535 0ca10882 Dimitris Aragiorgis
    @rtype: dict or None
2536 0ca10882 Dimitris Aragiorgis
    @return: netparams
2537 0ca10882 Dimitris Aragiorgis

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

2553 0ca10882 Dimitris Aragiorgis
    """
2554 0ca10882 Dimitris Aragiorgis
    return self._UnlockedGetGroupNetParams(net, node)
2555 0ca10882 Dimitris Aragiorgis
2556 0ca10882 Dimitris Aragiorgis
2557 0ca10882 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2558 0ca10882 Dimitris Aragiorgis
  def CheckIPInNodeGroup(self, ip, node):
2559 0ca10882 Dimitris Aragiorgis
    """Check for conflictig IP.
2560 0ca10882 Dimitris Aragiorgis

2561 0ca10882 Dimitris Aragiorgis
    @type ip: string
2562 0ca10882 Dimitris Aragiorgis
    @param ip: ip address
2563 0ca10882 Dimitris Aragiorgis
    @type node: string
2564 0ca10882 Dimitris Aragiorgis
    @param node: node name
2565 0ca10882 Dimitris Aragiorgis
    @rtype: (string, dict) or (None, None)
2566 0ca10882 Dimitris Aragiorgis
    @return: (network name, netparams)
2567 0ca10882 Dimitris Aragiorgis

2568 0ca10882 Dimitris Aragiorgis
    """
2569 0ca10882 Dimitris Aragiorgis
    if ip is None:
2570 0ca10882 Dimitris Aragiorgis
      return (None, None)
2571 0ca10882 Dimitris Aragiorgis
    node_info = self._UnlockedGetNodeInfo(node)
2572 0ca10882 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2573 0ca10882 Dimitris Aragiorgis
    for net_uuid in nodegroup_info.networks.keys():
2574 0ca10882 Dimitris Aragiorgis
      net_info = self._UnlockedGetNetwork(net_uuid)
2575 0ca10882 Dimitris Aragiorgis
      pool = network.AddressPool(net_info)
2576 0ca10882 Dimitris Aragiorgis
      if pool._Contains(ip):
2577 0ca10882 Dimitris Aragiorgis
        return (net_info.name, nodegroup_info.networks[net_uuid])
2578 0ca10882 Dimitris Aragiorgis
2579 0ca10882 Dimitris Aragiorgis
    return (None, None)