Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 3da6e141

History | View | Annotate | Download (86.3 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

472 c41eea6e Iustin Pop
    @rtype: string
473 c41eea6e Iustin Pop
    @return: the unique id
474 923b1523 Iustin Pop

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

483 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
484 430b923c Iustin Pop

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

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

494 c41eea6e Iustin Pop
    @rtype: list
495 c41eea6e Iustin Pop
    @return: the list of all MACs
496 c41eea6e Iustin Pop

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

508 c41eea6e Iustin Pop
    @rtype: list
509 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
510 c41eea6e Iustin Pop

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

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

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

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

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

837 4a89c54a Iustin Pop
    This is just a wrapper over L{_UnlockedVerifyConfig}.
838 4a89c54a Iustin Pop

839 4a89c54a Iustin Pop
    @rtype: list
840 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
841 4a89c54a Iustin Pop
        configuration errors
842 4a89c54a Iustin Pop

843 4a89c54a Iustin Pop
    """
844 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
845 4a89c54a Iustin Pop
846 f78ede4e Guido Trotter
  def _UnlockedSetDiskID(self, disk, node_name):
847 a8083063 Iustin Pop
    """Convert the unique ID to the ID needed on the target nodes.
848 a8083063 Iustin Pop

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

851 a8083063 Iustin Pop
    The routine descends down and updates its children also, because
852 a8083063 Iustin Pop
    this helps when the only the top device is passed to the remote
853 a8083063 Iustin Pop
    node.
854 a8083063 Iustin Pop

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

857 a8083063 Iustin Pop
    """
858 a8083063 Iustin Pop
    if disk.children:
859 a8083063 Iustin Pop
      for child in disk.children:
860 f78ede4e Guido Trotter
        self._UnlockedSetDiskID(child, node_name)
861 a8083063 Iustin Pop
862 a8083063 Iustin Pop
    if disk.logical_id is None and disk.physical_id is not None:
863 a8083063 Iustin Pop
      return
864 ffa1c0dc Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
865 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = disk.logical_id
866 a8083063 Iustin Pop
      if node_name not in (pnode, snode):
867 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
868 3ecf6786 Iustin Pop
                                        node_name)
869 f78ede4e Guido Trotter
      pnode_info = self._UnlockedGetNodeInfo(pnode)
870 f78ede4e Guido Trotter
      snode_info = self._UnlockedGetNodeInfo(snode)
871 a8083063 Iustin Pop
      if pnode_info is None or snode_info is None:
872 a8083063 Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
873 a8083063 Iustin Pop
                                        " for %s" % str(disk))
874 ffa1c0dc Iustin Pop
      p_data = (pnode_info.secondary_ip, port)
875 ffa1c0dc Iustin Pop
      s_data = (snode_info.secondary_ip, port)
876 a8083063 Iustin Pop
      if pnode == node_name:
877 f9518d38 Iustin Pop
        disk.physical_id = p_data + s_data + (pminor, secret)
878 a8083063 Iustin Pop
      else: # it must be secondary, we tested above
879 f9518d38 Iustin Pop
        disk.physical_id = s_data + p_data + (sminor, secret)
880 a8083063 Iustin Pop
    else:
881 a8083063 Iustin Pop
      disk.physical_id = disk.logical_id
882 a8083063 Iustin Pop
    return
883 a8083063 Iustin Pop
884 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
885 f78ede4e Guido Trotter
  def SetDiskID(self, disk, node_name):
886 f78ede4e Guido Trotter
    """Convert the unique ID to the ID needed on the target nodes.
887 f78ede4e Guido Trotter

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

890 f78ede4e Guido Trotter
    The routine descends down and updates its children also, because
891 f78ede4e Guido Trotter
    this helps when the only the top device is passed to the remote
892 f78ede4e Guido Trotter
    node.
893 f78ede4e Guido Trotter

894 f78ede4e Guido Trotter
    """
895 f78ede4e Guido Trotter
    return self._UnlockedSetDiskID(disk, node_name)
896 f78ede4e Guido Trotter
897 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
898 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
899 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
900 b2fddf63 Iustin Pop

901 3b3b1bca Dimitris Aragiorgis
    @warning: this method does not "flush" the configuration (via
902 3b3b1bca Dimitris Aragiorgis
        L{_WriteConfig}); callers should do that themselves once the
903 3b3b1bca Dimitris Aragiorgis
        configuration is stable
904 3b3b1bca Dimitris Aragiorgis

905 b2fddf63 Iustin Pop
    """
906 264bb3c5 Michael Hanselmann
    if not isinstance(port, int):
907 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed for port")
908 264bb3c5 Michael Hanselmann
909 b2fddf63 Iustin Pop
    self._config_data.cluster.tcpudp_port_pool.add(port)
910 264bb3c5 Michael Hanselmann
911 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
912 b2fddf63 Iustin Pop
  def GetPortList(self):
913 264bb3c5 Michael Hanselmann
    """Returns a copy of the current port list.
914 264bb3c5 Michael Hanselmann

915 264bb3c5 Michael Hanselmann
    """
916 b2fddf63 Iustin Pop
    return self._config_data.cluster.tcpudp_port_pool.copy()
917 264bb3c5 Michael Hanselmann
918 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
919 a8083063 Iustin Pop
  def AllocatePort(self):
920 a8083063 Iustin Pop
    """Allocate a port.
921 a8083063 Iustin Pop

922 b2fddf63 Iustin Pop
    The port will be taken from the available port pool or from the
923 b2fddf63 Iustin Pop
    default port range (and in this case we increase
924 b2fddf63 Iustin Pop
    highest_used_port).
925 a8083063 Iustin Pop

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

944 4a89c54a Iustin Pop
    @rtype: (dict, list)
945 c41eea6e Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
946 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
947 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
948 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
949 4a89c54a Iustin Pop
        should raise an exception
950 a81c53c9 Iustin Pop

951 a81c53c9 Iustin Pop
    """
952 a81c53c9 Iustin Pop
    def _AppendUsedPorts(instance_name, disk, used):
953 4a89c54a Iustin Pop
      duplicates = []
954 f9518d38 Iustin Pop
      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
955 7c4d6c7b Michael Hanselmann
        node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
956 7c4d6c7b Michael Hanselmann
        for node, port in ((node_a, minor_a), (node_b, minor_b)):
957 4a89c54a Iustin Pop
          assert node in used, ("Node '%s' of instance '%s' not found"
958 4a89c54a Iustin Pop
                                " in node list" % (node, instance_name))
959 a81c53c9 Iustin Pop
          if port in used[node]:
960 4a89c54a Iustin Pop
            duplicates.append((node, port, instance_name, used[node][port]))
961 4a89c54a Iustin Pop
          else:
962 4a89c54a Iustin Pop
            used[node][port] = instance_name
963 a81c53c9 Iustin Pop
      if disk.children:
964 a81c53c9 Iustin Pop
        for child in disk.children:
965 4a89c54a Iustin Pop
          duplicates.extend(_AppendUsedPorts(instance_name, child, used))
966 4a89c54a Iustin Pop
      return duplicates
967 a81c53c9 Iustin Pop
968 4a89c54a Iustin Pop
    duplicates = []
969 a81c53c9 Iustin Pop
    my_dict = dict((node, {}) for node in self._config_data.nodes)
970 79b26a7a Iustin Pop
    for instance in self._config_data.instances.itervalues():
971 79b26a7a Iustin Pop
      for disk in instance.disks:
972 79b26a7a Iustin Pop
        duplicates.extend(_AppendUsedPorts(instance.name, disk, my_dict))
973 a81c53c9 Iustin Pop
    for (node, minor), instance in self._temporary_drbds.iteritems():
974 79b26a7a Iustin Pop
      if minor in my_dict[node] and my_dict[node][minor] != instance:
975 4a89c54a Iustin Pop
        duplicates.append((node, minor, instance, my_dict[node][minor]))
976 4a89c54a Iustin Pop
      else:
977 4a89c54a Iustin Pop
        my_dict[node][minor] = instance
978 4a89c54a Iustin Pop
    return my_dict, duplicates
979 a81c53c9 Iustin Pop
980 a81c53c9 Iustin Pop
  @locking.ssynchronized(_config_lock)
981 6d2e83d5 Iustin Pop
  def ComputeDRBDMap(self):
982 6d2e83d5 Iustin Pop
    """Compute the used DRBD minor/nodes.
983 6d2e83d5 Iustin Pop

984 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
985 6d2e83d5 Iustin Pop

986 6d2e83d5 Iustin Pop
    @return: dictionary of node_name: dict of minor: instance_name;
987 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
988 6d2e83d5 Iustin Pop
        an empty list).
989 6d2e83d5 Iustin Pop

990 6d2e83d5 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 4a89c54a Iustin Pop
    return d_map
996 6d2e83d5 Iustin Pop
997 6d2e83d5 Iustin Pop
  @locking.ssynchronized(_config_lock)
998 a81c53c9 Iustin Pop
  def AllocateDRBDMinor(self, nodes, instance):
999 a81c53c9 Iustin Pop
    """Allocate a drbd minor.
1000 a81c53c9 Iustin Pop

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

1006 32388e6d Iustin Pop
    @type instance: string
1007 32388e6d Iustin Pop
    @param instance: the instance for which we allocate minors
1008 32388e6d Iustin Pop

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

1056 a81c53c9 Iustin Pop
    @type instance: string
1057 a81c53c9 Iustin Pop
    @param instance: the instance for which temporary minors should be
1058 a81c53c9 Iustin Pop
                     released
1059 a81c53c9 Iustin Pop

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

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

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

1077 61cf6b5e Iustin Pop
    @type instance: string
1078 61cf6b5e Iustin Pop
    @param instance: the instance for which temporary minors should be
1079 61cf6b5e Iustin Pop
                     released
1080 61cf6b5e Iustin Pop

1081 61cf6b5e Iustin Pop
    """
1082 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance)
1083 61cf6b5e Iustin Pop
1084 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1085 4a8b186a Michael Hanselmann
  def GetConfigVersion(self):
1086 4a8b186a Michael Hanselmann
    """Get the configuration version.
1087 4a8b186a Michael Hanselmann

1088 4a8b186a Michael Hanselmann
    @return: Config version
1089 4a8b186a Michael Hanselmann

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

1097 4a8b186a Michael Hanselmann
    @return: Cluster name
1098 4a8b186a Michael Hanselmann

1099 4a8b186a Michael Hanselmann
    """
1100 4a8b186a Michael Hanselmann
    return self._config_data.cluster.cluster_name
1101 4a8b186a Michael Hanselmann
1102 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1103 4a8b186a Michael Hanselmann
  def GetMasterNode(self):
1104 4a8b186a Michael Hanselmann
    """Get the hostname of the master node for this cluster.
1105 4a8b186a Michael Hanselmann

1106 4a8b186a Michael Hanselmann
    @return: Master hostname
1107 4a8b186a Michael Hanselmann

1108 4a8b186a Michael Hanselmann
    """
1109 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
1110 4a8b186a Michael Hanselmann
1111 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1112 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
1113 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
1114 4a8b186a Michael Hanselmann

1115 4a8b186a Michael Hanselmann
    @return: Master IP
1116 4a8b186a Michael Hanselmann

1117 4a8b186a Michael Hanselmann
    """
1118 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
1119 4a8b186a Michael Hanselmann
1120 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1121 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
1122 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
1123 4a8b186a Michael Hanselmann

1124 4a8b186a Michael Hanselmann
    """
1125 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
1126 4a8b186a Michael Hanselmann
1127 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1128 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
1129 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
1130 5a8648eb Andrea Spadaccini

1131 5a8648eb Andrea Spadaccini
    """
1132 5a8648eb Andrea Spadaccini
    return self._config_data.cluster.master_netmask
1133 5a8648eb Andrea Spadaccini
1134 5a8648eb Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1135 33be7576 Andrea Spadaccini
  def GetUseExternalMipScript(self):
1136 33be7576 Andrea Spadaccini
    """Get flag representing whether to use the external master IP setup script.
1137 33be7576 Andrea Spadaccini

1138 33be7576 Andrea Spadaccini
    """
1139 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
1140 33be7576 Andrea Spadaccini
1141 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1142 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
1143 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
1144 4a8b186a Michael Hanselmann

1145 4a8b186a Michael Hanselmann
    """
1146 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
1147 4a8b186a Michael Hanselmann
1148 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1149 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
1150 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
1151 4b97f902 Apollon Oikonomopoulos

1152 4b97f902 Apollon Oikonomopoulos
    """
1153 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1154 4b97f902 Apollon Oikonomopoulos
1155 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1156 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1157 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1158 4a8b186a Michael Hanselmann

1159 4a8b186a Michael Hanselmann
    """
1160 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
1161 4a8b186a Michael Hanselmann
1162 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1163 a8083063 Iustin Pop
  def GetHostKey(self):
1164 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1165 a8083063 Iustin Pop

1166 c41eea6e Iustin Pop
    @rtype: string
1167 c41eea6e Iustin Pop
    @return: the rsa hostkey
1168 a8083063 Iustin Pop

1169 a8083063 Iustin Pop
    """
1170 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1171 a8083063 Iustin Pop
1172 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1173 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1174 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1175 bf4af505 Apollon Oikonomopoulos

1176 bf4af505 Apollon Oikonomopoulos
    """
1177 bf4af505 Apollon Oikonomopoulos
    return self._config_data.cluster.default_iallocator
1178 bf4af505 Apollon Oikonomopoulos
1179 868a98ca Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
1180 868a98ca Manuel Franceschini
  def GetPrimaryIPFamily(self):
1181 868a98ca Manuel Franceschini
    """Get cluster primary ip family.
1182 868a98ca Manuel Franceschini

1183 868a98ca Manuel Franceschini
    @return: primary ip family
1184 868a98ca Manuel Franceschini

1185 868a98ca Manuel Franceschini
    """
1186 868a98ca Manuel Franceschini
    return self._config_data.cluster.primary_ip_family
1187 868a98ca Manuel Franceschini
1188 c9f4b8e6 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1189 c9f4b8e6 Andrea Spadaccini
  def GetMasterNetworkParameters(self):
1190 c9f4b8e6 Andrea Spadaccini
    """Get network parameters of the master node.
1191 c9f4b8e6 Andrea Spadaccini

1192 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1193 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1194 c9f4b8e6 Andrea Spadaccini

1195 c9f4b8e6 Andrea Spadaccini
    """
1196 c9f4b8e6 Andrea Spadaccini
    cluster = self._config_data.cluster
1197 5ae4945a Iustin Pop
    result = objects.MasterNetworkParameters(
1198 5ae4945a Iustin Pop
      name=cluster.master_node, ip=cluster.master_ip,
1199 5ae4945a Iustin Pop
      netmask=cluster.master_netmask, netdev=cluster.master_netdev,
1200 c79198a0 Andrea Spadaccini
      ip_family=cluster.primary_ip_family)
1201 c9f4b8e6 Andrea Spadaccini
1202 f9d20654 Andrea Spadaccini
    return result
1203 f9d20654 Andrea Spadaccini
1204 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1205 e11a1b77 Adeodato Simo
  def AddNodeGroup(self, group, ec_id, check_uuid=True):
1206 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1207 e11a1b77 Adeodato Simo

1208 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1209 90e99856 Adeodato Simo
    according to their default values.
1210 90e99856 Adeodato Simo

1211 e11a1b77 Adeodato Simo
    @type group: L{objects.NodeGroup}
1212 e11a1b77 Adeodato Simo
    @param group: the NodeGroup object to add
1213 e11a1b77 Adeodato Simo
    @type ec_id: string
1214 e11a1b77 Adeodato Simo
    @param ec_id: unique id for the job to use when creating a missing UUID
1215 e11a1b77 Adeodato Simo
    @type check_uuid: bool
1216 e11a1b77 Adeodato Simo
    @param check_uuid: add an UUID to the group if it doesn't have one or, if
1217 e11a1b77 Adeodato Simo
                       it does, ensure that it does not exist in the
1218 e11a1b77 Adeodato Simo
                       configuration already
1219 e11a1b77 Adeodato Simo

1220 e11a1b77 Adeodato Simo
    """
1221 e11a1b77 Adeodato Simo
    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1222 e11a1b77 Adeodato Simo
    self._WriteConfig()
1223 e11a1b77 Adeodato Simo
1224 e11a1b77 Adeodato Simo
  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1225 e11a1b77 Adeodato Simo
    """Add a node group to the configuration.
1226 e11a1b77 Adeodato Simo

1227 e11a1b77 Adeodato Simo
    """
1228 e11a1b77 Adeodato Simo
    logging.info("Adding node group %s to configuration", group.name)
1229 e11a1b77 Adeodato Simo
1230 e11a1b77 Adeodato Simo
    # Some code might need to add a node group with a pre-populated UUID
1231 e11a1b77 Adeodato Simo
    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
1232 e11a1b77 Adeodato Simo
    # the "does this UUID" exist already check.
1233 e11a1b77 Adeodato Simo
    if check_uuid:
1234 e11a1b77 Adeodato Simo
      self._EnsureUUID(group, ec_id)
1235 e11a1b77 Adeodato Simo
1236 18ffc0fe Stephen Shirley
    try:
1237 18ffc0fe Stephen Shirley
      existing_uuid = self._UnlockedLookupNodeGroup(group.name)
1238 18ffc0fe Stephen Shirley
    except errors.OpPrereqError:
1239 18ffc0fe Stephen Shirley
      pass
1240 18ffc0fe Stephen Shirley
    else:
1241 18ffc0fe Stephen Shirley
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
1242 18ffc0fe Stephen Shirley
                                 " node group (UUID: %s)" %
1243 18ffc0fe Stephen Shirley
                                 (group.name, existing_uuid),
1244 18ffc0fe Stephen Shirley
                                 errors.ECODE_EXISTS)
1245 18ffc0fe Stephen Shirley
1246 e11a1b77 Adeodato Simo
    group.serial_no = 1
1247 e11a1b77 Adeodato Simo
    group.ctime = group.mtime = time.time()
1248 90e99856 Adeodato Simo
    group.UpgradeConfig()
1249 e11a1b77 Adeodato Simo
1250 e11a1b77 Adeodato Simo
    self._config_data.nodegroups[group.uuid] = group
1251 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1252 e11a1b77 Adeodato Simo
1253 e11a1b77 Adeodato Simo
  @locking.ssynchronized(_config_lock)
1254 e11a1b77 Adeodato Simo
  def RemoveNodeGroup(self, group_uuid):
1255 e11a1b77 Adeodato Simo
    """Remove a node group from the configuration.
1256 e11a1b77 Adeodato Simo

1257 e11a1b77 Adeodato Simo
    @type group_uuid: string
1258 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1259 e11a1b77 Adeodato Simo

1260 e11a1b77 Adeodato Simo
    """
1261 e11a1b77 Adeodato Simo
    logging.info("Removing node group %s from configuration", group_uuid)
1262 e11a1b77 Adeodato Simo
1263 e11a1b77 Adeodato Simo
    if group_uuid not in self._config_data.nodegroups:
1264 e11a1b77 Adeodato Simo
      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1265 e11a1b77 Adeodato Simo
1266 0389c42a Stephen Shirley
    assert len(self._config_data.nodegroups) != 1, \
1267 0389c42a Stephen Shirley
            "Group '%s' is the only group, cannot be removed" % group_uuid
1268 0389c42a Stephen Shirley
1269 e11a1b77 Adeodato Simo
    del self._config_data.nodegroups[group_uuid]
1270 e11a1b77 Adeodato Simo
    self._config_data.cluster.serial_no += 1
1271 e11a1b77 Adeodato Simo
    self._WriteConfig()
1272 e11a1b77 Adeodato Simo
1273 e85d8982 Stephen Shirley
  def _UnlockedLookupNodeGroup(self, target):
1274 412b3531 Guido Trotter
    """Lookup a node group's UUID.
1275 eaa98a04 Guido Trotter

1276 eaa98a04 Guido Trotter
    @type target: string or None
1277 412b3531 Guido Trotter
    @param target: group name or UUID or None to look for the default
1278 eaa98a04 Guido Trotter
    @rtype: string
1279 412b3531 Guido Trotter
    @return: nodegroup UUID
1280 eaa98a04 Guido Trotter
    @raises errors.OpPrereqError: when the target group cannot be found
1281 eaa98a04 Guido Trotter

1282 eaa98a04 Guido Trotter
    """
1283 eaa98a04 Guido Trotter
    if target is None:
1284 eaa98a04 Guido Trotter
      if len(self._config_data.nodegroups) != 1:
1285 913cc25e Adeodato Simo
        raise errors.OpPrereqError("More than one node group exists. Target"
1286 2ed0e208 Iustin Pop
                                   " group must be specified explicitly.")
1287 eaa98a04 Guido Trotter
      else:
1288 eaa98a04 Guido Trotter
        return self._config_data.nodegroups.keys()[0]
1289 eaa98a04 Guido Trotter
    if target in self._config_data.nodegroups:
1290 eaa98a04 Guido Trotter
      return target
1291 eaa98a04 Guido Trotter
    for nodegroup in self._config_data.nodegroups.values():
1292 eaa98a04 Guido Trotter
      if nodegroup.name == target:
1293 eaa98a04 Guido Trotter
        return nodegroup.uuid
1294 e0f9ed64 Adeodato Simo
    raise errors.OpPrereqError("Node group '%s' not found" % target,
1295 e0f9ed64 Adeodato Simo
                               errors.ECODE_NOENT)
1296 eaa98a04 Guido Trotter
1297 e85d8982 Stephen Shirley
  @locking.ssynchronized(_config_lock, shared=1)
1298 e85d8982 Stephen Shirley
  def LookupNodeGroup(self, target):
1299 e85d8982 Stephen Shirley
    """Lookup a node group's UUID.
1300 e85d8982 Stephen Shirley

1301 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1302 e85d8982 Stephen Shirley

1303 e85d8982 Stephen Shirley
    @type target: string or None
1304 e85d8982 Stephen Shirley
    @param target: group name or UUID or None to look for the default
1305 e85d8982 Stephen Shirley
    @rtype: string
1306 e85d8982 Stephen Shirley
    @return: nodegroup UUID
1307 e85d8982 Stephen Shirley

1308 e85d8982 Stephen Shirley
    """
1309 e85d8982 Stephen Shirley
    return self._UnlockedLookupNodeGroup(target)
1310 e85d8982 Stephen Shirley
1311 5768e6a6 René Nussbaumer
  def _UnlockedGetNodeGroup(self, uuid):
1312 648e4196 Guido Trotter
    """Lookup a node group.
1313 648e4196 Guido Trotter

1314 648e4196 Guido Trotter
    @type uuid: string
1315 648e4196 Guido Trotter
    @param uuid: group UUID
1316 648e4196 Guido Trotter
    @rtype: L{objects.NodeGroup} or None
1317 648e4196 Guido Trotter
    @return: nodegroup object, or None if not found
1318 648e4196 Guido Trotter

1319 648e4196 Guido Trotter
    """
1320 648e4196 Guido Trotter
    if uuid not in self._config_data.nodegroups:
1321 648e4196 Guido Trotter
      return None
1322 648e4196 Guido Trotter
1323 648e4196 Guido Trotter
    return self._config_data.nodegroups[uuid]
1324 648e4196 Guido Trotter
1325 648e4196 Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1326 5768e6a6 René Nussbaumer
  def GetNodeGroup(self, uuid):
1327 5768e6a6 René Nussbaumer
    """Lookup a node group.
1328 5768e6a6 René Nussbaumer

1329 5768e6a6 René Nussbaumer
    @type uuid: string
1330 5768e6a6 René Nussbaumer
    @param uuid: group UUID
1331 5768e6a6 René Nussbaumer
    @rtype: L{objects.NodeGroup} or None
1332 5768e6a6 René Nussbaumer
    @return: nodegroup object, or None if not found
1333 5768e6a6 René Nussbaumer

1334 5768e6a6 René Nussbaumer
    """
1335 5768e6a6 René Nussbaumer
    return self._UnlockedGetNodeGroup(uuid)
1336 5768e6a6 René Nussbaumer
1337 5768e6a6 René Nussbaumer
  @locking.ssynchronized(_config_lock, shared=1)
1338 622444e5 Iustin Pop
  def GetAllNodeGroupsInfo(self):
1339 622444e5 Iustin Pop
    """Get the configuration of all node groups.
1340 622444e5 Iustin Pop

1341 622444e5 Iustin Pop
    """
1342 622444e5 Iustin Pop
    return dict(self._config_data.nodegroups)
1343 622444e5 Iustin Pop
1344 1ac6f2ad Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1345 1ac6f2ad Guido Trotter
  def GetNodeGroupList(self):
1346 1ac6f2ad Guido Trotter
    """Get a list of node groups.
1347 1ac6f2ad Guido Trotter

1348 1ac6f2ad Guido Trotter
    """
1349 1ac6f2ad Guido Trotter
    return self._config_data.nodegroups.keys()
1350 1ac6f2ad Guido Trotter
1351 dac81741 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1352 dac81741 Michael Hanselmann
  def GetNodeGroupMembersByNodes(self, nodes):
1353 dac81741 Michael Hanselmann
    """Get nodes which are member in the same nodegroups as the given nodes.
1354 dac81741 Michael Hanselmann

1355 dac81741 Michael Hanselmann
    """
1356 dac81741 Michael Hanselmann
    ngfn = lambda node_name: self._UnlockedGetNodeInfo(node_name).group
1357 dac81741 Michael Hanselmann
    return frozenset(member_name
1358 dac81741 Michael Hanselmann
                     for node_name in nodes
1359 dac81741 Michael Hanselmann
                     for member_name in
1360 dac81741 Michael Hanselmann
                       self._UnlockedGetNodeGroup(ngfn(node_name)).members)
1361 dac81741 Michael Hanselmann
1362 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1363 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1364 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1365 080fbeea Michael Hanselmann

1366 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1367 080fbeea Michael Hanselmann
    @rtype: list
1368 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1369 080fbeea Michael Hanselmann

1370 080fbeea Michael Hanselmann
    """
1371 080fbeea Michael Hanselmann
    return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1372 080fbeea Michael Hanselmann
1373 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1374 0debfb35 Guido Trotter
  def AddInstance(self, instance, ec_id):
1375 a8083063 Iustin Pop
    """Add an instance to the config.
1376 a8083063 Iustin Pop

1377 a8083063 Iustin Pop
    This should be used after creating a new instance.
1378 a8083063 Iustin Pop

1379 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1380 c41eea6e Iustin Pop
    @param instance: the instance object
1381 c41eea6e Iustin Pop

1382 a8083063 Iustin Pop
    """
1383 a8083063 Iustin Pop
    if not isinstance(instance, objects.Instance):
1384 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to AddInstance")
1385 a8083063 Iustin Pop
1386 e00fb268 Iustin Pop
    if instance.disk_template != constants.DT_DISKLESS:
1387 e00fb268 Iustin Pop
      all_lvs = instance.MapLVsByNode()
1388 74a48621 Iustin Pop
      logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs)
1389 923b1523 Iustin Pop
1390 e4640214 Guido Trotter
    all_macs = self._AllMACs()
1391 e4640214 Guido Trotter
    for nic in instance.nics:
1392 e4640214 Guido Trotter
      if nic.mac in all_macs:
1393 e4640214 Guido Trotter
        raise errors.ConfigurationError("Cannot add instance %s:"
1394 430b923c Iustin Pop
                                        " MAC address '%s' already in use." %
1395 430b923c Iustin Pop
                                        (instance.name, nic.mac))
1396 430b923c Iustin Pop
1397 0debfb35 Guido Trotter
    self._EnsureUUID(instance, ec_id)
1398 e4640214 Guido Trotter
1399 b989e85d Iustin Pop
    instance.serial_no = 1
1400 d693c864 Iustin Pop
    instance.ctime = instance.mtime = time.time()
1401 a8083063 Iustin Pop
    self._config_data.instances[instance.name] = instance
1402 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1403 61cf6b5e Iustin Pop
    self._UnlockedReleaseDRBDMinors(instance.name)
1404 e8e079f3 Dimitris Aragiorgis
    self._UnlockedCommitTemporaryIps(ec_id)
1405 a8083063 Iustin Pop
    self._WriteConfig()
1406 a8083063 Iustin Pop
1407 0debfb35 Guido Trotter
  def _EnsureUUID(self, item, ec_id):
1408 430b923c Iustin Pop
    """Ensures a given object has a valid UUID.
1409 430b923c Iustin Pop

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

1413 430b923c Iustin Pop
    """
1414 430b923c Iustin Pop
    if not item.uuid:
1415 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1416 be0fc05d Iustin Pop
    elif item.uuid in self._AllIDs(include_temporary=True):
1417 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1418 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1419 430b923c Iustin Pop
1420 6a408fb2 Iustin Pop
  def _SetInstanceStatus(self, instance_name, status):
1421 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1422 a8083063 Iustin Pop

1423 a8083063 Iustin Pop
    """
1424 2e04d454 Agata Murawska
    assert status in constants.ADMINST_ALL, \
1425 0d68c45d Iustin Pop
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1426 a8083063 Iustin Pop
1427 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1428 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1429 3ecf6786 Iustin Pop
                                      instance_name)
1430 a8083063 Iustin Pop
    instance = self._config_data.instances[instance_name]
1431 9ca8a7c5 Agata Murawska
    if instance.admin_state != status:
1432 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1433 b989e85d Iustin Pop
      instance.serial_no += 1
1434 d693c864 Iustin Pop
      instance.mtime = time.time()
1435 455a3445 Iustin Pop
      self._WriteConfig()
1436 a8083063 Iustin Pop
1437 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1438 6a408fb2 Iustin Pop
  def MarkInstanceUp(self, instance_name):
1439 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1440 6a408fb2 Iustin Pop

1441 6a408fb2 Iustin Pop
    """
1442 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_UP)
1443 57de31c0 Agata Murawska
1444 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1445 57de31c0 Agata Murawska
  def MarkInstanceOffline(self, instance_name):
1446 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1447 57de31c0 Agata Murawska

1448 57de31c0 Agata Murawska
    """
1449 57de31c0 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_OFFLINE)
1450 6a408fb2 Iustin Pop
1451 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1452 a8083063 Iustin Pop
  def RemoveInstance(self, instance_name):
1453 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1454 a8083063 Iustin Pop

1455 a8083063 Iustin Pop
    """
1456 a8083063 Iustin Pop
    if instance_name not in self._config_data.instances:
1457 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1458 f396ad8c Vangelis Koukis
1459 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1460 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1461 f396ad8c Vangelis Koukis
    inst = self._config_data.instances[instance_name]
1462 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1463 f396ad8c Vangelis Koukis
    if network_port is not None:
1464 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1465 f396ad8c Vangelis Koukis
1466 ced51149 Dimitris Aragiorgis
    instance = self._UnlockedGetInstanceInfo(instance_name)
1467 ced51149 Dimitris Aragiorgis
1468 ced51149 Dimitris Aragiorgis
    for nic in instance.nics:
1469 9394f4d1 Dimitris Aragiorgis
      if nic.network and nic.ip:
1470 1b68f268 Helga Velroyen
        # Return all IP addresses to the respective address pools
1471 9394f4d1 Dimitris Aragiorgis
        self._UnlockedCommitIp(constants.RELEASE_ACTION, nic.network, nic.ip)
1472 ced51149 Dimitris Aragiorgis
1473 a8083063 Iustin Pop
    del self._config_data.instances[instance_name]
1474 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1475 a8083063 Iustin Pop
    self._WriteConfig()
1476 a8083063 Iustin Pop
1477 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1478 fc95f88f Iustin Pop
  def RenameInstance(self, old_name, new_name):
1479 fc95f88f Iustin Pop
    """Rename an instance.
1480 fc95f88f Iustin Pop

1481 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1482 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1483 fc95f88f Iustin Pop
    rename.
1484 fc95f88f Iustin Pop

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

1515 a8083063 Iustin Pop
    """
1516 2e04d454 Agata Murawska
    self._SetInstanceStatus(instance_name, constants.ADMINST_DOWN)
1517 a8083063 Iustin Pop
1518 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1519 94bbfece Iustin Pop
    """Get the list of instances.
1520 94bbfece Iustin Pop

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

1523 94bbfece Iustin Pop
    """
1524 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1525 94bbfece Iustin Pop
1526 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1527 a8083063 Iustin Pop
  def GetInstanceList(self):
1528 a8083063 Iustin Pop
    """Get the list of instances.
1529 a8083063 Iustin Pop

1530 c41eea6e Iustin Pop
    @return: array of instances, ex. ['instance2.example.com',
1531 c41eea6e Iustin Pop
        'instance1.example.com']
1532 a8083063 Iustin Pop

1533 a8083063 Iustin Pop
    """
1534 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1535 a8083063 Iustin Pop
1536 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1537 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1538 a8083063 Iustin Pop

1539 a8083063 Iustin Pop
    """
1540 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetInstanceList}
1541 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetInstanceList())
1542 a8083063 Iustin Pop
1543 94bbfece Iustin Pop
  def _UnlockedGetInstanceInfo(self, instance_name):
1544 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1545 94bbfece Iustin Pop

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

1548 94bbfece Iustin Pop
    """
1549 94bbfece Iustin Pop
    if instance_name not in self._config_data.instances:
1550 94bbfece Iustin Pop
      return None
1551 94bbfece Iustin Pop
1552 94bbfece Iustin Pop
    return self._config_data.instances[instance_name]
1553 94bbfece Iustin Pop
1554 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1555 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
1556 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1557 a8083063 Iustin Pop

1558 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1559 a8083063 Iustin Pop
    an instance are taken from the live systems.
1560 a8083063 Iustin Pop

1561 c41eea6e Iustin Pop
    @param instance_name: name of the instance, e.g.
1562 c41eea6e Iustin Pop
        I{instance1.example.com}
1563 a8083063 Iustin Pop

1564 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1565 c41eea6e Iustin Pop
    @return: the instance object
1566 a8083063 Iustin Pop

1567 a8083063 Iustin Pop
    """
1568 94bbfece Iustin Pop
    return self._UnlockedGetInstanceInfo(instance_name)
1569 a8083063 Iustin Pop
1570 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1571 2674690b Michael Hanselmann
  def GetInstanceNodeGroups(self, instance_name, primary_only=False):
1572 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1573 2674690b Michael Hanselmann

1574 2674690b Michael Hanselmann
    @rtype: frozenset
1575 2674690b Michael Hanselmann

1576 2674690b Michael Hanselmann
    """
1577 2674690b Michael Hanselmann
    instance = self._UnlockedGetInstanceInfo(instance_name)
1578 2674690b Michael Hanselmann
    if not instance:
1579 2674690b Michael Hanselmann
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1580 2674690b Michael Hanselmann
1581 2674690b Michael Hanselmann
    if primary_only:
1582 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1583 2674690b Michael Hanselmann
    else:
1584 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1585 2674690b Michael Hanselmann
1586 2674690b Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(node_name).group
1587 2674690b Michael Hanselmann
                     for node_name in nodes)
1588 2674690b Michael Hanselmann
1589 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1590 922610c9 Dimitris Aragiorgis
  def GetInstanceNetworks(self, instance_name):
1591 922610c9 Dimitris Aragiorgis
    """Returns set of network UUIDs for instance's nics.
1592 922610c9 Dimitris Aragiorgis

1593 922610c9 Dimitris Aragiorgis
    @rtype: frozenset
1594 922610c9 Dimitris Aragiorgis

1595 922610c9 Dimitris Aragiorgis
    """
1596 922610c9 Dimitris Aragiorgis
    instance = self._UnlockedGetInstanceInfo(instance_name)
1597 922610c9 Dimitris Aragiorgis
    if not instance:
1598 922610c9 Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
1599 922610c9 Dimitris Aragiorgis
1600 922610c9 Dimitris Aragiorgis
    networks = set()
1601 922610c9 Dimitris Aragiorgis
    for nic in instance.nics:
1602 922610c9 Dimitris Aragiorgis
      if nic.network:
1603 922610c9 Dimitris Aragiorgis
        networks.add(nic.network)
1604 922610c9 Dimitris Aragiorgis
1605 922610c9 Dimitris Aragiorgis
    return frozenset(networks)
1606 922610c9 Dimitris Aragiorgis
1607 922610c9 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
1608 71333cb9 Iustin Pop
  def GetMultiInstanceInfo(self, instances):
1609 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1610 71333cb9 Iustin Pop

1611 71333cb9 Iustin Pop
    @param instances: list of instance names
1612 71333cb9 Iustin Pop
    @rtype: list
1613 71333cb9 Iustin Pop
    @return: list of tuples (instance, instance_info), where
1614 71333cb9 Iustin Pop
        instance_info is what would GetInstanceInfo return for the
1615 71333cb9 Iustin Pop
        node, while keeping the original order
1616 71333cb9 Iustin Pop

1617 71333cb9 Iustin Pop
    """
1618 71333cb9 Iustin Pop
    return [(name, self._UnlockedGetInstanceInfo(name)) for name in instances]
1619 71333cb9 Iustin Pop
1620 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1621 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1622 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1623 0b2de758 Iustin Pop

1624 0b2de758 Iustin Pop
    @rtype: dict
1625 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1626 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1627 0b2de758 Iustin Pop

1628 0b2de758 Iustin Pop
    """
1629 64d3bd52 Guido Trotter
    my_dict = dict([(instance, self._UnlockedGetInstanceInfo(instance))
1630 64d3bd52 Guido Trotter
                    for instance in self._UnlockedGetInstanceList()])
1631 0b2de758 Iustin Pop
    return my_dict
1632 0b2de758 Iustin Pop
1633 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1634 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1635 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1636 cc19798f Michael Hanselmann

1637 cc19798f Michael Hanselmann
    @type filter_fn: callable
1638 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1639 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1640 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1641 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1642 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1643 cc19798f Michael Hanselmann

1644 cc19798f Michael Hanselmann
    """
1645 cc19798f Michael Hanselmann
    return dict((name, inst)
1646 cc19798f Michael Hanselmann
                for (name, inst) in self._config_data.instances.items()
1647 cc19798f Michael Hanselmann
                if filter_fn(inst))
1648 cc19798f Michael Hanselmann
1649 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1650 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1651 a8083063 Iustin Pop
    """Add a node to the configuration.
1652 a8083063 Iustin Pop

1653 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1654 c41eea6e Iustin Pop
    @param node: a Node instance
1655 a8083063 Iustin Pop

1656 a8083063 Iustin Pop
    """
1657 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1658 d8470559 Michael Hanselmann
1659 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1660 430b923c Iustin Pop
1661 b989e85d Iustin Pop
    node.serial_no = 1
1662 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1663 f936c153 Iustin Pop
    self._UnlockedAddNodeToGroup(node.name, node.group)
1664 a8083063 Iustin Pop
    self._config_data.nodes[node.name] = node
1665 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1666 a8083063 Iustin Pop
    self._WriteConfig()
1667 a8083063 Iustin Pop
1668 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1669 a8083063 Iustin Pop
  def RemoveNode(self, node_name):
1670 a8083063 Iustin Pop
    """Remove a node from the configuration.
1671 a8083063 Iustin Pop

1672 a8083063 Iustin Pop
    """
1673 099c52ad Iustin Pop
    logging.info("Removing node %s from configuration", node_name)
1674 d8470559 Michael Hanselmann
1675 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1676 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown node '%s'" % node_name)
1677 a8083063 Iustin Pop
1678 190e3cb6 Guido Trotter
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name])
1679 a8083063 Iustin Pop
    del self._config_data.nodes[node_name]
1680 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1681 a8083063 Iustin Pop
    self._WriteConfig()
1682 a8083063 Iustin Pop
1683 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1684 fe698b38 Michael Hanselmann
    """Attempt to expand an incomplete node name.
1685 a8083063 Iustin Pop

1686 a8083063 Iustin Pop
    """
1687 fe698b38 Michael Hanselmann
    # Locking is done in L{ConfigWriter.GetNodeList}
1688 fe698b38 Michael Hanselmann
    return _MatchNameComponentIgnoreCase(short_name, self.GetNodeList())
1689 a8083063 Iustin Pop
1690 f78ede4e Guido Trotter
  def _UnlockedGetNodeInfo(self, node_name):
1691 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1692 a8083063 Iustin Pop

1693 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1694 c41eea6e Iustin Pop
    held.
1695 f78ede4e Guido Trotter

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

1698 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1699 c41eea6e Iustin Pop
    @return: the node object
1700 a8083063 Iustin Pop

1701 a8083063 Iustin Pop
    """
1702 a8083063 Iustin Pop
    if node_name not in self._config_data.nodes:
1703 a8083063 Iustin Pop
      return None
1704 a8083063 Iustin Pop
1705 a8083063 Iustin Pop
    return self._config_data.nodes[node_name]
1706 a8083063 Iustin Pop
1707 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1708 f78ede4e Guido Trotter
  def GetNodeInfo(self, node_name):
1709 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1710 f78ede4e Guido Trotter

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

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

1715 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1716 c41eea6e Iustin Pop
    @return: the node object
1717 f78ede4e Guido Trotter

1718 f78ede4e Guido Trotter
    """
1719 f78ede4e Guido Trotter
    return self._UnlockedGetNodeInfo(node_name)
1720 f78ede4e Guido Trotter
1721 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1722 8bf9e9a5 Iustin Pop
  def GetNodeInstances(self, node_name):
1723 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1724 8bf9e9a5 Iustin Pop

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

1727 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1728 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1729 8bf9e9a5 Iustin Pop

1730 8bf9e9a5 Iustin Pop
    """
1731 8bf9e9a5 Iustin Pop
    pri = []
1732 8bf9e9a5 Iustin Pop
    sec = []
1733 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1734 8bf9e9a5 Iustin Pop
      if inst.primary_node == node_name:
1735 8bf9e9a5 Iustin Pop
        pri.append(inst.name)
1736 8bf9e9a5 Iustin Pop
      if node_name in inst.secondary_nodes:
1737 8bf9e9a5 Iustin Pop
        sec.append(inst.name)
1738 8bf9e9a5 Iustin Pop
    return (pri, sec)
1739 8bf9e9a5 Iustin Pop
1740 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1741 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1742 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1743 c71b049c Michael Hanselmann

1744 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1745 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1746 c71b049c Michael Hanselmann
    @rtype: frozenset
1747 c71b049c Michael Hanselmann
    @return: List of instance names in node group
1748 c71b049c Michael Hanselmann

1749 c71b049c Michael Hanselmann
    """
1750 c71b049c Michael Hanselmann
    if primary_only:
1751 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1752 c71b049c Michael Hanselmann
    else:
1753 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1754 c71b049c Michael Hanselmann
1755 c71b049c Michael Hanselmann
    return frozenset(inst.name
1756 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1757 c71b049c Michael Hanselmann
                     for node_name in nodes_fn(inst)
1758 c71b049c Michael Hanselmann
                     if self._UnlockedGetNodeInfo(node_name).group == uuid)
1759 c71b049c Michael Hanselmann
1760 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1761 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1762 a8083063 Iustin Pop

1763 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1764 c41eea6e Iustin Pop
    held.
1765 c41eea6e Iustin Pop

1766 c41eea6e Iustin Pop
    @rtype: list
1767 f78ede4e Guido Trotter

1768 a8083063 Iustin Pop
    """
1769 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1770 a8083063 Iustin Pop
1771 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1772 f78ede4e Guido Trotter
  def GetNodeList(self):
1773 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1774 f78ede4e Guido Trotter

1775 f78ede4e Guido Trotter
    """
1776 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1777 f78ede4e Guido Trotter
1778 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1779 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1780 94a02bb5 Iustin Pop

1781 94a02bb5 Iustin Pop
    """
1782 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1783 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1784 94a02bb5 Iustin Pop
    return [node.name for node in all_nodes if not node.offline]
1785 94a02bb5 Iustin Pop
1786 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1787 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1788 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1789 6819dc49 Iustin Pop

1790 6819dc49 Iustin Pop
    """
1791 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1792 6819dc49 Iustin Pop
1793 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1794 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1795 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1796 075b62ca Iustin Pop

1797 075b62ca Iustin Pop
    """
1798 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1799 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1800 075b62ca Iustin Pop
    return [node.name for node in all_nodes if node.vm_capable]
1801 075b62ca Iustin Pop
1802 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1803 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1804 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1805 8bf9e9a5 Iustin Pop

1806 8bf9e9a5 Iustin Pop
    """
1807 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1808 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1809 8bf9e9a5 Iustin Pop
    return [node.name for node in all_nodes if not node.vm_capable]
1810 8bf9e9a5 Iustin Pop
1811 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1812 f5eaa3c1 Iustin Pop
  def GetMultiNodeInfo(self, nodes):
1813 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1814 f5eaa3c1 Iustin Pop

1815 f5eaa3c1 Iustin Pop
    @param nodes: list of node names
1816 f5eaa3c1 Iustin Pop
    @rtype: list
1817 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1818 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1819 f5eaa3c1 Iustin Pop
        order
1820 f5eaa3c1 Iustin Pop

1821 f5eaa3c1 Iustin Pop
    """
1822 f5eaa3c1 Iustin Pop
    return [(name, self._UnlockedGetNodeInfo(name)) for name in nodes]
1823 f5eaa3c1 Iustin Pop
1824 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1825 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
1826 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
1827 d65e5776 Iustin Pop

1828 d65e5776 Iustin Pop
    @rtype: dict
1829 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
1830 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
1831 d65e5776 Iustin Pop

1832 d65e5776 Iustin Pop
    """
1833 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
1834 ee14d800 Michael Hanselmann
1835 ee14d800 Michael Hanselmann
  def _UnlockedGetAllNodesInfo(self):
1836 ee14d800 Michael Hanselmann
    """Gets configuration of all nodes.
1837 ee14d800 Michael Hanselmann

1838 ee14d800 Michael Hanselmann
    @note: See L{GetAllNodesInfo}
1839 ee14d800 Michael Hanselmann

1840 ee14d800 Michael Hanselmann
    """
1841 ee14d800 Michael Hanselmann
    return dict([(node, self._UnlockedGetNodeInfo(node))
1842 ee14d800 Michael Hanselmann
                 for node in self._UnlockedGetNodeList()])
1843 d65e5776 Iustin Pop
1844 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1845 9d5b1371 Michael Hanselmann
  def GetNodeGroupsFromNodes(self, nodes):
1846 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
1847 9d5b1371 Michael Hanselmann

1848 9d5b1371 Michael Hanselmann
    @type nodes: list of string
1849 9d5b1371 Michael Hanselmann
    @param nodes: List of node names
1850 9d5b1371 Michael Hanselmann
    @rtype: frozenset
1851 9d5b1371 Michael Hanselmann

1852 9d5b1371 Michael Hanselmann
    """
1853 9d5b1371 Michael Hanselmann
    return frozenset(self._UnlockedGetNodeInfo(name).group for name in nodes)
1854 9d5b1371 Michael Hanselmann
1855 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
1856 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
1857 ec0292f1 Iustin Pop

1858 23f06b2b Iustin Pop
    @type exceptions: list
1859 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1860 ec0292f1 Iustin Pop
    @rtype: tuple
1861 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
1862 ec0292f1 Iustin Pop

1863 ec0292f1 Iustin Pop
    """
1864 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
1865 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
1866 23f06b2b Iustin Pop
      if exceptions and node.name in exceptions:
1867 23f06b2b Iustin Pop
        continue
1868 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
1869 ec0292f1 Iustin Pop
        mc_max += 1
1870 ec0292f1 Iustin Pop
      if node.master_candidate:
1871 ec0292f1 Iustin Pop
        mc_now += 1
1872 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
1873 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
1874 ec0292f1 Iustin Pop
1875 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1876 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
1877 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
1878 ec0292f1 Iustin Pop

1879 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
1880 ec0292f1 Iustin Pop

1881 23f06b2b Iustin Pop
    @type exceptions: list
1882 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
1883 ec0292f1 Iustin Pop
    @rtype: tuple
1884 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
1885 ec0292f1 Iustin Pop

1886 ec0292f1 Iustin Pop
    """
1887 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
1888 ec0292f1 Iustin Pop
1889 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
1890 44485f49 Guido Trotter
  def MaintainCandidatePool(self, exceptions):
1891 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
1892 ec0292f1 Iustin Pop

1893 44485f49 Guido Trotter
    @type exceptions: list
1894 44485f49 Guido Trotter
    @param exceptions: if passed, list of nodes that should be ignored
1895 ec0292f1 Iustin Pop
    @rtype: list
1896 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
1897 ec0292f1 Iustin Pop

1898 ec0292f1 Iustin Pop
    """
1899 44485f49 Guido Trotter
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(exceptions)
1900 ec0292f1 Iustin Pop
    mod_list = []
1901 ec0292f1 Iustin Pop
    if mc_now < mc_max:
1902 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
1903 ec0292f1 Iustin Pop
      random.shuffle(node_list)
1904 ec0292f1 Iustin Pop
      for name in node_list:
1905 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
1906 ec0292f1 Iustin Pop
          break
1907 ec0292f1 Iustin Pop
        node = self._config_data.nodes[name]
1908 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
1909 490acd18 Iustin Pop
            node.name in exceptions or not node.master_capable):
1910 ec0292f1 Iustin Pop
          continue
1911 ee513a66 Iustin Pop
        mod_list.append(node)
1912 ec0292f1 Iustin Pop
        node.master_candidate = True
1913 ec0292f1 Iustin Pop
        node.serial_no += 1
1914 ec0292f1 Iustin Pop
        mc_now += 1
1915 ec0292f1 Iustin Pop
      if mc_now != mc_max:
1916 ec0292f1 Iustin Pop
        # this should not happen
1917 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
1918 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
1919 ec0292f1 Iustin Pop
      if mod_list:
1920 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
1921 ec0292f1 Iustin Pop
        self._WriteConfig()
1922 ec0292f1 Iustin Pop
1923 ec0292f1 Iustin Pop
    return mod_list
1924 ec0292f1 Iustin Pop
1925 190e3cb6 Guido Trotter
  def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid):
1926 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
1927 190e3cb6 Guido Trotter

1928 190e3cb6 Guido Trotter
    """
1929 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
1930 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
1931 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
1932 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
1933 190e3cb6 Guido Trotter
      # is not found anymore.
1934 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
1935 190e3cb6 Guido Trotter
    if node_name not in self._config_data.nodegroups[nodegroup_uuid].members:
1936 190e3cb6 Guido Trotter
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_name)
1937 190e3cb6 Guido Trotter
1938 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
1939 190e3cb6 Guido Trotter
    """Remove a given node from its group.
1940 190e3cb6 Guido Trotter

1941 190e3cb6 Guido Trotter
    """
1942 f936c153 Iustin Pop
    nodegroup = node.group
1943 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
1944 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
1945 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1946 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
1947 190e3cb6 Guido Trotter
    if node.name not in nodegroup_obj.members:
1948 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
1949 190e3cb6 Guido Trotter
                      " (while being removed from it)", node.name, nodegroup)
1950 190e3cb6 Guido Trotter
    else:
1951 190e3cb6 Guido Trotter
      nodegroup_obj.members.remove(node.name)
1952 190e3cb6 Guido Trotter
1953 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
1954 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
1955 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
1956 54c31fd3 Michael Hanselmann

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

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

2029 a8083063 Iustin Pop
    """
2030 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
2031 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
2032 a8083063 Iustin Pop
2033 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
2034 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
2035 76d5d3a3 Iustin Pop

2036 76d5d3a3 Iustin Pop
    """
2037 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
2038 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
2039 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
2040 e0519c34 Dimitris Aragiorgis
            self._config_data.networks.values() +
2041 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
2042 76d5d3a3 Iustin Pop
2043 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
2044 a8083063 Iustin Pop
    """Read the config data from disk.
2045 a8083063 Iustin Pop

2046 a8083063 Iustin Pop
    """
2047 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
2048 13998ef2 Michael Hanselmann
2049 a8083063 Iustin Pop
    try:
2050 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
2051 13998ef2 Michael Hanselmann
    except Exception, err:
2052 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
2053 5b263ed7 Michael Hanselmann
2054 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
2055 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
2056 5b263ed7 Michael Hanselmann
2057 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
2058 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
2059 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
2060 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
2061 90d726a8 Iustin Pop
2062 eb180fe2 Iustin Pop
    if data.cluster.master_node != self._my_hostname and not accept_foreign:
2063 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
2064 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
2065 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
2066 eb180fe2 Iustin Pop
             (data.cluster.master_node, self._my_hostname))
2067 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
2068 eb180fe2 Iustin Pop
2069 a8083063 Iustin Pop
    self._config_data = data
2070 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
2071 0779e3aa Iustin Pop
    # ssconf update
2072 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
2073 a8083063 Iustin Pop
2074 45f62156 Bernardo Dal Seno
    # Upgrade configuration if needed
2075 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
2076 76d5d3a3 Iustin Pop
2077 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
2078 bd407597 Iustin Pop
2079 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
2080 45f62156 Bernardo Dal Seno
    """Run any upgrade steps.
2081 76d5d3a3 Iustin Pop

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

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

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

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

2136 a8083063 Iustin Pop
    """
2137 a8083063 Iustin Pop
    if self._offline:
2138 a8083063 Iustin Pop
      return True
2139 a4eae71f Michael Hanselmann
2140 a8083063 Iustin Pop
    bad = False
2141 a8083063 Iustin Pop
2142 6a5b8b4b Iustin Pop
    node_list = []
2143 6a5b8b4b Iustin Pop
    addr_list = []
2144 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
2145 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
2146 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
2147 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
2148 6b294c53 Iustin Pop
    # in between
2149 6a5b8b4b Iustin Pop
    for node_name in self._UnlockedGetNodeList():
2150 6a5b8b4b Iustin Pop
      if node_name == myhostname:
2151 6a5b8b4b Iustin Pop
        continue
2152 6a5b8b4b Iustin Pop
      node_info = self._UnlockedGetNodeInfo(node_name)
2153 6a5b8b4b Iustin Pop
      if not node_info.master_candidate:
2154 6a5b8b4b Iustin Pop
        continue
2155 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
2156 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
2157 6b294c53 Iustin Pop
2158 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
2159 415a7304 Michael Hanselmann
    result = \
2160 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
2161 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
2162 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
2163 1b54fc6c Guido Trotter
      if msg:
2164 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
2165 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
2166 1b54fc6c Guido Trotter
        logging.error(msg)
2167 a4eae71f Michael Hanselmann
2168 a4eae71f Michael Hanselmann
        if feedback_fn:
2169 a4eae71f Michael Hanselmann
          feedback_fn(msg)
2170 a4eae71f Michael Hanselmann
2171 a8083063 Iustin Pop
        bad = True
2172 a4eae71f Michael Hanselmann
2173 a8083063 Iustin Pop
    return not bad
2174 a8083063 Iustin Pop
2175 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
2176 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
2177 a8083063 Iustin Pop

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

2238 054596f0 Iustin Pop
    @rtype: dict
2239 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2240 054596f0 Iustin Pop
        associated value
2241 054596f0 Iustin Pop

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

2313 d367b66c Manuel Franceschini
    """
2314 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2315 d367b66c Manuel Franceschini
2316 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2317 a8083063 Iustin Pop
  def GetVGName(self):
2318 a8083063 Iustin Pop
    """Return the volume group name.
2319 a8083063 Iustin Pop

2320 a8083063 Iustin Pop
    """
2321 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2322 a8083063 Iustin Pop
2323 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2324 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2325 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2326 89ff8e15 Manuel Franceschini

2327 89ff8e15 Manuel Franceschini
    """
2328 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2329 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2330 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2331 89ff8e15 Manuel Franceschini
2332 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2333 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2334 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2335 9e33896b Luca Bigliardi

2336 9e33896b Luca Bigliardi
    """
2337 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2338 9e33896b Luca Bigliardi
2339 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2340 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2341 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2342 9e33896b Luca Bigliardi

2343 9e33896b Luca Bigliardi
    """
2344 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2345 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2346 9e33896b Luca Bigliardi
    self._WriteConfig()
2347 9e33896b Luca Bigliardi
2348 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2349 a8083063 Iustin Pop
  def GetMACPrefix(self):
2350 a8083063 Iustin Pop
    """Return the mac prefix.
2351 a8083063 Iustin Pop

2352 a8083063 Iustin Pop
    """
2353 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2354 62779dd0 Iustin Pop
2355 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2356 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2357 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2358 62779dd0 Iustin Pop

2359 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2360 c41eea6e Iustin Pop
    @return: the cluster object
2361 62779dd0 Iustin Pop

2362 62779dd0 Iustin Pop
    """
2363 62779dd0 Iustin Pop
    return self._config_data.cluster
2364 e00fb268 Iustin Pop
2365 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2366 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2367 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2368 51cb1581 Luca Bigliardi

2369 51cb1581 Luca Bigliardi
    """
2370 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2371 51cb1581 Luca Bigliardi
2372 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2373 d1547283 Dimitris Aragiorgis
  def Update(self, target, feedback_fn, ec_id=None):
2374 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2375 e00fb268 Iustin Pop

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

2382 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2383 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2384 c41eea6e Iustin Pop
        the cluster
2385 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2386 c41eea6e Iustin Pop

2387 e00fb268 Iustin Pop
    """
2388 e00fb268 Iustin Pop
    if self._config_data is None:
2389 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2390 3ecf6786 Iustin Pop
                                   " cannot save.")
2391 f34901f8 Iustin Pop
    update_serial = False
2392 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2393 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2394 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2395 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2396 f34901f8 Iustin Pop
      update_serial = True
2397 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2398 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2399 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2400 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2401 1e0d3321 Dimitris Aragiorgis
    elif isinstance(target, objects.Network):
2402 1e0d3321 Dimitris Aragiorgis
      test = target in self._config_data.networks.values()
2403 e00fb268 Iustin Pop
    else:
2404 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2405 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2406 e00fb268 Iustin Pop
    if not test:
2407 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2408 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2409 f34901f8 Iustin Pop
    target.serial_no += 1
2410 d693c864 Iustin Pop
    target.mtime = now = time.time()
2411 f34901f8 Iustin Pop
2412 cff4c037 Iustin Pop
    if update_serial:
2413 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2414 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2415 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2416 b989e85d Iustin Pop
2417 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2418 61cf6b5e Iustin Pop
      self._UnlockedReleaseDRBDMinors(target.name)
2419 61cf6b5e Iustin Pop
2420 d1547283 Dimitris Aragiorgis
    if ec_id is not None:
2421 d1547283 Dimitris Aragiorgis
      # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams
2422 d1547283 Dimitris Aragiorgis
      self._UnlockedCommitTemporaryIps(ec_id)
2423 d1547283 Dimitris Aragiorgis
2424 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2425 73064714 Guido Trotter
2426 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2427 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2428 73064714 Guido Trotter
    """Drop per-execution-context reservations
2429 73064714 Guido Trotter

2430 73064714 Guido Trotter
    """
2431 d8aee57e Iustin Pop
    for rm in self._all_rms:
2432 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)
2433 6c0a75db Dimitris Aragiorgis
2434 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2435 6c0a75db Dimitris Aragiorgis
  def GetAllNetworksInfo(self):
2436 6a94d553 Dimitris Aragiorgis
    """Get configuration info of all the networks.
2437 6c0a75db Dimitris Aragiorgis

2438 6c0a75db Dimitris Aragiorgis
    """
2439 6c0a75db Dimitris Aragiorgis
    return dict(self._config_data.networks)
2440 6c0a75db Dimitris Aragiorgis
2441 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetworkList(self):
2442 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2443 6c0a75db Dimitris Aragiorgis

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

2446 6c0a75db Dimitris Aragiorgis
    """
2447 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks.keys()
2448 6c0a75db Dimitris Aragiorgis
2449 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2450 6c0a75db Dimitris Aragiorgis
  def GetNetworkList(self):
2451 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2452 6c0a75db Dimitris Aragiorgis

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

2455 6c0a75db Dimitris Aragiorgis
    """
2456 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetworkList()
2457 6c0a75db Dimitris Aragiorgis
2458 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2459 6c0a75db Dimitris Aragiorgis
  def GetNetworkNames(self):
2460 6c0a75db Dimitris Aragiorgis
    """Get a list of network names
2461 6c0a75db Dimitris Aragiorgis

2462 6c0a75db Dimitris Aragiorgis
    """
2463 beb81ea5 Dimitris Aragiorgis
    names = [net.name
2464 beb81ea5 Dimitris Aragiorgis
             for net in self._config_data.networks.values()]
2465 6c0a75db Dimitris Aragiorgis
    return names
2466 6c0a75db Dimitris Aragiorgis
2467 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetwork(self, uuid):
2468 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2469 6c0a75db Dimitris Aragiorgis

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

2472 6c0a75db Dimitris Aragiorgis
    """
2473 6c0a75db Dimitris Aragiorgis
    if uuid not in self._config_data.networks:
2474 6c0a75db Dimitris Aragiorgis
      return None
2475 6c0a75db Dimitris Aragiorgis
2476 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks[uuid]
2477 6c0a75db Dimitris Aragiorgis
2478 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2479 6c0a75db Dimitris Aragiorgis
  def GetNetwork(self, uuid):
2480 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2481 6c0a75db Dimitris Aragiorgis

2482 6c0a75db Dimitris Aragiorgis
    It takes the information from the configuration file.
2483 6c0a75db Dimitris Aragiorgis

2484 6c0a75db Dimitris Aragiorgis
    @param uuid: UUID of the network
2485 6c0a75db Dimitris Aragiorgis

2486 6c0a75db Dimitris Aragiorgis
    @rtype: L{objects.Network}
2487 6c0a75db Dimitris Aragiorgis
    @return: the network object
2488 6c0a75db Dimitris Aragiorgis

2489 6c0a75db Dimitris Aragiorgis
    """
2490 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetwork(uuid)
2491 6c0a75db Dimitris Aragiorgis
2492 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2493 6c0a75db Dimitris Aragiorgis
  def AddNetwork(self, net, ec_id, check_uuid=True):
2494 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2495 6c0a75db Dimitris Aragiorgis

2496 6c0a75db Dimitris Aragiorgis
    @type net: L{objects.Network}
2497 6c0a75db Dimitris Aragiorgis
    @param net: the Network object to add
2498 6c0a75db Dimitris Aragiorgis
    @type ec_id: string
2499 6c0a75db Dimitris Aragiorgis
    @param ec_id: unique id for the job to use when creating a missing UUID
2500 6c0a75db Dimitris Aragiorgis

2501 6c0a75db Dimitris Aragiorgis
    """
2502 6c0a75db Dimitris Aragiorgis
    self._UnlockedAddNetwork(net, ec_id, check_uuid)
2503 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2504 6c0a75db Dimitris Aragiorgis
2505 6c0a75db Dimitris Aragiorgis
  def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
2506 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2507 6c0a75db Dimitris Aragiorgis

2508 6c0a75db Dimitris Aragiorgis
    """
2509 6c0a75db Dimitris Aragiorgis
    logging.info("Adding network %s to configuration", net.name)
2510 6c0a75db Dimitris Aragiorgis
2511 6c0a75db Dimitris Aragiorgis
    if check_uuid:
2512 6c0a75db Dimitris Aragiorgis
      self._EnsureUUID(net, ec_id)
2513 6c0a75db Dimitris Aragiorgis
2514 6c0a75db Dimitris Aragiorgis
    net.serial_no = 1
2515 6c0a75db Dimitris Aragiorgis
    self._config_data.networks[net.uuid] = net
2516 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2517 6c0a75db Dimitris Aragiorgis
2518 6c0a75db Dimitris Aragiorgis
  def _UnlockedLookupNetwork(self, target):
2519 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2520 6c0a75db Dimitris Aragiorgis

2521 6c0a75db Dimitris Aragiorgis
    @type target: string
2522 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2523 6c0a75db Dimitris Aragiorgis
    @rtype: string
2524 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2525 6c0a75db Dimitris Aragiorgis
    @raises errors.OpPrereqError: when the target network cannot be found
2526 6c0a75db Dimitris Aragiorgis

2527 6c0a75db Dimitris Aragiorgis
    """
2528 9394f4d1 Dimitris Aragiorgis
    if target is None:
2529 9394f4d1 Dimitris Aragiorgis
      return None
2530 6c0a75db Dimitris Aragiorgis
    if target in self._config_data.networks:
2531 6c0a75db Dimitris Aragiorgis
      return target
2532 6c0a75db Dimitris Aragiorgis
    for net in self._config_data.networks.values():
2533 6c0a75db Dimitris Aragiorgis
      if net.name == target:
2534 6c0a75db Dimitris Aragiorgis
        return net.uuid
2535 1b68f268 Helga Velroyen
    raise errors.OpPrereqError("Network '%s' not found" % target,
2536 1b68f268 Helga Velroyen
                               errors.ECODE_NOENT)
2537 6c0a75db Dimitris Aragiorgis
2538 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2539 6c0a75db Dimitris Aragiorgis
  def LookupNetwork(self, target):
2540 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2541 6c0a75db Dimitris Aragiorgis

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

2544 6c0a75db Dimitris Aragiorgis
    @type target: string
2545 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2546 6c0a75db Dimitris Aragiorgis
    @rtype: string
2547 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2548 6c0a75db Dimitris Aragiorgis

2549 6c0a75db Dimitris Aragiorgis
    """
2550 6c0a75db Dimitris Aragiorgis
    return self._UnlockedLookupNetwork(target)
2551 6c0a75db Dimitris Aragiorgis
2552 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2553 6c0a75db Dimitris Aragiorgis
  def RemoveNetwork(self, network_uuid):
2554 6c0a75db Dimitris Aragiorgis
    """Remove a network from the configuration.
2555 6c0a75db Dimitris Aragiorgis

2556 6c0a75db Dimitris Aragiorgis
    @type network_uuid: string
2557 6c0a75db Dimitris Aragiorgis
    @param network_uuid: the UUID of the network to remove
2558 6c0a75db Dimitris Aragiorgis

2559 6c0a75db Dimitris Aragiorgis
    """
2560 6c0a75db Dimitris Aragiorgis
    logging.info("Removing network %s from configuration", network_uuid)
2561 6c0a75db Dimitris Aragiorgis
2562 6c0a75db Dimitris Aragiorgis
    if network_uuid not in self._config_data.networks:
2563 6c0a75db Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
2564 6c0a75db Dimitris Aragiorgis
2565 6c0a75db Dimitris Aragiorgis
    del self._config_data.networks[network_uuid]
2566 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2567 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2568 ad4a9ae7 Dimitris Aragiorgis
2569 9ccacbc8 Dimitris Aragiorgis
  def _UnlockedGetGroupNetParams(self, net_uuid, node):
2570 ad4a9ae7 Dimitris Aragiorgis
    """Get the netparams (mode, link) of a network.
2571 ad4a9ae7 Dimitris Aragiorgis

2572 ad4a9ae7 Dimitris Aragiorgis
    Get a network's netparams for a given node.
2573 ad4a9ae7 Dimitris Aragiorgis

2574 9ccacbc8 Dimitris Aragiorgis
    @type net_uuid: string
2575 9ccacbc8 Dimitris Aragiorgis
    @param net_uuid: network uuid
2576 ad4a9ae7 Dimitris Aragiorgis
    @type node: string
2577 ad4a9ae7 Dimitris Aragiorgis
    @param node: node name
2578 ad4a9ae7 Dimitris Aragiorgis
    @rtype: dict or None
2579 ad4a9ae7 Dimitris Aragiorgis
    @return: netparams
2580 ad4a9ae7 Dimitris Aragiorgis

2581 ad4a9ae7 Dimitris Aragiorgis
    """
2582 ad4a9ae7 Dimitris Aragiorgis
    node_info = self._UnlockedGetNodeInfo(node)
2583 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2584 ad4a9ae7 Dimitris Aragiorgis
    netparams = nodegroup_info.networks.get(net_uuid, None)
2585 ad4a9ae7 Dimitris Aragiorgis
2586 ad4a9ae7 Dimitris Aragiorgis
    return netparams
2587 ad4a9ae7 Dimitris Aragiorgis
2588 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2589 9ccacbc8 Dimitris Aragiorgis
  def GetGroupNetParams(self, net_uuid, node):
2590 ad4a9ae7 Dimitris Aragiorgis
    """Locking wrapper of _UnlockedGetGroupNetParams()
2591 ad4a9ae7 Dimitris Aragiorgis

2592 ad4a9ae7 Dimitris Aragiorgis
    """
2593 9ccacbc8 Dimitris Aragiorgis
    return self._UnlockedGetGroupNetParams(net_uuid, node)
2594 ad4a9ae7 Dimitris Aragiorgis
2595 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2596 ad4a9ae7 Dimitris Aragiorgis
  def CheckIPInNodeGroup(self, ip, node):
2597 6a94d553 Dimitris Aragiorgis
    """Check IP uniqueness in nodegroup.
2598 6a94d553 Dimitris Aragiorgis

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

2603 ad4a9ae7 Dimitris Aragiorgis
    @type ip: string
2604 ad4a9ae7 Dimitris Aragiorgis
    @param ip: ip address
2605 ad4a9ae7 Dimitris Aragiorgis
    @type node: string
2606 ad4a9ae7 Dimitris Aragiorgis
    @param node: node name
2607 ad4a9ae7 Dimitris Aragiorgis
    @rtype: (string, dict) or (None, None)
2608 ad4a9ae7 Dimitris Aragiorgis
    @return: (network name, netparams)
2609 ad4a9ae7 Dimitris Aragiorgis

2610 ad4a9ae7 Dimitris Aragiorgis
    """
2611 ad4a9ae7 Dimitris Aragiorgis
    if ip is None:
2612 ad4a9ae7 Dimitris Aragiorgis
      return (None, None)
2613 ad4a9ae7 Dimitris Aragiorgis
    node_info = self._UnlockedGetNodeInfo(node)
2614 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2615 ad4a9ae7 Dimitris Aragiorgis
    for net_uuid in nodegroup_info.networks.keys():
2616 ad4a9ae7 Dimitris Aragiorgis
      net_info = self._UnlockedGetNetwork(net_uuid)
2617 ad4a9ae7 Dimitris Aragiorgis
      pool = network.AddressPool(net_info)
2618 beb81ea5 Dimitris Aragiorgis
      if pool.Contains(ip):
2619 ad4a9ae7 Dimitris Aragiorgis
        return (net_info.name, nodegroup_info.networks[net_uuid])
2620 ad4a9ae7 Dimitris Aragiorgis
2621 ad4a9ae7 Dimitris Aragiorgis
    return (None, None)