Statistics
| Branch: | Tag: | Revision:

root / lib / config.py @ 4f90370c

History | View | Annotate | Download (94.9 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 3efa7659 Thomas Thrainer
class ConfigWriter(object):
171 098c0958 Michael Hanselmann
  """The interface to the cluster configuration.
172 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

480 34e54ebc Iustin Pop
    @type include_temporary: boolean
481 34e54ebc Iustin Pop
    @param include_temporary: whether to include the _temporary_ids set
482 34e54ebc Iustin Pop
    @rtype: set
483 34e54ebc Iustin Pop
    @return: a set of IDs
484 34e54ebc Iustin Pop

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

498 923b1523 Iustin Pop
    This checks the current node, instances and disk names for
499 923b1523 Iustin Pop
    duplicates.
500 923b1523 Iustin Pop

501 c41eea6e Iustin Pop
    @rtype: string
502 c41eea6e Iustin Pop
    @return: the unique id
503 923b1523 Iustin Pop

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

512 430b923c Iustin Pop
    This is just a wrapper over the unlocked version.
513 430b923c Iustin Pop

514 4fae38c5 Guido Trotter
    @type ec_id: string
515 4fae38c5 Guido Trotter
    @param ec_id: unique id for the job to reserve the id to
516 34d657ba Iustin Pop

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

523 c41eea6e Iustin Pop
    @rtype: list
524 c41eea6e Iustin Pop
    @return: the list of all MACs
525 c41eea6e Iustin Pop

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

537 c41eea6e Iustin Pop
    @rtype: list
538 c41eea6e Iustin Pop
    @return: the list of all DRBD secrets
539 c41eea6e Iustin Pop

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

559 4b98ac29 Iustin Pop
    @type disk: L{objects.Disk}
560 4b98ac29 Iustin Pop
    @param disk: the disk at which to start searching
561 4b98ac29 Iustin Pop
    @type l_ids: list
562 4b98ac29 Iustin Pop
    @param l_ids: list of current logical ids
563 4b98ac29 Iustin Pop
    @rtype: list
564 4b98ac29 Iustin Pop
    @return: a list of error messages
565 4b98ac29 Iustin Pop

566 4b98ac29 Iustin Pop
    """
567 4b98ac29 Iustin Pop
    result = []
568 25ae22e4 Iustin Pop
    if disk.logical_id is not None:
569 25ae22e4 Iustin Pop
      if disk.logical_id in l_ids:
570 25ae22e4 Iustin Pop
        result.append("duplicate logical id %s" % str(disk.logical_id))
571 25ae22e4 Iustin Pop
      else:
572 25ae22e4 Iustin Pop
        l_ids.append(disk.logical_id)
573 4b98ac29 Iustin Pop
574 4b98ac29 Iustin Pop
    if disk.children:
575 4b98ac29 Iustin Pop
      for child in disk.children:
576 a57e502a Thomas Thrainer
        result.extend(self._CheckDiskIDs(child, l_ids))
577 4b98ac29 Iustin Pop
    return result
578 4b98ac29 Iustin Pop
579 4a89c54a Iustin Pop
  def _UnlockedVerifyConfig(self):
580 a8efbb40 Iustin Pop
    """Verify function.
581 a8efbb40 Iustin Pop

582 4a89c54a Iustin Pop
    @rtype: list
583 4a89c54a Iustin Pop
    @return: a list of error messages; a non-empty list signifies
584 4a89c54a Iustin Pop
        configuration errors
585 4a89c54a Iustin Pop

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

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

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

875 4a89c54a Iustin Pop
    """
876 4a89c54a Iustin Pop
    return self._UnlockedVerifyConfig()
877 4a89c54a Iustin Pop
878 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
879 b2fddf63 Iustin Pop
  def AddTcpUdpPort(self, port):
880 b2fddf63 Iustin Pop
    """Adds a new port to the available port pool.
881 b2fddf63 Iustin Pop

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

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

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

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

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

925 4a89c54a Iustin Pop
    @rtype: (dict, list)
926 da4a52a3 Thomas Thrainer
    @return: dictionary of node_uuid: dict of minor: instance_uuid;
927 c41eea6e Iustin Pop
        the returned dict will have all the nodes in it (even if with
928 4a89c54a Iustin Pop
        an empty list), and a list of duplicates; if the duplicates
929 4a89c54a Iustin Pop
        list is not empty, the configuration is corrupted and its caller
930 4a89c54a Iustin Pop
        should raise an exception
931 a81c53c9 Iustin Pop

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

970 6d2e83d5 Iustin Pop
    This is just a wrapper over L{_UnlockedComputeDRBDMap}.
971 6d2e83d5 Iustin Pop

972 da4a52a3 Thomas Thrainer
    @return: dictionary of node_uuid: dict of minor: instance_uuid;
973 6d2e83d5 Iustin Pop
        the returned dict will have all the nodes in it (even if with
974 6d2e83d5 Iustin Pop
        an empty list).
975 6d2e83d5 Iustin Pop

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

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

992 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
993 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which we allocate minors
994 32388e6d Iustin Pop

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

1042 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1043 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1044 da4a52a3 Thomas Thrainer
                      released
1045 a81c53c9 Iustin Pop

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

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

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

1063 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1064 da4a52a3 Thomas Thrainer
    @param inst_uuid: the instance for which temporary minors should be
1065 da4a52a3 Thomas Thrainer
                      released
1066 61cf6b5e Iustin Pop

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

1074 4a8b186a Michael Hanselmann
    @return: Config version
1075 4a8b186a Michael Hanselmann

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

1083 4a8b186a Michael Hanselmann
    @return: Cluster name
1084 4a8b186a Michael Hanselmann

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

1092 1c3231aa Thomas Thrainer
    @return: Master node UUID
1093 4a8b186a Michael Hanselmann

1094 4a8b186a Michael Hanselmann
    """
1095 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_node
1096 4a8b186a Michael Hanselmann
1097 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1098 1c3231aa Thomas Thrainer
  def GetMasterNodeName(self):
1099 1c3231aa Thomas Thrainer
    """Get the hostname of the master node for this cluster.
1100 1c3231aa Thomas Thrainer

1101 1c3231aa Thomas Thrainer
    @return: Master node hostname
1102 1c3231aa Thomas Thrainer

1103 1c3231aa Thomas Thrainer
    """
1104 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(self._config_data.cluster.master_node)
1105 1c3231aa Thomas Thrainer
1106 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1107 b730e2a7 Thomas Thrainer
  def GetMasterNodeInfo(self):
1108 b730e2a7 Thomas Thrainer
    """Get the master node information for this cluster.
1109 b730e2a7 Thomas Thrainer

1110 b730e2a7 Thomas Thrainer
    @rtype: objects.Node
1111 b730e2a7 Thomas Thrainer
    @return: Master node L{objects.Node} object
1112 b730e2a7 Thomas Thrainer

1113 b730e2a7 Thomas Thrainer
    """
1114 b730e2a7 Thomas Thrainer
    return self._UnlockedGetNodeInfo(self._config_data.cluster.master_node)
1115 b730e2a7 Thomas Thrainer
1116 b730e2a7 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1117 4a8b186a Michael Hanselmann
  def GetMasterIP(self):
1118 4a8b186a Michael Hanselmann
    """Get the IP of the master node for this cluster.
1119 4a8b186a Michael Hanselmann

1120 4a8b186a Michael Hanselmann
    @return: Master IP
1121 4a8b186a Michael Hanselmann

1122 4a8b186a Michael Hanselmann
    """
1123 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_ip
1124 4a8b186a Michael Hanselmann
1125 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1126 4a8b186a Michael Hanselmann
  def GetMasterNetdev(self):
1127 4a8b186a Michael Hanselmann
    """Get the master network device for this cluster.
1128 4a8b186a Michael Hanselmann

1129 4a8b186a Michael Hanselmann
    """
1130 4a8b186a Michael Hanselmann
    return self._config_data.cluster.master_netdev
1131 4a8b186a Michael Hanselmann
1132 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1133 5a8648eb Andrea Spadaccini
  def GetMasterNetmask(self):
1134 5a8648eb Andrea Spadaccini
    """Get the netmask of the master node for this cluster.
1135 5a8648eb Andrea Spadaccini

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

1143 33be7576 Andrea Spadaccini
    """
1144 33be7576 Andrea Spadaccini
    return self._config_data.cluster.use_external_mip_script
1145 33be7576 Andrea Spadaccini
1146 33be7576 Andrea Spadaccini
  @locking.ssynchronized(_config_lock, shared=1)
1147 4a8b186a Michael Hanselmann
  def GetFileStorageDir(self):
1148 4a8b186a Michael Hanselmann
    """Get the file storage dir for this cluster.
1149 4a8b186a Michael Hanselmann

1150 4a8b186a Michael Hanselmann
    """
1151 4a8b186a Michael Hanselmann
    return self._config_data.cluster.file_storage_dir
1152 4a8b186a Michael Hanselmann
1153 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1154 4b97f902 Apollon Oikonomopoulos
  def GetSharedFileStorageDir(self):
1155 4b97f902 Apollon Oikonomopoulos
    """Get the shared file storage dir for this cluster.
1156 4b97f902 Apollon Oikonomopoulos

1157 4b97f902 Apollon Oikonomopoulos
    """
1158 4b97f902 Apollon Oikonomopoulos
    return self._config_data.cluster.shared_file_storage_dir
1159 4b97f902 Apollon Oikonomopoulos
1160 4b97f902 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1161 4a8b186a Michael Hanselmann
  def GetHypervisorType(self):
1162 4a8b186a Michael Hanselmann
    """Get the hypervisor type for this cluster.
1163 4a8b186a Michael Hanselmann

1164 4a8b186a Michael Hanselmann
    """
1165 066f465d Guido Trotter
    return self._config_data.cluster.enabled_hypervisors[0]
1166 4a8b186a Michael Hanselmann
1167 4a8b186a Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1168 a9542a4f Thomas Thrainer
  def GetRsaHostKey(self):
1169 a8083063 Iustin Pop
    """Return the rsa hostkey from the config.
1170 a8083063 Iustin Pop

1171 c41eea6e Iustin Pop
    @rtype: string
1172 c41eea6e Iustin Pop
    @return: the rsa hostkey
1173 a8083063 Iustin Pop

1174 a8083063 Iustin Pop
    """
1175 a8083063 Iustin Pop
    return self._config_data.cluster.rsahostkeypub
1176 a8083063 Iustin Pop
1177 bf4af505 Apollon Oikonomopoulos
  @locking.ssynchronized(_config_lock, shared=1)
1178 a9542a4f Thomas Thrainer
  def GetDsaHostKey(self):
1179 a9542a4f Thomas Thrainer
    """Return the dsa hostkey from the config.
1180 a9542a4f Thomas Thrainer

1181 a9542a4f Thomas Thrainer
    @rtype: string
1182 a9542a4f Thomas Thrainer
    @return: the dsa hostkey
1183 a9542a4f Thomas Thrainer

1184 a9542a4f Thomas Thrainer
    """
1185 a9542a4f Thomas Thrainer
    return self._config_data.cluster.dsahostkeypub
1186 a9542a4f Thomas Thrainer
1187 a9542a4f Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1188 bf4af505 Apollon Oikonomopoulos
  def GetDefaultIAllocator(self):
1189 bf4af505 Apollon Oikonomopoulos
    """Get the default instance allocator for this cluster.
1190 bf4af505 Apollon Oikonomopoulos

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

1198 868a98ca Manuel Franceschini
    @return: primary ip family
1199 868a98ca Manuel Franceschini

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

1207 f9d20654 Andrea Spadaccini
    @rtype: L{object.MasterNetworkParameters}
1208 f9d20654 Andrea Spadaccini
    @return: network parameters of the master node
1209 c9f4b8e6 Andrea Spadaccini

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

1223 90e99856 Adeodato Simo
    This method calls group.UpgradeConfig() to fill any missing attributes
1224 90e99856 Adeodato Simo
    according to their default values.
1225 90e99856 Adeodato Simo

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

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

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

1272 e11a1b77 Adeodato Simo
    @type group_uuid: string
1273 e11a1b77 Adeodato Simo
    @param group_uuid: the UUID of the node group to remove
1274 e11a1b77 Adeodato Simo

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

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

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

1316 e85d8982 Stephen Shirley
    This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1317 e85d8982 Stephen Shirley

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

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

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

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

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

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

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

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

1370 dac81741 Michael Hanselmann
    """
1371 1c3231aa Thomas Thrainer
    ngfn = lambda node_uuid: self._UnlockedGetNodeInfo(node_uuid).group
1372 1c3231aa Thomas Thrainer
    return frozenset(member_uuid
1373 1c3231aa Thomas Thrainer
                     for node_uuid in nodes
1374 1c3231aa Thomas Thrainer
                     for member_uuid in
1375 1c3231aa Thomas Thrainer
                       self._UnlockedGetNodeGroup(ngfn(node_uuid)).members)
1376 dac81741 Michael Hanselmann
1377 080fbeea Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1378 080fbeea Michael Hanselmann
  def GetMultiNodeGroupInfo(self, group_uuids):
1379 080fbeea Michael Hanselmann
    """Get the configuration of multiple node groups.
1380 080fbeea Michael Hanselmann

1381 080fbeea Michael Hanselmann
    @param group_uuids: List of node group UUIDs
1382 080fbeea Michael Hanselmann
    @rtype: list
1383 080fbeea Michael Hanselmann
    @return: List of tuples of (group_uuid, group_info)
1384 080fbeea Michael Hanselmann

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

1392 a8083063 Iustin Pop
    This should be used after creating a new instance.
1393 a8083063 Iustin Pop

1394 c41eea6e Iustin Pop
    @type instance: L{objects.Instance}
1395 c41eea6e Iustin Pop
    @param instance: the instance object
1396 c41eea6e Iustin Pop

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

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

1428 430b923c Iustin Pop
    """
1429 430b923c Iustin Pop
    if not item.uuid:
1430 4fae38c5 Guido Trotter
      item.uuid = self._GenerateUniqueID(ec_id)
1431 da4a52a3 Thomas Thrainer
    else:
1432 da4a52a3 Thomas Thrainer
      self._CheckUniqueUUID(item, include_temporary=True)
1433 da4a52a3 Thomas Thrainer
1434 da4a52a3 Thomas Thrainer
  def _CheckUniqueUUID(self, item, include_temporary):
1435 da4a52a3 Thomas Thrainer
    """Checks that the UUID of the given object is unique.
1436 da4a52a3 Thomas Thrainer

1437 da4a52a3 Thomas Thrainer
    @param item: the instance or node to be checked
1438 da4a52a3 Thomas Thrainer
    @param include_temporary: whether temporarily generated UUID's should be
1439 da4a52a3 Thomas Thrainer
              included in the check. If the UUID of the item to be checked is
1440 da4a52a3 Thomas Thrainer
              a temporarily generated one, this has to be C{False}.
1441 da4a52a3 Thomas Thrainer

1442 da4a52a3 Thomas Thrainer
    """
1443 da4a52a3 Thomas Thrainer
    if not item.uuid:
1444 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("'%s' must have an UUID" % (item.name,))
1445 da4a52a3 Thomas Thrainer
    if item.uuid in self._AllIDs(include_temporary=include_temporary):
1446 be0fc05d Iustin Pop
      raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1447 be0fc05d Iustin Pop
                                      " in use" % (item.name, item.uuid))
1448 430b923c Iustin Pop
1449 da4a52a3 Thomas Thrainer
  def _SetInstanceStatus(self, inst_uuid, status, disks_active):
1450 6a408fb2 Iustin Pop
    """Set the instance's status to a given value.
1451 a8083063 Iustin Pop

1452 a8083063 Iustin Pop
    """
1453 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1454 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Unknown instance '%s'" %
1455 da4a52a3 Thomas Thrainer
                                      inst_uuid)
1456 da4a52a3 Thomas Thrainer
    instance = self._config_data.instances[inst_uuid]
1457 1d4a4b26 Thomas Thrainer
1458 1d4a4b26 Thomas Thrainer
    if status is None:
1459 1d4a4b26 Thomas Thrainer
      status = instance.admin_state
1460 1d4a4b26 Thomas Thrainer
    if disks_active is None:
1461 1d4a4b26 Thomas Thrainer
      disks_active = instance.disks_active
1462 1d4a4b26 Thomas Thrainer
1463 1d4a4b26 Thomas Thrainer
    assert status in constants.ADMINST_ALL, \
1464 1d4a4b26 Thomas Thrainer
           "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1465 1d4a4b26 Thomas Thrainer
1466 1d4a4b26 Thomas Thrainer
    if instance.admin_state != status or \
1467 1d4a4b26 Thomas Thrainer
       instance.disks_active != disks_active:
1468 9ca8a7c5 Agata Murawska
      instance.admin_state = status
1469 1d4a4b26 Thomas Thrainer
      instance.disks_active = disks_active
1470 b989e85d Iustin Pop
      instance.serial_no += 1
1471 d693c864 Iustin Pop
      instance.mtime = time.time()
1472 455a3445 Iustin Pop
      self._WriteConfig()
1473 a8083063 Iustin Pop
1474 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1475 da4a52a3 Thomas Thrainer
  def MarkInstanceUp(self, inst_uuid):
1476 6a408fb2 Iustin Pop
    """Mark the instance status to up in the config.
1477 6a408fb2 Iustin Pop

1478 1d4a4b26 Thomas Thrainer
    This also sets the instance disks active flag.
1479 1d4a4b26 Thomas Thrainer

1480 6a408fb2 Iustin Pop
    """
1481 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_UP, True)
1482 57de31c0 Agata Murawska
1483 57de31c0 Agata Murawska
  @locking.ssynchronized(_config_lock)
1484 da4a52a3 Thomas Thrainer
  def MarkInstanceOffline(self, inst_uuid):
1485 57de31c0 Agata Murawska
    """Mark the instance status to down in the config.
1486 57de31c0 Agata Murawska

1487 1d4a4b26 Thomas Thrainer
    This also clears the instance disks active flag.
1488 1d4a4b26 Thomas Thrainer

1489 57de31c0 Agata Murawska
    """
1490 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_OFFLINE, False)
1491 6a408fb2 Iustin Pop
1492 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1493 da4a52a3 Thomas Thrainer
  def RemoveInstance(self, inst_uuid):
1494 a8083063 Iustin Pop
    """Remove the instance from the configuration.
1495 a8083063 Iustin Pop

1496 a8083063 Iustin Pop
    """
1497 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1498 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1499 f396ad8c Vangelis Koukis
1500 f396ad8c Vangelis Koukis
    # If a network port has been allocated to the instance,
1501 f396ad8c Vangelis Koukis
    # return it to the pool of free ports.
1502 da4a52a3 Thomas Thrainer
    inst = self._config_data.instances[inst_uuid]
1503 f396ad8c Vangelis Koukis
    network_port = getattr(inst, "network_port", None)
1504 f396ad8c Vangelis Koukis
    if network_port is not None:
1505 f396ad8c Vangelis Koukis
      self._config_data.cluster.tcpudp_port_pool.add(network_port)
1506 f396ad8c Vangelis Koukis
1507 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1508 ced51149 Dimitris Aragiorgis
1509 ced51149 Dimitris Aragiorgis
    for nic in instance.nics:
1510 9394f4d1 Dimitris Aragiorgis
      if nic.network and nic.ip:
1511 1b68f268 Helga Velroyen
        # Return all IP addresses to the respective address pools
1512 9394f4d1 Dimitris Aragiorgis
        self._UnlockedCommitIp(constants.RELEASE_ACTION, nic.network, nic.ip)
1513 ced51149 Dimitris Aragiorgis
1514 da4a52a3 Thomas Thrainer
    del self._config_data.instances[inst_uuid]
1515 81a49123 Iustin Pop
    self._config_data.cluster.serial_no += 1
1516 a8083063 Iustin Pop
    self._WriteConfig()
1517 a8083063 Iustin Pop
1518 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1519 da4a52a3 Thomas Thrainer
  def RenameInstance(self, inst_uuid, new_name):
1520 fc95f88f Iustin Pop
    """Rename an instance.
1521 fc95f88f Iustin Pop

1522 fc95f88f Iustin Pop
    This needs to be done in ConfigWriter and not by RemoveInstance
1523 fc95f88f Iustin Pop
    combined with AddInstance as only we can guarantee an atomic
1524 fc95f88f Iustin Pop
    rename.
1525 fc95f88f Iustin Pop

1526 fc95f88f Iustin Pop
    """
1527 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1528 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1529 ea642319 Michael Hanselmann
1530 da4a52a3 Thomas Thrainer
    inst = self._config_data.instances[inst_uuid]
1531 fc95f88f Iustin Pop
    inst.name = new_name
1532 b23c4333 Manuel Franceschini
1533 ea642319 Michael Hanselmann
    for (idx, disk) in enumerate(inst.disks):
1534 cd3b4ff4 Helga Velroyen
      if disk.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1535 b23c4333 Manuel Franceschini
        # rename the file paths in logical and physical id
1536 b23c4333 Manuel Franceschini
        file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1537 ea642319 Michael Hanselmann
        disk.logical_id = (disk.logical_id[0],
1538 ea642319 Michael Hanselmann
                           utils.PathJoin(file_storage_dir, inst.name,
1539 ea642319 Michael Hanselmann
                                          "disk%s" % idx))
1540 ea642319 Michael Hanselmann
1541 1fc34c48 Michael Hanselmann
    # Force update of ssconf files
1542 1fc34c48 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
1543 1fc34c48 Michael Hanselmann
1544 fc95f88f Iustin Pop
    self._WriteConfig()
1545 fc95f88f Iustin Pop
1546 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1547 da4a52a3 Thomas Thrainer
  def MarkInstanceDown(self, inst_uuid):
1548 a8083063 Iustin Pop
    """Mark the status of an instance to down in the configuration.
1549 a8083063 Iustin Pop

1550 1d4a4b26 Thomas Thrainer
    This does not touch the instance disks active flag, as shut down instances
1551 1d4a4b26 Thomas Thrainer
    can still have active disks.
1552 1d4a4b26 Thomas Thrainer

1553 1d4a4b26 Thomas Thrainer
    """
1554 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None)
1555 1d4a4b26 Thomas Thrainer
1556 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1557 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksActive(self, inst_uuid):
1558 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks active.
1559 1d4a4b26 Thomas Thrainer

1560 1d4a4b26 Thomas Thrainer
    """
1561 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, True)
1562 1d4a4b26 Thomas Thrainer
1563 1d4a4b26 Thomas Thrainer
  @locking.ssynchronized(_config_lock)
1564 da4a52a3 Thomas Thrainer
  def MarkInstanceDisksInactive(self, inst_uuid):
1565 1d4a4b26 Thomas Thrainer
    """Mark the status of instance disks inactive.
1566 1d4a4b26 Thomas Thrainer

1567 a8083063 Iustin Pop
    """
1568 da4a52a3 Thomas Thrainer
    self._SetInstanceStatus(inst_uuid, None, False)
1569 a8083063 Iustin Pop
1570 94bbfece Iustin Pop
  def _UnlockedGetInstanceList(self):
1571 94bbfece Iustin Pop
    """Get the list of instances.
1572 94bbfece Iustin Pop

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

1575 94bbfece Iustin Pop
    """
1576 94bbfece Iustin Pop
    return self._config_data.instances.keys()
1577 94bbfece Iustin Pop
1578 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1579 a8083063 Iustin Pop
  def GetInstanceList(self):
1580 a8083063 Iustin Pop
    """Get the list of instances.
1581 a8083063 Iustin Pop

1582 da4a52a3 Thomas Thrainer
    @return: array of instances, ex. ['instance2-uuid', 'instance1-uuid']
1583 a8083063 Iustin Pop

1584 a8083063 Iustin Pop
    """
1585 94bbfece Iustin Pop
    return self._UnlockedGetInstanceList()
1586 a8083063 Iustin Pop
1587 a8083063 Iustin Pop
  def ExpandInstanceName(self, short_name):
1588 a8083063 Iustin Pop
    """Attempt to expand an incomplete instance name.
1589 a8083063 Iustin Pop

1590 a8083063 Iustin Pop
    """
1591 da4a52a3 Thomas Thrainer
    # Locking is done in L{ConfigWriter.GetAllInstancesInfo}
1592 da4a52a3 Thomas Thrainer
    all_insts = self.GetAllInstancesInfo().values()
1593 da4a52a3 Thomas Thrainer
    expanded_name = _MatchNameComponentIgnoreCase(
1594 da4a52a3 Thomas Thrainer
                      short_name, [inst.name for inst in all_insts])
1595 da4a52a3 Thomas Thrainer
1596 da4a52a3 Thomas Thrainer
    if expanded_name is not None:
1597 da4a52a3 Thomas Thrainer
      # there has to be exactly one instance with that name
1598 da4a52a3 Thomas Thrainer
      inst = (filter(lambda n: n.name == expanded_name, all_insts)[0])
1599 da4a52a3 Thomas Thrainer
      return (inst.uuid, inst.name)
1600 da4a52a3 Thomas Thrainer
    else:
1601 738436bf Thomas Thrainer
      return (None, None)
1602 a8083063 Iustin Pop
1603 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceInfo(self, inst_uuid):
1604 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1605 94bbfece Iustin Pop

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

1608 94bbfece Iustin Pop
    """
1609 da4a52a3 Thomas Thrainer
    if inst_uuid not in self._config_data.instances:
1610 94bbfece Iustin Pop
      return None
1611 94bbfece Iustin Pop
1612 da4a52a3 Thomas Thrainer
    return self._config_data.instances[inst_uuid]
1613 94bbfece Iustin Pop
1614 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1615 da4a52a3 Thomas Thrainer
  def GetInstanceInfo(self, inst_uuid):
1616 5bbd3f7f Michael Hanselmann
    """Returns information about an instance.
1617 a8083063 Iustin Pop

1618 5bbd3f7f Michael Hanselmann
    It takes the information from the configuration file. Other information of
1619 a8083063 Iustin Pop
    an instance are taken from the live systems.
1620 a8083063 Iustin Pop

1621 da4a52a3 Thomas Thrainer
    @param inst_uuid: UUID of the instance
1622 a8083063 Iustin Pop

1623 c41eea6e Iustin Pop
    @rtype: L{objects.Instance}
1624 c41eea6e Iustin Pop
    @return: the instance object
1625 a8083063 Iustin Pop

1626 a8083063 Iustin Pop
    """
1627 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfo(inst_uuid)
1628 a8083063 Iustin Pop
1629 0b2de758 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1630 da4a52a3 Thomas Thrainer
  def GetInstanceNodeGroups(self, inst_uuid, primary_only=False):
1631 2674690b Michael Hanselmann
    """Returns set of node group UUIDs for instance's nodes.
1632 2674690b Michael Hanselmann

1633 2674690b Michael Hanselmann
    @rtype: frozenset
1634 2674690b Michael Hanselmann

1635 2674690b Michael Hanselmann
    """
1636 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1637 2674690b Michael Hanselmann
    if not instance:
1638 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1639 2674690b Michael Hanselmann
1640 2674690b Michael Hanselmann
    if primary_only:
1641 2674690b Michael Hanselmann
      nodes = [instance.primary_node]
1642 2674690b Michael Hanselmann
    else:
1643 2674690b Michael Hanselmann
      nodes = instance.all_nodes
1644 2674690b Michael Hanselmann
1645 1c3231aa Thomas Thrainer
    return frozenset(self._UnlockedGetNodeInfo(node_uuid).group
1646 1c3231aa Thomas Thrainer
                     for node_uuid in nodes)
1647 2674690b Michael Hanselmann
1648 2674690b Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1649 da4a52a3 Thomas Thrainer
  def GetInstanceNetworks(self, inst_uuid):
1650 922610c9 Dimitris Aragiorgis
    """Returns set of network UUIDs for instance's nics.
1651 922610c9 Dimitris Aragiorgis

1652 922610c9 Dimitris Aragiorgis
    @rtype: frozenset
1653 922610c9 Dimitris Aragiorgis

1654 922610c9 Dimitris Aragiorgis
    """
1655 da4a52a3 Thomas Thrainer
    instance = self._UnlockedGetInstanceInfo(inst_uuid)
1656 922610c9 Dimitris Aragiorgis
    if not instance:
1657 da4a52a3 Thomas Thrainer
      raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1658 922610c9 Dimitris Aragiorgis
1659 922610c9 Dimitris Aragiorgis
    networks = set()
1660 922610c9 Dimitris Aragiorgis
    for nic in instance.nics:
1661 922610c9 Dimitris Aragiorgis
      if nic.network:
1662 922610c9 Dimitris Aragiorgis
        networks.add(nic.network)
1663 922610c9 Dimitris Aragiorgis
1664 922610c9 Dimitris Aragiorgis
    return frozenset(networks)
1665 922610c9 Dimitris Aragiorgis
1666 922610c9 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
1667 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfo(self, inst_uuids):
1668 da4a52a3 Thomas Thrainer
    """Get the configuration of multiple instances.
1669 da4a52a3 Thomas Thrainer

1670 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs
1671 da4a52a3 Thomas Thrainer
    @rtype: list
1672 da4a52a3 Thomas Thrainer
    @return: list of tuples (instance UUID, instance_info), where
1673 da4a52a3 Thomas Thrainer
        instance_info is what would GetInstanceInfo return for the
1674 da4a52a3 Thomas Thrainer
        node, while keeping the original order
1675 da4a52a3 Thomas Thrainer

1676 da4a52a3 Thomas Thrainer
    """
1677 da4a52a3 Thomas Thrainer
    return [(uuid, self._UnlockedGetInstanceInfo(uuid)) for uuid in inst_uuids]
1678 da4a52a3 Thomas Thrainer
1679 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1680 da4a52a3 Thomas Thrainer
  def GetMultiInstanceInfoByName(self, inst_names):
1681 71333cb9 Iustin Pop
    """Get the configuration of multiple instances.
1682 71333cb9 Iustin Pop

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

1689 71333cb9 Iustin Pop
    """
1690 da4a52a3 Thomas Thrainer
    result = []
1691 da4a52a3 Thomas Thrainer
    for name in inst_names:
1692 da4a52a3 Thomas Thrainer
      instance = self._UnlockedGetInstanceInfoByName(name)
1693 da4a52a3 Thomas Thrainer
      result.append((instance.uuid, instance))
1694 da4a52a3 Thomas Thrainer
    return result
1695 71333cb9 Iustin Pop
1696 71333cb9 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1697 0b2de758 Iustin Pop
  def GetAllInstancesInfo(self):
1698 0b2de758 Iustin Pop
    """Get the configuration of all instances.
1699 0b2de758 Iustin Pop

1700 0b2de758 Iustin Pop
    @rtype: dict
1701 5fcc718f Iustin Pop
    @return: dict of (instance, instance_info), where instance_info is what
1702 0b2de758 Iustin Pop
              would GetInstanceInfo return for the node
1703 0b2de758 Iustin Pop

1704 0b2de758 Iustin Pop
    """
1705 da4a52a3 Thomas Thrainer
    return self._UnlockedGetAllInstancesInfo()
1706 da4a52a3 Thomas Thrainer
1707 da4a52a3 Thomas Thrainer
  def _UnlockedGetAllInstancesInfo(self):
1708 da4a52a3 Thomas Thrainer
    my_dict = dict([(inst_uuid, self._UnlockedGetInstanceInfo(inst_uuid))
1709 da4a52a3 Thomas Thrainer
                    for inst_uuid in self._UnlockedGetInstanceList()])
1710 0b2de758 Iustin Pop
    return my_dict
1711 0b2de758 Iustin Pop
1712 cc19798f Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1713 cc19798f Michael Hanselmann
  def GetInstancesInfoByFilter(self, filter_fn):
1714 cc19798f Michael Hanselmann
    """Get instance configuration with a filter.
1715 cc19798f Michael Hanselmann

1716 cc19798f Michael Hanselmann
    @type filter_fn: callable
1717 cc19798f Michael Hanselmann
    @param filter_fn: Filter function receiving instance object as parameter,
1718 cc19798f Michael Hanselmann
      returning boolean. Important: this function is called while the
1719 cc19798f Michael Hanselmann
      configuration locks is held. It must not do any complex work or call
1720 cc19798f Michael Hanselmann
      functions potentially leading to a deadlock. Ideally it doesn't call any
1721 cc19798f Michael Hanselmann
      other functions and just compares instance attributes.
1722 cc19798f Michael Hanselmann

1723 cc19798f Michael Hanselmann
    """
1724 da4a52a3 Thomas Thrainer
    return dict((uuid, inst)
1725 da4a52a3 Thomas Thrainer
                for (uuid, inst) in self._config_data.instances.items()
1726 cc19798f Michael Hanselmann
                if filter_fn(inst))
1727 cc19798f Michael Hanselmann
1728 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1729 da4a52a3 Thomas Thrainer
  def GetInstanceInfoByName(self, inst_name):
1730 da4a52a3 Thomas Thrainer
    """Get the L{objects.Instance} object for a named instance.
1731 da4a52a3 Thomas Thrainer

1732 da4a52a3 Thomas Thrainer
    @param inst_name: name of the instance to get information for
1733 da4a52a3 Thomas Thrainer
    @type inst_name: string
1734 da4a52a3 Thomas Thrainer
    @return: the corresponding L{objects.Instance} instance or None if no
1735 da4a52a3 Thomas Thrainer
          information is available
1736 da4a52a3 Thomas Thrainer

1737 da4a52a3 Thomas Thrainer
    """
1738 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceInfoByName(inst_name)
1739 da4a52a3 Thomas Thrainer
1740 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceInfoByName(self, inst_name):
1741 da4a52a3 Thomas Thrainer
    for inst in self._UnlockedGetAllInstancesInfo().values():
1742 da4a52a3 Thomas Thrainer
      if inst.name == inst_name:
1743 da4a52a3 Thomas Thrainer
        return inst
1744 da4a52a3 Thomas Thrainer
    return None
1745 da4a52a3 Thomas Thrainer
1746 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceName(self, inst_uuid):
1747 da4a52a3 Thomas Thrainer
    inst_info = self._UnlockedGetInstanceInfo(inst_uuid)
1748 da4a52a3 Thomas Thrainer
    if inst_info is None:
1749 da4a52a3 Thomas Thrainer
      raise errors.OpExecError("Unknown instance: %s" % inst_uuid)
1750 da4a52a3 Thomas Thrainer
    return inst_info.name
1751 da4a52a3 Thomas Thrainer
1752 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1753 da4a52a3 Thomas Thrainer
  def GetInstanceName(self, inst_uuid):
1754 da4a52a3 Thomas Thrainer
    """Gets the instance name for the passed instance.
1755 da4a52a3 Thomas Thrainer

1756 da4a52a3 Thomas Thrainer
    @param inst_uuid: instance UUID to get name for
1757 da4a52a3 Thomas Thrainer
    @type inst_uuid: string
1758 da4a52a3 Thomas Thrainer
    @rtype: string
1759 da4a52a3 Thomas Thrainer
    @return: instance name
1760 da4a52a3 Thomas Thrainer

1761 da4a52a3 Thomas Thrainer
    """
1762 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceName(inst_uuid)
1763 da4a52a3 Thomas Thrainer
1764 da4a52a3 Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
1765 da4a52a3 Thomas Thrainer
  def GetInstanceNames(self, inst_uuids):
1766 da4a52a3 Thomas Thrainer
    """Gets the instance names for the passed list of nodes.
1767 da4a52a3 Thomas Thrainer

1768 da4a52a3 Thomas Thrainer
    @param inst_uuids: list of instance UUIDs to get names for
1769 da4a52a3 Thomas Thrainer
    @type inst_uuids: list of strings
1770 da4a52a3 Thomas Thrainer
    @rtype: list of strings
1771 da4a52a3 Thomas Thrainer
    @return: list of instance names
1772 da4a52a3 Thomas Thrainer

1773 da4a52a3 Thomas Thrainer
    """
1774 da4a52a3 Thomas Thrainer
    return self._UnlockedGetInstanceNames(inst_uuids)
1775 da4a52a3 Thomas Thrainer
1776 da4a52a3 Thomas Thrainer
  def _UnlockedGetInstanceNames(self, inst_uuids):
1777 da4a52a3 Thomas Thrainer
    return [self._UnlockedGetInstanceName(uuid) for uuid in inst_uuids]
1778 da4a52a3 Thomas Thrainer
1779 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1780 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
1781 a8083063 Iustin Pop
    """Add a node to the configuration.
1782 a8083063 Iustin Pop

1783 c41eea6e Iustin Pop
    @type node: L{objects.Node}
1784 c41eea6e Iustin Pop
    @param node: a Node instance
1785 a8083063 Iustin Pop

1786 a8083063 Iustin Pop
    """
1787 099c52ad Iustin Pop
    logging.info("Adding node %s to configuration", node.name)
1788 d8470559 Michael Hanselmann
1789 0debfb35 Guido Trotter
    self._EnsureUUID(node, ec_id)
1790 430b923c Iustin Pop
1791 b989e85d Iustin Pop
    node.serial_no = 1
1792 d693c864 Iustin Pop
    node.ctime = node.mtime = time.time()
1793 1c3231aa Thomas Thrainer
    self._UnlockedAddNodeToGroup(node.uuid, node.group)
1794 1c3231aa Thomas Thrainer
    self._config_data.nodes[node.uuid] = node
1795 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1796 a8083063 Iustin Pop
    self._WriteConfig()
1797 a8083063 Iustin Pop
1798 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
1799 1c3231aa Thomas Thrainer
  def RemoveNode(self, node_uuid):
1800 a8083063 Iustin Pop
    """Remove a node from the configuration.
1801 a8083063 Iustin Pop

1802 a8083063 Iustin Pop
    """
1803 1c3231aa Thomas Thrainer
    logging.info("Removing node %s from configuration", node_uuid)
1804 d8470559 Michael Hanselmann
1805 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
1806 1c3231aa Thomas Thrainer
      raise errors.ConfigurationError("Unknown node '%s'" % node_uuid)
1807 a8083063 Iustin Pop
1808 1c3231aa Thomas Thrainer
    self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_uuid])
1809 1c3231aa Thomas Thrainer
    del self._config_data.nodes[node_uuid]
1810 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
1811 a8083063 Iustin Pop
    self._WriteConfig()
1812 a8083063 Iustin Pop
1813 a8083063 Iustin Pop
  def ExpandNodeName(self, short_name):
1814 1c3231aa Thomas Thrainer
    """Attempt to expand an incomplete node name into a node UUID.
1815 a8083063 Iustin Pop

1816 a8083063 Iustin Pop
    """
1817 1c3231aa Thomas Thrainer
    # Locking is done in L{ConfigWriter.GetAllNodesInfo}
1818 1c3231aa Thomas Thrainer
    all_nodes = self.GetAllNodesInfo().values()
1819 1c3231aa Thomas Thrainer
    expanded_name = _MatchNameComponentIgnoreCase(
1820 1c3231aa Thomas Thrainer
                      short_name, [node.name for node in all_nodes])
1821 a8083063 Iustin Pop
1822 1c3231aa Thomas Thrainer
    if expanded_name is not None:
1823 da4a52a3 Thomas Thrainer
      # there has to be exactly one node with that name
1824 1c3231aa Thomas Thrainer
      node = (filter(lambda n: n.name == expanded_name, all_nodes)[0])
1825 1c3231aa Thomas Thrainer
      return (node.uuid, node.name)
1826 1c3231aa Thomas Thrainer
    else:
1827 738436bf Thomas Thrainer
      return (None, None)
1828 1c3231aa Thomas Thrainer
1829 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfo(self, node_uuid):
1830 a8083063 Iustin Pop
    """Get the configuration of a node, as stored in the config.
1831 a8083063 Iustin Pop

1832 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1833 c41eea6e Iustin Pop
    held.
1834 f78ede4e Guido Trotter

1835 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1836 a8083063 Iustin Pop

1837 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1838 c41eea6e Iustin Pop
    @return: the node object
1839 a8083063 Iustin Pop

1840 a8083063 Iustin Pop
    """
1841 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodes:
1842 a8083063 Iustin Pop
      return None
1843 a8083063 Iustin Pop
1844 1c3231aa Thomas Thrainer
    return self._config_data.nodes[node_uuid]
1845 a8083063 Iustin Pop
1846 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1847 1c3231aa Thomas Thrainer
  def GetNodeInfo(self, node_uuid):
1848 f78ede4e Guido Trotter
    """Get the configuration of a node, as stored in the config.
1849 f78ede4e Guido Trotter

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

1852 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1853 c41eea6e Iustin Pop

1854 c41eea6e Iustin Pop
    @rtype: L{objects.Node}
1855 c41eea6e Iustin Pop
    @return: the node object
1856 f78ede4e Guido Trotter

1857 f78ede4e Guido Trotter
    """
1858 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfo(node_uuid)
1859 f78ede4e Guido Trotter
1860 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1861 1c3231aa Thomas Thrainer
  def GetNodeInstances(self, node_uuid):
1862 8bf9e9a5 Iustin Pop
    """Get the instances of a node, as stored in the config.
1863 8bf9e9a5 Iustin Pop

1864 1c3231aa Thomas Thrainer
    @param node_uuid: the node UUID
1865 8bf9e9a5 Iustin Pop

1866 8bf9e9a5 Iustin Pop
    @rtype: (list, list)
1867 8bf9e9a5 Iustin Pop
    @return: a tuple with two lists: the primary and the secondary instances
1868 8bf9e9a5 Iustin Pop

1869 8bf9e9a5 Iustin Pop
    """
1870 8bf9e9a5 Iustin Pop
    pri = []
1871 8bf9e9a5 Iustin Pop
    sec = []
1872 8bf9e9a5 Iustin Pop
    for inst in self._config_data.instances.values():
1873 1c3231aa Thomas Thrainer
      if inst.primary_node == node_uuid:
1874 da4a52a3 Thomas Thrainer
        pri.append(inst.uuid)
1875 1c3231aa Thomas Thrainer
      if node_uuid in inst.secondary_nodes:
1876 da4a52a3 Thomas Thrainer
        sec.append(inst.uuid)
1877 8bf9e9a5 Iustin Pop
    return (pri, sec)
1878 8bf9e9a5 Iustin Pop
1879 c71b049c Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
1880 c71b049c Michael Hanselmann
  def GetNodeGroupInstances(self, uuid, primary_only=False):
1881 c71b049c Michael Hanselmann
    """Get the instances of a node group.
1882 c71b049c Michael Hanselmann

1883 c71b049c Michael Hanselmann
    @param uuid: Node group UUID
1884 c71b049c Michael Hanselmann
    @param primary_only: Whether to only consider primary nodes
1885 c71b049c Michael Hanselmann
    @rtype: frozenset
1886 da4a52a3 Thomas Thrainer
    @return: List of instance UUIDs in node group
1887 c71b049c Michael Hanselmann

1888 c71b049c Michael Hanselmann
    """
1889 c71b049c Michael Hanselmann
    if primary_only:
1890 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: [inst.primary_node]
1891 c71b049c Michael Hanselmann
    else:
1892 c71b049c Michael Hanselmann
      nodes_fn = lambda inst: inst.all_nodes
1893 c71b049c Michael Hanselmann
1894 da4a52a3 Thomas Thrainer
    return frozenset(inst.uuid
1895 c71b049c Michael Hanselmann
                     for inst in self._config_data.instances.values()
1896 1c3231aa Thomas Thrainer
                     for node_uuid in nodes_fn(inst)
1897 1c3231aa Thomas Thrainer
                     if self._UnlockedGetNodeInfo(node_uuid).group == uuid)
1898 c71b049c Michael Hanselmann
1899 def6577f Helga Velroyen
  def _UnlockedGetHvparamsString(self, hvname):
1900 def6577f Helga Velroyen
    """Return the string representation of the list of hyervisor parameters of
1901 def6577f Helga Velroyen
    the given hypervisor.
1902 def6577f Helga Velroyen

1903 def6577f Helga Velroyen
    @see: C{GetHvparams}
1904 def6577f Helga Velroyen

1905 def6577f Helga Velroyen
    """
1906 def6577f Helga Velroyen
    result = ""
1907 def6577f Helga Velroyen
    hvparams = self._config_data.cluster.hvparams[hvname]
1908 def6577f Helga Velroyen
    for key in hvparams:
1909 def6577f Helga Velroyen
      result += "%s=%s\n" % (key, hvparams[key])
1910 def6577f Helga Velroyen
    return result
1911 def6577f Helga Velroyen
1912 def6577f Helga Velroyen
  @locking.ssynchronized(_config_lock, shared=1)
1913 def6577f Helga Velroyen
  def GetHvparamsString(self, hvname):
1914 def6577f Helga Velroyen
    """Return the hypervisor parameters of the given hypervisor.
1915 def6577f Helga Velroyen

1916 def6577f Helga Velroyen
    @type hvname: string
1917 def6577f Helga Velroyen
    @param hvname: name of a hypervisor
1918 def6577f Helga Velroyen
    @rtype: string
1919 def6577f Helga Velroyen
    @return: string containing key-value-pairs, one pair on each line;
1920 def6577f Helga Velroyen
      format: KEY=VALUE
1921 def6577f Helga Velroyen

1922 def6577f Helga Velroyen
    """
1923 def6577f Helga Velroyen
    return self._UnlockedGetHvparamsString(hvname)
1924 def6577f Helga Velroyen
1925 f78ede4e Guido Trotter
  def _UnlockedGetNodeList(self):
1926 a8083063 Iustin Pop
    """Return the list of nodes which are in the configuration.
1927 a8083063 Iustin Pop

1928 c41eea6e Iustin Pop
    This function is for internal use, when the config lock is already
1929 c41eea6e Iustin Pop
    held.
1930 c41eea6e Iustin Pop

1931 c41eea6e Iustin Pop
    @rtype: list
1932 f78ede4e Guido Trotter

1933 a8083063 Iustin Pop
    """
1934 a8083063 Iustin Pop
    return self._config_data.nodes.keys()
1935 a8083063 Iustin Pop
1936 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
1937 f78ede4e Guido Trotter
  def GetNodeList(self):
1938 f78ede4e Guido Trotter
    """Return the list of nodes which are in the configuration.
1939 f78ede4e Guido Trotter

1940 f78ede4e Guido Trotter
    """
1941 f78ede4e Guido Trotter
    return self._UnlockedGetNodeList()
1942 f78ede4e Guido Trotter
1943 6819dc49 Iustin Pop
  def _UnlockedGetOnlineNodeList(self):
1944 94a02bb5 Iustin Pop
    """Return the list of nodes which are online.
1945 94a02bb5 Iustin Pop

1946 94a02bb5 Iustin Pop
    """
1947 94a02bb5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1948 94a02bb5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1949 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.offline]
1950 94a02bb5 Iustin Pop
1951 94a02bb5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1952 6819dc49 Iustin Pop
  def GetOnlineNodeList(self):
1953 6819dc49 Iustin Pop
    """Return the list of nodes which are online.
1954 6819dc49 Iustin Pop

1955 6819dc49 Iustin Pop
    """
1956 6819dc49 Iustin Pop
    return self._UnlockedGetOnlineNodeList()
1957 6819dc49 Iustin Pop
1958 6819dc49 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1959 075b62ca Iustin Pop
  def GetVmCapableNodeList(self):
1960 075b62ca Iustin Pop
    """Return the list of nodes which are not vm capable.
1961 075b62ca Iustin Pop

1962 075b62ca Iustin Pop
    """
1963 075b62ca Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1964 075b62ca Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1965 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if node.vm_capable]
1966 075b62ca Iustin Pop
1967 075b62ca Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1968 8bf9e9a5 Iustin Pop
  def GetNonVmCapableNodeList(self):
1969 8bf9e9a5 Iustin Pop
    """Return the list of nodes which are not vm capable.
1970 8bf9e9a5 Iustin Pop

1971 8bf9e9a5 Iustin Pop
    """
1972 8bf9e9a5 Iustin Pop
    all_nodes = [self._UnlockedGetNodeInfo(node)
1973 8bf9e9a5 Iustin Pop
                 for node in self._UnlockedGetNodeList()]
1974 1c3231aa Thomas Thrainer
    return [node.uuid for node in all_nodes if not node.vm_capable]
1975 8bf9e9a5 Iustin Pop
1976 8bf9e9a5 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1977 1c3231aa Thomas Thrainer
  def GetMultiNodeInfo(self, node_uuids):
1978 f5eaa3c1 Iustin Pop
    """Get the configuration of multiple nodes.
1979 f5eaa3c1 Iustin Pop

1980 1c3231aa Thomas Thrainer
    @param node_uuids: list of node UUIDs
1981 f5eaa3c1 Iustin Pop
    @rtype: list
1982 f5eaa3c1 Iustin Pop
    @return: list of tuples of (node, node_info), where node_info is
1983 f5eaa3c1 Iustin Pop
        what would GetNodeInfo return for the node, in the original
1984 f5eaa3c1 Iustin Pop
        order
1985 f5eaa3c1 Iustin Pop

1986 f5eaa3c1 Iustin Pop
    """
1987 1c3231aa Thomas Thrainer
    return [(uuid, self._UnlockedGetNodeInfo(uuid)) for uuid in node_uuids]
1988 1c3231aa Thomas Thrainer
1989 1c3231aa Thomas Thrainer
  def _UnlockedGetAllNodesInfo(self):
1990 1c3231aa Thomas Thrainer
    """Gets configuration of all nodes.
1991 1c3231aa Thomas Thrainer

1992 1c3231aa Thomas Thrainer
    @note: See L{GetAllNodesInfo}
1993 1c3231aa Thomas Thrainer

1994 1c3231aa Thomas Thrainer
    """
1995 1c3231aa Thomas Thrainer
    return dict([(node_uuid, self._UnlockedGetNodeInfo(node_uuid))
1996 1c3231aa Thomas Thrainer
                 for node_uuid in self._UnlockedGetNodeList()])
1997 f5eaa3c1 Iustin Pop
1998 f5eaa3c1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
1999 d65e5776 Iustin Pop
  def GetAllNodesInfo(self):
2000 d65e5776 Iustin Pop
    """Get the configuration of all nodes.
2001 d65e5776 Iustin Pop

2002 d65e5776 Iustin Pop
    @rtype: dict
2003 ec0292f1 Iustin Pop
    @return: dict of (node, node_info), where node_info is what
2004 d65e5776 Iustin Pop
              would GetNodeInfo return for the node
2005 d65e5776 Iustin Pop

2006 d65e5776 Iustin Pop
    """
2007 ee14d800 Michael Hanselmann
    return self._UnlockedGetAllNodesInfo()
2008 ee14d800 Michael Hanselmann
2009 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeInfoByName(self, node_name):
2010 1c3231aa Thomas Thrainer
    for node in self._UnlockedGetAllNodesInfo().values():
2011 1c3231aa Thomas Thrainer
      if node.name == node_name:
2012 1c3231aa Thomas Thrainer
        return node
2013 1c3231aa Thomas Thrainer
    return None
2014 ee14d800 Michael Hanselmann
2015 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2016 1c3231aa Thomas Thrainer
  def GetNodeInfoByName(self, node_name):
2017 1c3231aa Thomas Thrainer
    """Get the L{objects.Node} object for a named node.
2018 1c3231aa Thomas Thrainer

2019 1c3231aa Thomas Thrainer
    @param node_name: name of the node to get information for
2020 1c3231aa Thomas Thrainer
    @type node_name: string
2021 1c3231aa Thomas Thrainer
    @return: the corresponding L{objects.Node} instance or None if no
2022 1c3231aa Thomas Thrainer
          information is available
2023 1c3231aa Thomas Thrainer

2024 1c3231aa Thomas Thrainer
    """
2025 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeInfoByName(node_name)
2026 1c3231aa Thomas Thrainer
2027 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeName(self, node_spec):
2028 1c3231aa Thomas Thrainer
    if isinstance(node_spec, objects.Node):
2029 1c3231aa Thomas Thrainer
      return node_spec.name
2030 1c3231aa Thomas Thrainer
    elif isinstance(node_spec, basestring):
2031 1c3231aa Thomas Thrainer
      node_info = self._UnlockedGetNodeInfo(node_spec)
2032 1c3231aa Thomas Thrainer
      if node_info is None:
2033 1c3231aa Thomas Thrainer
        raise errors.OpExecError("Unknown node: %s" % node_spec)
2034 1c3231aa Thomas Thrainer
      return node_info.name
2035 1c3231aa Thomas Thrainer
    else:
2036 1c3231aa Thomas Thrainer
      raise errors.ProgrammerError("Can't handle node spec '%s'" % node_spec)
2037 1c3231aa Thomas Thrainer
2038 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2039 1c3231aa Thomas Thrainer
  def GetNodeName(self, node_spec):
2040 1c3231aa Thomas Thrainer
    """Gets the node name for the passed node.
2041 1c3231aa Thomas Thrainer

2042 1c3231aa Thomas Thrainer
    @param node_spec: node to get names for
2043 1c3231aa Thomas Thrainer
    @type node_spec: either node UUID or a L{objects.Node} object
2044 1c3231aa Thomas Thrainer
    @rtype: string
2045 1c3231aa Thomas Thrainer
    @return: node name
2046 1c3231aa Thomas Thrainer

2047 1c3231aa Thomas Thrainer
    """
2048 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeName(node_spec)
2049 1c3231aa Thomas Thrainer
2050 1c3231aa Thomas Thrainer
  def _UnlockedGetNodeNames(self, node_specs):
2051 1c3231aa Thomas Thrainer
    return [self._UnlockedGetNodeName(node_spec) for node_spec in node_specs]
2052 1c3231aa Thomas Thrainer
2053 1c3231aa Thomas Thrainer
  @locking.ssynchronized(_config_lock, shared=1)
2054 1c3231aa Thomas Thrainer
  def GetNodeNames(self, node_specs):
2055 1c3231aa Thomas Thrainer
    """Gets the node names for the passed list of nodes.
2056 1c3231aa Thomas Thrainer

2057 1c3231aa Thomas Thrainer
    @param node_specs: list of nodes to get names for
2058 1c3231aa Thomas Thrainer
    @type node_specs: list of either node UUIDs or L{objects.Node} objects
2059 1c3231aa Thomas Thrainer
    @rtype: list of strings
2060 1c3231aa Thomas Thrainer
    @return: list of node names
2061 ee14d800 Michael Hanselmann

2062 ee14d800 Michael Hanselmann
    """
2063 1c3231aa Thomas Thrainer
    return self._UnlockedGetNodeNames(node_specs)
2064 d65e5776 Iustin Pop
2065 9d5b1371 Michael Hanselmann
  @locking.ssynchronized(_config_lock, shared=1)
2066 1c3231aa Thomas Thrainer
  def GetNodeGroupsFromNodes(self, node_uuids):
2067 9d5b1371 Michael Hanselmann
    """Returns groups for a list of nodes.
2068 9d5b1371 Michael Hanselmann

2069 1c3231aa Thomas Thrainer
    @type node_uuids: list of string
2070 1c3231aa Thomas Thrainer
    @param node_uuids: List of node UUIDs
2071 9d5b1371 Michael Hanselmann
    @rtype: frozenset
2072 9d5b1371 Michael Hanselmann

2073 9d5b1371 Michael Hanselmann
    """
2074 1c3231aa Thomas Thrainer
    return frozenset(self._UnlockedGetNodeInfo(uuid).group
2075 1c3231aa Thomas Thrainer
                     for uuid in node_uuids)
2076 9d5b1371 Michael Hanselmann
2077 23f06b2b Iustin Pop
  def _UnlockedGetMasterCandidateStats(self, exceptions=None):
2078 ec0292f1 Iustin Pop
    """Get the number of current and maximum desired and possible candidates.
2079 ec0292f1 Iustin Pop

2080 23f06b2b Iustin Pop
    @type exceptions: list
2081 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
2082 ec0292f1 Iustin Pop
    @rtype: tuple
2083 e623dbe3 Guido Trotter
    @return: tuple of (current, desired and possible, possible)
2084 ec0292f1 Iustin Pop

2085 ec0292f1 Iustin Pop
    """
2086 e623dbe3 Guido Trotter
    mc_now = mc_should = mc_max = 0
2087 23f06b2b Iustin Pop
    for node in self._config_data.nodes.values():
2088 1c3231aa Thomas Thrainer
      if exceptions and node.uuid in exceptions:
2089 23f06b2b Iustin Pop
        continue
2090 490acd18 Iustin Pop
      if not (node.offline or node.drained) and node.master_capable:
2091 ec0292f1 Iustin Pop
        mc_max += 1
2092 ec0292f1 Iustin Pop
      if node.master_candidate:
2093 ec0292f1 Iustin Pop
        mc_now += 1
2094 e623dbe3 Guido Trotter
    mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
2095 e623dbe3 Guido Trotter
    return (mc_now, mc_should, mc_max)
2096 ec0292f1 Iustin Pop
2097 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock, shared=1)
2098 23f06b2b Iustin Pop
  def GetMasterCandidateStats(self, exceptions=None):
2099 ec0292f1 Iustin Pop
    """Get the number of current and maximum possible candidates.
2100 ec0292f1 Iustin Pop

2101 ec0292f1 Iustin Pop
    This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
2102 ec0292f1 Iustin Pop

2103 23f06b2b Iustin Pop
    @type exceptions: list
2104 23f06b2b Iustin Pop
    @param exceptions: if passed, list of nodes that should be ignored
2105 ec0292f1 Iustin Pop
    @rtype: tuple
2106 ec0292f1 Iustin Pop
    @return: tuple of (current, max)
2107 ec0292f1 Iustin Pop

2108 ec0292f1 Iustin Pop
    """
2109 23f06b2b Iustin Pop
    return self._UnlockedGetMasterCandidateStats(exceptions)
2110 ec0292f1 Iustin Pop
2111 ec0292f1 Iustin Pop
  @locking.ssynchronized(_config_lock)
2112 1c3231aa Thomas Thrainer
  def MaintainCandidatePool(self, exception_node_uuids):
2113 ec0292f1 Iustin Pop
    """Try to grow the candidate pool to the desired size.
2114 ec0292f1 Iustin Pop

2115 1c3231aa Thomas Thrainer
    @type exception_node_uuids: list
2116 1c3231aa Thomas Thrainer
    @param exception_node_uuids: if passed, list of nodes that should be ignored
2117 ec0292f1 Iustin Pop
    @rtype: list
2118 ee513a66 Iustin Pop
    @return: list with the adjusted nodes (L{objects.Node} instances)
2119 ec0292f1 Iustin Pop

2120 ec0292f1 Iustin Pop
    """
2121 1c3231aa Thomas Thrainer
    mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(
2122 1c3231aa Thomas Thrainer
                          exception_node_uuids)
2123 ec0292f1 Iustin Pop
    mod_list = []
2124 ec0292f1 Iustin Pop
    if mc_now < mc_max:
2125 ec0292f1 Iustin Pop
      node_list = self._config_data.nodes.keys()
2126 ec0292f1 Iustin Pop
      random.shuffle(node_list)
2127 1c3231aa Thomas Thrainer
      for uuid in node_list:
2128 ec0292f1 Iustin Pop
        if mc_now >= mc_max:
2129 ec0292f1 Iustin Pop
          break
2130 1c3231aa Thomas Thrainer
        node = self._config_data.nodes[uuid]
2131 44485f49 Guido Trotter
        if (node.master_candidate or node.offline or node.drained or
2132 1c3231aa Thomas Thrainer
            node.uuid in exception_node_uuids or not node.master_capable):
2133 ec0292f1 Iustin Pop
          continue
2134 ee513a66 Iustin Pop
        mod_list.append(node)
2135 ec0292f1 Iustin Pop
        node.master_candidate = True
2136 ec0292f1 Iustin Pop
        node.serial_no += 1
2137 ec0292f1 Iustin Pop
        mc_now += 1
2138 ec0292f1 Iustin Pop
      if mc_now != mc_max:
2139 ec0292f1 Iustin Pop
        # this should not happen
2140 ec0292f1 Iustin Pop
        logging.warning("Warning: MaintainCandidatePool didn't manage to"
2141 ec0292f1 Iustin Pop
                        " fill the candidate pool (%d/%d)", mc_now, mc_max)
2142 ec0292f1 Iustin Pop
      if mod_list:
2143 ec0292f1 Iustin Pop
        self._config_data.cluster.serial_no += 1
2144 ec0292f1 Iustin Pop
        self._WriteConfig()
2145 ec0292f1 Iustin Pop
2146 ec0292f1 Iustin Pop
    return mod_list
2147 ec0292f1 Iustin Pop
2148 1c3231aa Thomas Thrainer
  def _UnlockedAddNodeToGroup(self, node_uuid, nodegroup_uuid):
2149 190e3cb6 Guido Trotter
    """Add a given node to the specified group.
2150 190e3cb6 Guido Trotter

2151 190e3cb6 Guido Trotter
    """
2152 190e3cb6 Guido Trotter
    if nodegroup_uuid not in self._config_data.nodegroups:
2153 190e3cb6 Guido Trotter
      # This can happen if a node group gets deleted between its lookup and
2154 190e3cb6 Guido Trotter
      # when we're adding the first node to it, since we don't keep a lock in
2155 190e3cb6 Guido Trotter
      # the meantime. It's ok though, as we'll fail cleanly if the node group
2156 190e3cb6 Guido Trotter
      # is not found anymore.
2157 f936c153 Iustin Pop
      raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
2158 1c3231aa Thomas Thrainer
    if node_uuid not in self._config_data.nodegroups[nodegroup_uuid].members:
2159 1c3231aa Thomas Thrainer
      self._config_data.nodegroups[nodegroup_uuid].members.append(node_uuid)
2160 190e3cb6 Guido Trotter
2161 190e3cb6 Guido Trotter
  def _UnlockedRemoveNodeFromGroup(self, node):
2162 190e3cb6 Guido Trotter
    """Remove a given node from its group.
2163 190e3cb6 Guido Trotter

2164 190e3cb6 Guido Trotter
    """
2165 f936c153 Iustin Pop
    nodegroup = node.group
2166 190e3cb6 Guido Trotter
    if nodegroup not in self._config_data.nodegroups:
2167 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' has unknown node group '%s'"
2168 1c3231aa Thomas Thrainer
                      " (while being removed from it)", node.uuid, nodegroup)
2169 190e3cb6 Guido Trotter
    nodegroup_obj = self._config_data.nodegroups[nodegroup]
2170 1c3231aa Thomas Thrainer
    if node.uuid not in nodegroup_obj.members:
2171 f936c153 Iustin Pop
      logging.warning("Warning: node '%s' not a member of its node group '%s'"
2172 1c3231aa Thomas Thrainer
                      " (while being removed from it)", node.uuid, nodegroup)
2173 190e3cb6 Guido Trotter
    else:
2174 1c3231aa Thomas Thrainer
      nodegroup_obj.members.remove(node.uuid)
2175 190e3cb6 Guido Trotter
2176 54c31fd3 Michael Hanselmann
  @locking.ssynchronized(_config_lock)
2177 54c31fd3 Michael Hanselmann
  def AssignGroupNodes(self, mods):
2178 54c31fd3 Michael Hanselmann
    """Changes the group of a number of nodes.
2179 54c31fd3 Michael Hanselmann

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

2183 54c31fd3 Michael Hanselmann
    """
2184 54c31fd3 Michael Hanselmann
    groups = self._config_data.nodegroups
2185 54c31fd3 Michael Hanselmann
    nodes = self._config_data.nodes
2186 54c31fd3 Michael Hanselmann
2187 54c31fd3 Michael Hanselmann
    resmod = []
2188 54c31fd3 Michael Hanselmann
2189 1c3231aa Thomas Thrainer
    # Try to resolve UUIDs first
2190 1c3231aa Thomas Thrainer
    for (node_uuid, new_group_uuid) in mods:
2191 54c31fd3 Michael Hanselmann
      try:
2192 1c3231aa Thomas Thrainer
        node = nodes[node_uuid]
2193 54c31fd3 Michael Hanselmann
      except KeyError:
2194 1c3231aa Thomas Thrainer
        raise errors.ConfigurationError("Unable to find node '%s'" % node_uuid)
2195 54c31fd3 Michael Hanselmann
2196 54c31fd3 Michael Hanselmann
      if node.group == new_group_uuid:
2197 54c31fd3 Michael Hanselmann
        # Node is being assigned to its current group
2198 54c31fd3 Michael Hanselmann
        logging.debug("Node '%s' was assigned to its current group (%s)",
2199 1c3231aa Thomas Thrainer
                      node_uuid, node.group)
2200 54c31fd3 Michael Hanselmann
        continue
2201 54c31fd3 Michael Hanselmann
2202 54c31fd3 Michael Hanselmann
      # Try to find current group of node
2203 54c31fd3 Michael Hanselmann
      try:
2204 54c31fd3 Michael Hanselmann
        old_group = groups[node.group]
2205 54c31fd3 Michael Hanselmann
      except KeyError:
2206 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find old group '%s'" %
2207 54c31fd3 Michael Hanselmann
                                        node.group)
2208 54c31fd3 Michael Hanselmann
2209 54c31fd3 Michael Hanselmann
      # Try to find new group for node
2210 54c31fd3 Michael Hanselmann
      try:
2211 54c31fd3 Michael Hanselmann
        new_group = groups[new_group_uuid]
2212 54c31fd3 Michael Hanselmann
      except KeyError:
2213 54c31fd3 Michael Hanselmann
        raise errors.ConfigurationError("Unable to find new group '%s'" %
2214 54c31fd3 Michael Hanselmann
                                        new_group_uuid)
2215 54c31fd3 Michael Hanselmann
2216 1c3231aa Thomas Thrainer
      assert node.uuid in old_group.members, \
2217 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' not listed in members for its"
2218 1c3231aa Thomas Thrainer
         " old group '%s'" % (node.uuid, old_group.uuid))
2219 1c3231aa Thomas Thrainer
      assert node.uuid not in new_group.members, \
2220 54c31fd3 Michael Hanselmann
        ("Inconsistent configuration: node '%s' already listed in members for"
2221 1c3231aa Thomas Thrainer
         " its new group '%s'" % (node.uuid, new_group.uuid))
2222 54c31fd3 Michael Hanselmann
2223 54c31fd3 Michael Hanselmann
      resmod.append((node, old_group, new_group))
2224 54c31fd3 Michael Hanselmann
2225 54c31fd3 Michael Hanselmann
    # Apply changes
2226 54c31fd3 Michael Hanselmann
    for (node, old_group, new_group) in resmod:
2227 54c31fd3 Michael Hanselmann
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
2228 54c31fd3 Michael Hanselmann
        "Assigning to current group is not possible"
2229 54c31fd3 Michael Hanselmann
2230 54c31fd3 Michael Hanselmann
      node.group = new_group.uuid
2231 54c31fd3 Michael Hanselmann
2232 54c31fd3 Michael Hanselmann
      # Update members of involved groups
2233 1c3231aa Thomas Thrainer
      if node.uuid in old_group.members:
2234 1c3231aa Thomas Thrainer
        old_group.members.remove(node.uuid)
2235 1c3231aa Thomas Thrainer
      if node.uuid not in new_group.members:
2236 1c3231aa Thomas Thrainer
        new_group.members.append(node.uuid)
2237 54c31fd3 Michael Hanselmann
2238 54c31fd3 Michael Hanselmann
    # Update timestamps and serials (only once per node/group object)
2239 54c31fd3 Michael Hanselmann
    now = time.time()
2240 75191077 Michael Hanselmann
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142
2241 54c31fd3 Michael Hanselmann
      obj.serial_no += 1
2242 54c31fd3 Michael Hanselmann
      obj.mtime = now
2243 54c31fd3 Michael Hanselmann
2244 54c31fd3 Michael Hanselmann
    # Force ssconf update
2245 54c31fd3 Michael Hanselmann
    self._config_data.cluster.serial_no += 1
2246 54c31fd3 Michael Hanselmann
2247 54c31fd3 Michael Hanselmann
    self._WriteConfig()
2248 54c31fd3 Michael Hanselmann
2249 a8083063 Iustin Pop
  def _BumpSerialNo(self):
2250 a8083063 Iustin Pop
    """Bump up the serial number of the config.
2251 a8083063 Iustin Pop

2252 a8083063 Iustin Pop
    """
2253 9d38c6e1 Iustin Pop
    self._config_data.serial_no += 1
2254 d693c864 Iustin Pop
    self._config_data.mtime = time.time()
2255 a8083063 Iustin Pop
2256 76d5d3a3 Iustin Pop
  def _AllUUIDObjects(self):
2257 76d5d3a3 Iustin Pop
    """Returns all objects with uuid attributes.
2258 76d5d3a3 Iustin Pop

2259 76d5d3a3 Iustin Pop
    """
2260 76d5d3a3 Iustin Pop
    return (self._config_data.instances.values() +
2261 76d5d3a3 Iustin Pop
            self._config_data.nodes.values() +
2262 3df43542 Guido Trotter
            self._config_data.nodegroups.values() +
2263 e0519c34 Dimitris Aragiorgis
            self._config_data.networks.values() +
2264 b87a9c5f Christos Stavrakakis
            self._AllDisks() +
2265 b87a9c5f Christos Stavrakakis
            self._AllNICs() +
2266 76d5d3a3 Iustin Pop
            [self._config_data.cluster])
2267 76d5d3a3 Iustin Pop
2268 eb180fe2 Iustin Pop
  def _OpenConfig(self, accept_foreign):
2269 a8083063 Iustin Pop
    """Read the config data from disk.
2270 a8083063 Iustin Pop

2271 a8083063 Iustin Pop
    """
2272 13998ef2 Michael Hanselmann
    raw_data = utils.ReadFile(self._cfg_file)
2273 13998ef2 Michael Hanselmann
2274 a8083063 Iustin Pop
    try:
2275 13998ef2 Michael Hanselmann
      data = objects.ConfigData.FromDict(serializer.Load(raw_data))
2276 13998ef2 Michael Hanselmann
    except Exception, err:
2277 13998ef2 Michael Hanselmann
      raise errors.ConfigurationError(err)
2278 5b263ed7 Michael Hanselmann
2279 5b263ed7 Michael Hanselmann
    # Make sure the configuration has the right version
2280 5b263ed7 Michael Hanselmann
    _ValidateConfig(data)
2281 5b263ed7 Michael Hanselmann
2282 3ccb3a64 Michael Hanselmann
    if (not hasattr(data, "cluster") or
2283 3ccb3a64 Michael Hanselmann
        not hasattr(data.cluster, "rsahostkeypub")):
2284 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Incomplete configuration"
2285 243cdbcc Michael Hanselmann
                                      " (missing cluster.rsahostkeypub)")
2286 90d726a8 Iustin Pop
2287 1c3231aa Thomas Thrainer
    if not data.cluster.master_node in data.nodes:
2288 1c3231aa Thomas Thrainer
      msg = ("The configuration denotes node %s as master, but does not"
2289 1c3231aa Thomas Thrainer
             " contain information about this node" %
2290 1c3231aa Thomas Thrainer
             data.cluster.master_node)
2291 1c3231aa Thomas Thrainer
      raise errors.ConfigurationError(msg)
2292 1c3231aa Thomas Thrainer
2293 1c3231aa Thomas Thrainer
    master_info = data.nodes[data.cluster.master_node]
2294 1c3231aa Thomas Thrainer
    if master_info.name != self._my_hostname and not accept_foreign:
2295 eb180fe2 Iustin Pop
      msg = ("The configuration denotes node %s as master, while my"
2296 eb180fe2 Iustin Pop
             " hostname is %s; opening a foreign configuration is only"
2297 eb180fe2 Iustin Pop
             " possible in accept_foreign mode" %
2298 1c3231aa Thomas Thrainer
             (master_info.name, self._my_hostname))
2299 eb180fe2 Iustin Pop
      raise errors.ConfigurationError(msg)
2300 eb180fe2 Iustin Pop
2301 a8083063 Iustin Pop
    self._config_data = data
2302 3c7f6c44 Iustin Pop
    # reset the last serial as -1 so that the next write will cause
2303 0779e3aa Iustin Pop
    # ssconf update
2304 0779e3aa Iustin Pop
    self._last_cluster_serial = -1
2305 a8083063 Iustin Pop
2306 45f62156 Bernardo Dal Seno
    # Upgrade configuration if needed
2307 76d5d3a3 Iustin Pop
    self._UpgradeConfig()
2308 76d5d3a3 Iustin Pop
2309 bd407597 Iustin Pop
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
2310 bd407597 Iustin Pop
2311 76d5d3a3 Iustin Pop
  def _UpgradeConfig(self):
2312 45f62156 Bernardo Dal Seno
    """Run any upgrade steps.
2313 76d5d3a3 Iustin Pop

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

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

2324 76d5d3a3 Iustin Pop
    """
2325 45f62156 Bernardo Dal Seno
    # Keep a copy of the persistent part of _config_data to check for changes
2326 45f62156 Bernardo Dal Seno
    # Serialization doesn't guarantee order in dictionaries
2327 45f62156 Bernardo Dal Seno
    oldconf = copy.deepcopy(self._config_data.ToDict())
2328 45f62156 Bernardo Dal Seno
2329 45f62156 Bernardo Dal Seno
    # In-object upgrades
2330 45f62156 Bernardo Dal Seno
    self._config_data.UpgradeConfig()
2331 45f62156 Bernardo Dal Seno
2332 76d5d3a3 Iustin Pop
    for item in self._AllUUIDObjects():
2333 76d5d3a3 Iustin Pop
      if item.uuid is None:
2334 4fae38c5 Guido Trotter
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
2335 f9e81396 Guido Trotter
    if not self._config_data.nodegroups:
2336 75cf411a Adeodato Simo
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
2337 75cf411a Adeodato Simo
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
2338 75cf411a Adeodato Simo
                                            members=[])
2339 e11a1b77 Adeodato Simo
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
2340 190e3cb6 Guido Trotter
    for node in self._config_data.nodes.values():
2341 f936c153 Iustin Pop
      if not node.group:
2342 f936c153 Iustin Pop
        node.group = self.LookupNodeGroup(None)
2343 190e3cb6 Guido Trotter
      # This is technically *not* an upgrade, but needs to be done both when
2344 190e3cb6 Guido Trotter
      # nodegroups are being added, and upon normally loading the config,
2345 190e3cb6 Guido Trotter
      # because the members list of a node group is discarded upon
2346 190e3cb6 Guido Trotter
      # serializing/deserializing the object.
2347 1c3231aa Thomas Thrainer
      self._UnlockedAddNodeToGroup(node.uuid, node.group)
2348 45f62156 Bernardo Dal Seno
2349 45f62156 Bernardo Dal Seno
    modified = (oldconf != self._config_data.ToDict())
2350 76d5d3a3 Iustin Pop
    if modified:
2351 76d5d3a3 Iustin Pop
      self._WriteConfig()
2352 4fae38c5 Guido Trotter
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
2353 4fae38c5 Guido Trotter
      # only called at config init time, without the lock held
2354 4fae38c5 Guido Trotter
      self.DropECReservations(_UPGRADE_CONFIG_JID)
2355 04db1880 Bernardo Dal Seno
    else:
2356 04db1880 Bernardo Dal Seno
      config_errors = self._UnlockedVerifyConfig()
2357 04db1880 Bernardo Dal Seno
      if config_errors:
2358 04db1880 Bernardo Dal Seno
        errmsg = ("Loaded configuration data is not consistent: %s" %
2359 04db1880 Bernardo Dal Seno
                  (utils.CommaJoin(config_errors)))
2360 04db1880 Bernardo Dal Seno
        logging.critical(errmsg)
2361 4fae38c5 Guido Trotter
2362 a4eae71f Michael Hanselmann
  def _DistributeConfig(self, feedback_fn):
2363 a8083063 Iustin Pop
    """Distribute the configuration to the other nodes.
2364 a8083063 Iustin Pop

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

2368 a8083063 Iustin Pop
    """
2369 a8083063 Iustin Pop
    if self._offline:
2370 a8083063 Iustin Pop
      return True
2371 a4eae71f Michael Hanselmann
2372 a8083063 Iustin Pop
    bad = False
2373 a8083063 Iustin Pop
2374 6a5b8b4b Iustin Pop
    node_list = []
2375 6a5b8b4b Iustin Pop
    addr_list = []
2376 6a5b8b4b Iustin Pop
    myhostname = self._my_hostname
2377 6b294c53 Iustin Pop
    # we can skip checking whether _UnlockedGetNodeInfo returns None
2378 6b294c53 Iustin Pop
    # since the node list comes from _UnlocketGetNodeList, and we are
2379 6b294c53 Iustin Pop
    # called with the lock held, so no modifications should take place
2380 6b294c53 Iustin Pop
    # in between
2381 1c3231aa Thomas Thrainer
    for node_uuid in self._UnlockedGetNodeList():
2382 1c3231aa Thomas Thrainer
      node_info = self._UnlockedGetNodeInfo(node_uuid)
2383 1c3231aa Thomas Thrainer
      if node_info.name == myhostname or not node_info.master_candidate:
2384 6a5b8b4b Iustin Pop
        continue
2385 6a5b8b4b Iustin Pop
      node_list.append(node_info.name)
2386 6a5b8b4b Iustin Pop
      addr_list.append(node_info.primary_ip)
2387 6b294c53 Iustin Pop
2388 415a7304 Michael Hanselmann
    # TODO: Use dedicated resolver talking to config writer for name resolution
2389 415a7304 Michael Hanselmann
    result = \
2390 b2acdbdc Michael Hanselmann
      self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
2391 1b54fc6c Guido Trotter
    for to_node, to_result in result.items():
2392 3cebe102 Michael Hanselmann
      msg = to_result.fail_msg
2393 1b54fc6c Guido Trotter
      if msg:
2394 1b54fc6c Guido Trotter
        msg = ("Copy of file %s to node %s failed: %s" %
2395 dd7db360 Iustin Pop
               (self._cfg_file, to_node, msg))
2396 1b54fc6c Guido Trotter
        logging.error(msg)
2397 a4eae71f Michael Hanselmann
2398 a4eae71f Michael Hanselmann
        if feedback_fn:
2399 a4eae71f Michael Hanselmann
          feedback_fn(msg)
2400 a4eae71f Michael Hanselmann
2401 a8083063 Iustin Pop
        bad = True
2402 a4eae71f Michael Hanselmann
2403 a8083063 Iustin Pop
    return not bad
2404 a8083063 Iustin Pop
2405 a4eae71f Michael Hanselmann
  def _WriteConfig(self, destination=None, feedback_fn=None):
2406 a8083063 Iustin Pop
    """Write the configuration data to persistent storage.
2407 a8083063 Iustin Pop

2408 a8083063 Iustin Pop
    """
2409 a4eae71f Michael Hanselmann
    assert feedback_fn is None or callable(feedback_fn)
2410 a4eae71f Michael Hanselmann
2411 d2231b8c Iustin Pop
    # Warn on config errors, but don't abort the save - the
2412 d2231b8c Iustin Pop
    # configuration has already been modified, and we can't revert;
2413 d2231b8c Iustin Pop
    # the best we can do is to warn the user and save as is, leaving
2414 d2231b8c Iustin Pop
    # recovery to the user
2415 4a89c54a Iustin Pop
    config_errors = self._UnlockedVerifyConfig()
2416 4a89c54a Iustin Pop
    if config_errors:
2417 d2231b8c Iustin Pop
      errmsg = ("Configuration data is not consistent: %s" %
2418 1f864b60 Iustin Pop
                (utils.CommaJoin(config_errors)))
2419 d2231b8c Iustin Pop
      logging.critical(errmsg)
2420 d2231b8c Iustin Pop
      if feedback_fn:
2421 d2231b8c Iustin Pop
        feedback_fn(errmsg)
2422 d2231b8c Iustin Pop
2423 a8083063 Iustin Pop
    if destination is None:
2424 a8083063 Iustin Pop
      destination = self._cfg_file
2425 a8083063 Iustin Pop
    self._BumpSerialNo()
2426 8d14b30d Iustin Pop
    txt = serializer.Dump(self._config_data.ToDict())
2427 13998ef2 Michael Hanselmann
2428 e60c73a1 René Nussbaumer
    getents = self._getents()
2429 bd407597 Iustin Pop
    try:
2430 bd407597 Iustin Pop
      fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
2431 bd407597 Iustin Pop
                               close=False, gid=getents.confd_gid, mode=0640)
2432 bd407597 Iustin Pop
    except errors.LockError:
2433 bd407597 Iustin Pop
      raise errors.ConfigurationError("The configuration file has been"
2434 bd407597 Iustin Pop
                                      " modified since the last write, cannot"
2435 bd407597 Iustin Pop
                                      " update")
2436 bd407597 Iustin Pop
    try:
2437 bd407597 Iustin Pop
      self._cfg_id = utils.GetFileID(fd=fd)
2438 bd407597 Iustin Pop
    finally:
2439 bd407597 Iustin Pop
      os.close(fd)
2440 13998ef2 Michael Hanselmann
2441 14e15659 Iustin Pop
    self.write_count += 1
2442 3d3a04bc Iustin Pop
2443 f56618e0 Iustin Pop
    # and redistribute the config file to master candidates
2444 a4eae71f Michael Hanselmann
    self._DistributeConfig(feedback_fn)
2445 a8083063 Iustin Pop
2446 54d1a06e Michael Hanselmann
    # Write ssconf files on all nodes (including locally)
2447 0779e3aa Iustin Pop
    if self._last_cluster_serial < self._config_data.cluster.serial_no:
2448 d9a855f1 Michael Hanselmann
      if not self._offline:
2449 b2acdbdc Michael Hanselmann
        result = self._GetRpc(None).call_write_ssconf_files(
2450 1c3231aa Thomas Thrainer
          self._UnlockedGetNodeNames(self._UnlockedGetOnlineNodeList()),
2451 e1e75d00 Iustin Pop
          self._UnlockedGetSsconfValues())
2452 a4eae71f Michael Hanselmann
2453 e1e75d00 Iustin Pop
        for nname, nresu in result.items():
2454 3cebe102 Michael Hanselmann
          msg = nresu.fail_msg
2455 e1e75d00 Iustin Pop
          if msg:
2456 a4eae71f Michael Hanselmann
            errmsg = ("Error while uploading ssconf files to"
2457 a4eae71f Michael Hanselmann
                      " node %s: %s" % (nname, msg))
2458 a4eae71f Michael Hanselmann
            logging.warning(errmsg)
2459 a4eae71f Michael Hanselmann
2460 a4eae71f Michael Hanselmann
            if feedback_fn:
2461 a4eae71f Michael Hanselmann
              feedback_fn(errmsg)
2462 a4eae71f Michael Hanselmann
2463 0779e3aa Iustin Pop
      self._last_cluster_serial = self._config_data.cluster.serial_no
2464 54d1a06e Michael Hanselmann
2465 def6577f Helga Velroyen
  def _GetAllHvparamsStrings(self, hypervisors):
2466 def6577f Helga Velroyen
    """Get the hvparams of all given hypervisors from the config.
2467 def6577f Helga Velroyen

2468 def6577f Helga Velroyen
    @type hypervisors: list of string
2469 def6577f Helga Velroyen
    @param hypervisors: list of hypervisor names
2470 def6577f Helga Velroyen
    @rtype: dict of strings
2471 def6577f Helga Velroyen
    @returns: dictionary mapping the hypervisor name to a string representation
2472 def6577f Helga Velroyen
      of the hypervisor's hvparams
2473 def6577f Helga Velroyen

2474 def6577f Helga Velroyen
    """
2475 def6577f Helga Velroyen
    hvparams = {}
2476 def6577f Helga Velroyen
    for hv in hypervisors:
2477 def6577f Helga Velroyen
      hvparams[hv] = self._UnlockedGetHvparamsString(hv)
2478 def6577f Helga Velroyen
    return hvparams
2479 def6577f Helga Velroyen
2480 def6577f Helga Velroyen
  @staticmethod
2481 def6577f Helga Velroyen
  def _ExtendByAllHvparamsStrings(ssconf_values, all_hvparams):
2482 def6577f Helga Velroyen
    """Extends the ssconf_values dictionary by hvparams.
2483 def6577f Helga Velroyen

2484 def6577f Helga Velroyen
    @type ssconf_values: dict of strings
2485 def6577f Helga Velroyen
    @param ssconf_values: dictionary mapping ssconf_keys to strings
2486 def6577f Helga Velroyen
      representing the content of ssconf files
2487 def6577f Helga Velroyen
    @type all_hvparams: dict of strings
2488 def6577f Helga Velroyen
    @param all_hvparams: dictionary mapping hypervisor names to a string
2489 def6577f Helga Velroyen
      representation of their hvparams
2490 def6577f Helga Velroyen
    @rtype: same as ssconf_values
2491 def6577f Helga Velroyen
    @returns: the ssconf_values dictionary extended by hvparams
2492 def6577f Helga Velroyen

2493 def6577f Helga Velroyen
    """
2494 def6577f Helga Velroyen
    for hv in all_hvparams:
2495 def6577f Helga Velroyen
      ssconf_key = constants.SS_HVPARAMS_PREF + hv
2496 def6577f Helga Velroyen
      ssconf_values[ssconf_key] = all_hvparams[hv]
2497 def6577f Helga Velroyen
    return ssconf_values
2498 def6577f Helga Velroyen
2499 03d1dba2 Michael Hanselmann
  def _UnlockedGetSsconfValues(self):
2500 054596f0 Iustin Pop
    """Return the values needed by ssconf.
2501 054596f0 Iustin Pop

2502 054596f0 Iustin Pop
    @rtype: dict
2503 054596f0 Iustin Pop
    @return: a dictionary with keys the ssconf names and values their
2504 054596f0 Iustin Pop
        associated value
2505 054596f0 Iustin Pop

2506 054596f0 Iustin Pop
    """
2507 a3316e4a Iustin Pop
    fn = "\n".join
2508 da4a52a3 Thomas Thrainer
    instance_names = utils.NiceSort(
2509 da4a52a3 Thomas Thrainer
                       [inst.name for inst in
2510 da4a52a3 Thomas Thrainer
                        self._UnlockedGetAllInstancesInfo().values()])
2511 1c3231aa Thomas Thrainer
    node_infos = self._UnlockedGetAllNodesInfo().values()
2512 1c3231aa Thomas Thrainer
    node_names = [node.name for node in node_infos]
2513 c3029d0a Luca Bigliardi
    node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
2514 1c3231aa Thomas Thrainer
                    for ninfo in node_infos]
2515 c3029d0a Luca Bigliardi
    node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
2516 1c3231aa Thomas Thrainer
                    for ninfo in node_infos]
2517 a3316e4a Iustin Pop
2518 81a49123 Iustin Pop
    instance_data = fn(instance_names)
2519 1c3231aa Thomas Thrainer
    off_data = fn(node.name for node in node_infos if node.offline)
2520 1c3231aa Thomas Thrainer
    on_data = fn(node.name for node in node_infos if not node.offline)
2521 1c3231aa Thomas Thrainer
    mc_data = fn(node.name for node in node_infos if node.master_candidate)
2522 1c3231aa Thomas Thrainer
    mc_ips_data = fn(node.primary_ip for node in node_infos
2523 8113a52e Luca Bigliardi
                     if node.master_candidate)
2524 a3316e4a Iustin Pop
    node_data = fn(node_names)
2525 f9780ccd Luca Bigliardi
    node_pri_ips_data = fn(node_pri_ips)
2526 f9780ccd Luca Bigliardi
    node_snd_ips_data = fn(node_snd_ips)
2527 f56618e0 Iustin Pop
2528 054596f0 Iustin Pop
    cluster = self._config_data.cluster
2529 5d60b3bd Iustin Pop
    cluster_tags = fn(cluster.GetTags())
2530 4f7a6a10 Iustin Pop
2531 4f7a6a10 Iustin Pop
    hypervisor_list = fn(cluster.enabled_hypervisors)
2532 def6577f Helga Velroyen
    all_hvparams = self._GetAllHvparamsStrings(constants.HYPER_TYPES)
2533 4f7a6a10 Iustin Pop
2534 0fbae49a Balazs Lecz
    uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
2535 0fbae49a Balazs Lecz
2536 6f076453 Guido Trotter
    nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
2537 6f076453 Guido Trotter
                  self._config_data.nodegroups.values()]
2538 6f076453 Guido Trotter
    nodegroups_data = fn(utils.NiceSort(nodegroups))
2539 6c0a75db Dimitris Aragiorgis
    networks = ["%s %s" % (net.uuid, net.name) for net in
2540 6c0a75db Dimitris Aragiorgis
                self._config_data.networks.values()]
2541 6c0a75db Dimitris Aragiorgis
    networks_data = fn(utils.NiceSort(networks))
2542 6f076453 Guido Trotter
2543 2afc9238 Iustin Pop
    ssconf_values = {
2544 054596f0 Iustin Pop
      constants.SS_CLUSTER_NAME: cluster.cluster_name,
2545 5d60b3bd Iustin Pop
      constants.SS_CLUSTER_TAGS: cluster_tags,
2546 054596f0 Iustin Pop
      constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
2547 4b97f902 Apollon Oikonomopoulos
      constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
2548 a3316e4a Iustin Pop
      constants.SS_MASTER_CANDIDATES: mc_data,
2549 8113a52e Luca Bigliardi
      constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
2550 054596f0 Iustin Pop
      constants.SS_MASTER_IP: cluster.master_ip,
2551 054596f0 Iustin Pop
      constants.SS_MASTER_NETDEV: cluster.master_netdev,
2552 5a8648eb Andrea Spadaccini
      constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
2553 1c3231aa Thomas Thrainer
      constants.SS_MASTER_NODE: self._UnlockedGetNodeName(cluster.master_node),
2554 a3316e4a Iustin Pop
      constants.SS_NODE_LIST: node_data,
2555 f9780ccd Luca Bigliardi
      constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
2556 f9780ccd Luca Bigliardi
      constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
2557 a3316e4a Iustin Pop
      constants.SS_OFFLINE_NODES: off_data,
2558 81a49123 Iustin Pop
      constants.SS_ONLINE_NODES: on_data,
2559 868a98ca Manuel Franceschini
      constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
2560 81a49123 Iustin Pop
      constants.SS_INSTANCE_LIST: instance_data,
2561 8a113c7a Iustin Pop
      constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
2562 4f7a6a10 Iustin Pop
      constants.SS_HYPERVISOR_LIST: hypervisor_list,
2563 5c465a95 Iustin Pop
      constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
2564 0fbae49a Balazs Lecz
      constants.SS_UID_POOL: uid_pool,
2565 6f076453 Guido Trotter
      constants.SS_NODEGROUPS: nodegroups_data,
2566 6c0a75db Dimitris Aragiorgis
      constants.SS_NETWORKS: networks_data,
2567 03d1dba2 Michael Hanselmann
      }
2568 def6577f Helga Velroyen
    ssconf_values = self._ExtendByAllHvparamsStrings(ssconf_values,
2569 def6577f Helga Velroyen
                                                     all_hvparams)
2570 2afc9238 Iustin Pop
    bad_values = [(k, v) for k, v in ssconf_values.items()
2571 2afc9238 Iustin Pop
                  if not isinstance(v, (str, basestring))]
2572 2afc9238 Iustin Pop
    if bad_values:
2573 2afc9238 Iustin Pop
      err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
2574 2afc9238 Iustin Pop
      raise errors.ConfigurationError("Some ssconf key(s) have non-string"
2575 2afc9238 Iustin Pop
                                      " values: %s" % err)
2576 2afc9238 Iustin Pop
    return ssconf_values
2577 03d1dba2 Michael Hanselmann
2578 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2579 d367b66c Manuel Franceschini
  def GetSsconfValues(self):
2580 d367b66c Manuel Franceschini
    """Wrapper using lock around _UnlockedGetSsconf().
2581 d367b66c Manuel Franceschini

2582 d367b66c Manuel Franceschini
    """
2583 d367b66c Manuel Franceschini
    return self._UnlockedGetSsconfValues()
2584 d367b66c Manuel Franceschini
2585 d367b66c Manuel Franceschini
  @locking.ssynchronized(_config_lock, shared=1)
2586 a8083063 Iustin Pop
  def GetVGName(self):
2587 a8083063 Iustin Pop
    """Return the volume group name.
2588 a8083063 Iustin Pop

2589 a8083063 Iustin Pop
    """
2590 a8083063 Iustin Pop
    return self._config_data.cluster.volume_group_name
2591 a8083063 Iustin Pop
2592 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2593 89ff8e15 Manuel Franceschini
  def SetVGName(self, vg_name):
2594 89ff8e15 Manuel Franceschini
    """Set the volume group name.
2595 89ff8e15 Manuel Franceschini

2596 89ff8e15 Manuel Franceschini
    """
2597 2d4011cd Manuel Franceschini
    self._config_data.cluster.volume_group_name = vg_name
2598 b9f72b4e Iustin Pop
    self._config_data.cluster.serial_no += 1
2599 89ff8e15 Manuel Franceschini
    self._WriteConfig()
2600 89ff8e15 Manuel Franceschini
2601 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2602 9e33896b Luca Bigliardi
  def GetDRBDHelper(self):
2603 9e33896b Luca Bigliardi
    """Return DRBD usermode helper.
2604 9e33896b Luca Bigliardi

2605 9e33896b Luca Bigliardi
    """
2606 9e33896b Luca Bigliardi
    return self._config_data.cluster.drbd_usermode_helper
2607 9e33896b Luca Bigliardi
2608 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock)
2609 9e33896b Luca Bigliardi
  def SetDRBDHelper(self, drbd_helper):
2610 9e33896b Luca Bigliardi
    """Set DRBD usermode helper.
2611 9e33896b Luca Bigliardi

2612 9e33896b Luca Bigliardi
    """
2613 9e33896b Luca Bigliardi
    self._config_data.cluster.drbd_usermode_helper = drbd_helper
2614 9e33896b Luca Bigliardi
    self._config_data.cluster.serial_no += 1
2615 9e33896b Luca Bigliardi
    self._WriteConfig()
2616 9e33896b Luca Bigliardi
2617 9e33896b Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2618 a8083063 Iustin Pop
  def GetMACPrefix(self):
2619 a8083063 Iustin Pop
    """Return the mac prefix.
2620 a8083063 Iustin Pop

2621 a8083063 Iustin Pop
    """
2622 a8083063 Iustin Pop
    return self._config_data.cluster.mac_prefix
2623 62779dd0 Iustin Pop
2624 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock, shared=1)
2625 62779dd0 Iustin Pop
  def GetClusterInfo(self):
2626 5bbd3f7f Michael Hanselmann
    """Returns information about the cluster
2627 62779dd0 Iustin Pop

2628 c41eea6e Iustin Pop
    @rtype: L{objects.Cluster}
2629 c41eea6e Iustin Pop
    @return: the cluster object
2630 62779dd0 Iustin Pop

2631 62779dd0 Iustin Pop
    """
2632 62779dd0 Iustin Pop
    return self._config_data.cluster
2633 e00fb268 Iustin Pop
2634 51cb1581 Luca Bigliardi
  @locking.ssynchronized(_config_lock, shared=1)
2635 51cb1581 Luca Bigliardi
  def HasAnyDiskOfType(self, dev_type):
2636 51cb1581 Luca Bigliardi
    """Check if in there is at disk of the given type in the configuration.
2637 51cb1581 Luca Bigliardi

2638 51cb1581 Luca Bigliardi
    """
2639 51cb1581 Luca Bigliardi
    return self._config_data.HasAnyDiskOfType(dev_type)
2640 51cb1581 Luca Bigliardi
2641 f78ede4e Guido Trotter
  @locking.ssynchronized(_config_lock)
2642 d1547283 Dimitris Aragiorgis
  def Update(self, target, feedback_fn, ec_id=None):
2643 e00fb268 Iustin Pop
    """Notify function to be called after updates.
2644 e00fb268 Iustin Pop

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

2651 c41eea6e Iustin Pop
    @param target: an instance of either L{objects.Cluster},
2652 c41eea6e Iustin Pop
        L{objects.Node} or L{objects.Instance} which is existing in
2653 c41eea6e Iustin Pop
        the cluster
2654 a4eae71f Michael Hanselmann
    @param feedback_fn: Callable feedback function
2655 c41eea6e Iustin Pop

2656 e00fb268 Iustin Pop
    """
2657 e00fb268 Iustin Pop
    if self._config_data is None:
2658 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Configuration file not read,"
2659 3ecf6786 Iustin Pop
                                   " cannot save.")
2660 f34901f8 Iustin Pop
    update_serial = False
2661 e00fb268 Iustin Pop
    if isinstance(target, objects.Cluster):
2662 e00fb268 Iustin Pop
      test = target == self._config_data.cluster
2663 e00fb268 Iustin Pop
    elif isinstance(target, objects.Node):
2664 e00fb268 Iustin Pop
      test = target in self._config_data.nodes.values()
2665 f34901f8 Iustin Pop
      update_serial = True
2666 e00fb268 Iustin Pop
    elif isinstance(target, objects.Instance):
2667 e00fb268 Iustin Pop
      test = target in self._config_data.instances.values()
2668 e11a1b77 Adeodato Simo
    elif isinstance(target, objects.NodeGroup):
2669 e11a1b77 Adeodato Simo
      test = target in self._config_data.nodegroups.values()
2670 1e0d3321 Dimitris Aragiorgis
    elif isinstance(target, objects.Network):
2671 1e0d3321 Dimitris Aragiorgis
      test = target in self._config_data.networks.values()
2672 e00fb268 Iustin Pop
    else:
2673 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Invalid object type (%s) passed to"
2674 3ecf6786 Iustin Pop
                                   " ConfigWriter.Update" % type(target))
2675 e00fb268 Iustin Pop
    if not test:
2676 3ecf6786 Iustin Pop
      raise errors.ConfigurationError("Configuration updated since object"
2677 3ecf6786 Iustin Pop
                                      " has been read or unknown object")
2678 f34901f8 Iustin Pop
    target.serial_no += 1
2679 d693c864 Iustin Pop
    target.mtime = now = time.time()
2680 f34901f8 Iustin Pop
2681 cff4c037 Iustin Pop
    if update_serial:
2682 f34901f8 Iustin Pop
      # for node updates, we need to increase the cluster serial too
2683 f34901f8 Iustin Pop
      self._config_data.cluster.serial_no += 1
2684 d693c864 Iustin Pop
      self._config_data.cluster.mtime = now
2685 b989e85d Iustin Pop
2686 61cf6b5e Iustin Pop
    if isinstance(target, objects.Instance):
2687 da4a52a3 Thomas Thrainer
      self._UnlockedReleaseDRBDMinors(target.uuid)
2688 61cf6b5e Iustin Pop
2689 d1547283 Dimitris Aragiorgis
    if ec_id is not None:
2690 d1547283 Dimitris Aragiorgis
      # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams
2691 d1547283 Dimitris Aragiorgis
      self._UnlockedCommitTemporaryIps(ec_id)
2692 d1547283 Dimitris Aragiorgis
2693 a4eae71f Michael Hanselmann
    self._WriteConfig(feedback_fn=feedback_fn)
2694 73064714 Guido Trotter
2695 73064714 Guido Trotter
  @locking.ssynchronized(_config_lock)
2696 73064714 Guido Trotter
  def DropECReservations(self, ec_id):
2697 73064714 Guido Trotter
    """Drop per-execution-context reservations
2698 73064714 Guido Trotter

2699 73064714 Guido Trotter
    """
2700 d8aee57e Iustin Pop
    for rm in self._all_rms:
2701 d8aee57e Iustin Pop
      rm.DropECReservations(ec_id)
2702 6c0a75db Dimitris Aragiorgis
2703 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2704 6c0a75db Dimitris Aragiorgis
  def GetAllNetworksInfo(self):
2705 6a94d553 Dimitris Aragiorgis
    """Get configuration info of all the networks.
2706 6c0a75db Dimitris Aragiorgis

2707 6c0a75db Dimitris Aragiorgis
    """
2708 6c0a75db Dimitris Aragiorgis
    return dict(self._config_data.networks)
2709 6c0a75db Dimitris Aragiorgis
2710 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetworkList(self):
2711 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2712 6c0a75db Dimitris Aragiorgis

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

2715 6c0a75db Dimitris Aragiorgis
    """
2716 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks.keys()
2717 6c0a75db Dimitris Aragiorgis
2718 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2719 6c0a75db Dimitris Aragiorgis
  def GetNetworkList(self):
2720 6c0a75db Dimitris Aragiorgis
    """Get the list of networks.
2721 6c0a75db Dimitris Aragiorgis

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

2724 6c0a75db Dimitris Aragiorgis
    """
2725 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetworkList()
2726 6c0a75db Dimitris Aragiorgis
2727 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2728 6c0a75db Dimitris Aragiorgis
  def GetNetworkNames(self):
2729 6c0a75db Dimitris Aragiorgis
    """Get a list of network names
2730 6c0a75db Dimitris Aragiorgis

2731 6c0a75db Dimitris Aragiorgis
    """
2732 beb81ea5 Dimitris Aragiorgis
    names = [net.name
2733 beb81ea5 Dimitris Aragiorgis
             for net in self._config_data.networks.values()]
2734 6c0a75db Dimitris Aragiorgis
    return names
2735 6c0a75db Dimitris Aragiorgis
2736 6c0a75db Dimitris Aragiorgis
  def _UnlockedGetNetwork(self, uuid):
2737 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2738 6c0a75db Dimitris Aragiorgis

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

2741 6c0a75db Dimitris Aragiorgis
    """
2742 6c0a75db Dimitris Aragiorgis
    if uuid not in self._config_data.networks:
2743 6c0a75db Dimitris Aragiorgis
      return None
2744 6c0a75db Dimitris Aragiorgis
2745 6c0a75db Dimitris Aragiorgis
    return self._config_data.networks[uuid]
2746 6c0a75db Dimitris Aragiorgis
2747 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2748 6c0a75db Dimitris Aragiorgis
  def GetNetwork(self, uuid):
2749 6c0a75db Dimitris Aragiorgis
    """Returns information about a network.
2750 6c0a75db Dimitris Aragiorgis

2751 6c0a75db Dimitris Aragiorgis
    It takes the information from the configuration file.
2752 6c0a75db Dimitris Aragiorgis

2753 6c0a75db Dimitris Aragiorgis
    @param uuid: UUID of the network
2754 6c0a75db Dimitris Aragiorgis

2755 6c0a75db Dimitris Aragiorgis
    @rtype: L{objects.Network}
2756 6c0a75db Dimitris Aragiorgis
    @return: the network object
2757 6c0a75db Dimitris Aragiorgis

2758 6c0a75db Dimitris Aragiorgis
    """
2759 6c0a75db Dimitris Aragiorgis
    return self._UnlockedGetNetwork(uuid)
2760 6c0a75db Dimitris Aragiorgis
2761 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2762 6c0a75db Dimitris Aragiorgis
  def AddNetwork(self, net, ec_id, check_uuid=True):
2763 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2764 6c0a75db Dimitris Aragiorgis

2765 6c0a75db Dimitris Aragiorgis
    @type net: L{objects.Network}
2766 6c0a75db Dimitris Aragiorgis
    @param net: the Network object to add
2767 6c0a75db Dimitris Aragiorgis
    @type ec_id: string
2768 6c0a75db Dimitris Aragiorgis
    @param ec_id: unique id for the job to use when creating a missing UUID
2769 6c0a75db Dimitris Aragiorgis

2770 6c0a75db Dimitris Aragiorgis
    """
2771 6c0a75db Dimitris Aragiorgis
    self._UnlockedAddNetwork(net, ec_id, check_uuid)
2772 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2773 6c0a75db Dimitris Aragiorgis
2774 6c0a75db Dimitris Aragiorgis
  def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
2775 6c0a75db Dimitris Aragiorgis
    """Add a network to the configuration.
2776 6c0a75db Dimitris Aragiorgis

2777 6c0a75db Dimitris Aragiorgis
    """
2778 6c0a75db Dimitris Aragiorgis
    logging.info("Adding network %s to configuration", net.name)
2779 6c0a75db Dimitris Aragiorgis
2780 6c0a75db Dimitris Aragiorgis
    if check_uuid:
2781 6c0a75db Dimitris Aragiorgis
      self._EnsureUUID(net, ec_id)
2782 6c0a75db Dimitris Aragiorgis
2783 6c0a75db Dimitris Aragiorgis
    net.serial_no = 1
2784 22ff02a7 Christos Stavrakakis
    net.ctime = net.mtime = time.time()
2785 6c0a75db Dimitris Aragiorgis
    self._config_data.networks[net.uuid] = net
2786 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2787 6c0a75db Dimitris Aragiorgis
2788 6c0a75db Dimitris Aragiorgis
  def _UnlockedLookupNetwork(self, target):
2789 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2790 6c0a75db Dimitris Aragiorgis

2791 6c0a75db Dimitris Aragiorgis
    @type target: string
2792 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2793 6c0a75db Dimitris Aragiorgis
    @rtype: string
2794 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2795 6c0a75db Dimitris Aragiorgis
    @raises errors.OpPrereqError: when the target network cannot be found
2796 6c0a75db Dimitris Aragiorgis

2797 6c0a75db Dimitris Aragiorgis
    """
2798 9394f4d1 Dimitris Aragiorgis
    if target is None:
2799 9394f4d1 Dimitris Aragiorgis
      return None
2800 6c0a75db Dimitris Aragiorgis
    if target in self._config_data.networks:
2801 6c0a75db Dimitris Aragiorgis
      return target
2802 6c0a75db Dimitris Aragiorgis
    for net in self._config_data.networks.values():
2803 6c0a75db Dimitris Aragiorgis
      if net.name == target:
2804 6c0a75db Dimitris Aragiorgis
        return net.uuid
2805 1b68f268 Helga Velroyen
    raise errors.OpPrereqError("Network '%s' not found" % target,
2806 1b68f268 Helga Velroyen
                               errors.ECODE_NOENT)
2807 6c0a75db Dimitris Aragiorgis
2808 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2809 6c0a75db Dimitris Aragiorgis
  def LookupNetwork(self, target):
2810 6c0a75db Dimitris Aragiorgis
    """Lookup a network's UUID.
2811 6c0a75db Dimitris Aragiorgis

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

2814 6c0a75db Dimitris Aragiorgis
    @type target: string
2815 6c0a75db Dimitris Aragiorgis
    @param target: network name or UUID
2816 6c0a75db Dimitris Aragiorgis
    @rtype: string
2817 6c0a75db Dimitris Aragiorgis
    @return: network UUID
2818 6c0a75db Dimitris Aragiorgis

2819 6c0a75db Dimitris Aragiorgis
    """
2820 6c0a75db Dimitris Aragiorgis
    return self._UnlockedLookupNetwork(target)
2821 6c0a75db Dimitris Aragiorgis
2822 6c0a75db Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock)
2823 6c0a75db Dimitris Aragiorgis
  def RemoveNetwork(self, network_uuid):
2824 6c0a75db Dimitris Aragiorgis
    """Remove a network from the configuration.
2825 6c0a75db Dimitris Aragiorgis

2826 6c0a75db Dimitris Aragiorgis
    @type network_uuid: string
2827 6c0a75db Dimitris Aragiorgis
    @param network_uuid: the UUID of the network to remove
2828 6c0a75db Dimitris Aragiorgis

2829 6c0a75db Dimitris Aragiorgis
    """
2830 6c0a75db Dimitris Aragiorgis
    logging.info("Removing network %s from configuration", network_uuid)
2831 6c0a75db Dimitris Aragiorgis
2832 6c0a75db Dimitris Aragiorgis
    if network_uuid not in self._config_data.networks:
2833 6c0a75db Dimitris Aragiorgis
      raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
2834 6c0a75db Dimitris Aragiorgis
2835 6c0a75db Dimitris Aragiorgis
    del self._config_data.networks[network_uuid]
2836 6c0a75db Dimitris Aragiorgis
    self._config_data.cluster.serial_no += 1
2837 6c0a75db Dimitris Aragiorgis
    self._WriteConfig()
2838 ad4a9ae7 Dimitris Aragiorgis
2839 1c3231aa Thomas Thrainer
  def _UnlockedGetGroupNetParams(self, net_uuid, node_uuid):
2840 ad4a9ae7 Dimitris Aragiorgis
    """Get the netparams (mode, link) of a network.
2841 ad4a9ae7 Dimitris Aragiorgis

2842 ad4a9ae7 Dimitris Aragiorgis
    Get a network's netparams for a given node.
2843 ad4a9ae7 Dimitris Aragiorgis

2844 9ccacbc8 Dimitris Aragiorgis
    @type net_uuid: string
2845 9ccacbc8 Dimitris Aragiorgis
    @param net_uuid: network uuid
2846 1c3231aa Thomas Thrainer
    @type node_uuid: string
2847 1c3231aa Thomas Thrainer
    @param node_uuid: node UUID
2848 ad4a9ae7 Dimitris Aragiorgis
    @rtype: dict or None
2849 ad4a9ae7 Dimitris Aragiorgis
    @return: netparams
2850 ad4a9ae7 Dimitris Aragiorgis

2851 ad4a9ae7 Dimitris Aragiorgis
    """
2852 1c3231aa Thomas Thrainer
    node_info = self._UnlockedGetNodeInfo(node_uuid)
2853 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2854 ad4a9ae7 Dimitris Aragiorgis
    netparams = nodegroup_info.networks.get(net_uuid, None)
2855 ad4a9ae7 Dimitris Aragiorgis
2856 ad4a9ae7 Dimitris Aragiorgis
    return netparams
2857 ad4a9ae7 Dimitris Aragiorgis
2858 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2859 1c3231aa Thomas Thrainer
  def GetGroupNetParams(self, net_uuid, node_uuid):
2860 ad4a9ae7 Dimitris Aragiorgis
    """Locking wrapper of _UnlockedGetGroupNetParams()
2861 ad4a9ae7 Dimitris Aragiorgis

2862 ad4a9ae7 Dimitris Aragiorgis
    """
2863 1c3231aa Thomas Thrainer
    return self._UnlockedGetGroupNetParams(net_uuid, node_uuid)
2864 ad4a9ae7 Dimitris Aragiorgis
2865 ad4a9ae7 Dimitris Aragiorgis
  @locking.ssynchronized(_config_lock, shared=1)
2866 1c3231aa Thomas Thrainer
  def CheckIPInNodeGroup(self, ip, node_uuid):
2867 6a94d553 Dimitris Aragiorgis
    """Check IP uniqueness in nodegroup.
2868 6a94d553 Dimitris Aragiorgis

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

2873 ad4a9ae7 Dimitris Aragiorgis
    @type ip: string
2874 ad4a9ae7 Dimitris Aragiorgis
    @param ip: ip address
2875 1c3231aa Thomas Thrainer
    @type node_uuid: string
2876 1c3231aa Thomas Thrainer
    @param node_uuid: node UUID
2877 ad4a9ae7 Dimitris Aragiorgis
    @rtype: (string, dict) or (None, None)
2878 ad4a9ae7 Dimitris Aragiorgis
    @return: (network name, netparams)
2879 ad4a9ae7 Dimitris Aragiorgis

2880 ad4a9ae7 Dimitris Aragiorgis
    """
2881 ad4a9ae7 Dimitris Aragiorgis
    if ip is None:
2882 ad4a9ae7 Dimitris Aragiorgis
      return (None, None)
2883 1c3231aa Thomas Thrainer
    node_info = self._UnlockedGetNodeInfo(node_uuid)
2884 ad4a9ae7 Dimitris Aragiorgis
    nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2885 ad4a9ae7 Dimitris Aragiorgis
    for net_uuid in nodegroup_info.networks.keys():
2886 ad4a9ae7 Dimitris Aragiorgis
      net_info = self._UnlockedGetNetwork(net_uuid)
2887 ad4a9ae7 Dimitris Aragiorgis
      pool = network.AddressPool(net_info)
2888 beb81ea5 Dimitris Aragiorgis
      if pool.Contains(ip):
2889 ad4a9ae7 Dimitris Aragiorgis
        return (net_info.name, nodegroup_info.networks[net_uuid])
2890 ad4a9ae7 Dimitris Aragiorgis
2891 ad4a9ae7 Dimitris Aragiorgis
    return (None, None)