Package ganeti :: Package config
[hide private]
[frames] | no frames]

Source Code for Package ganeti.config

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google Inc. 
   5  # All rights reserved. 
   6  # 
   7  # Redistribution and use in source and binary forms, with or without 
   8  # modification, are permitted provided that the following conditions are 
   9  # met: 
  10  # 
  11  # 1. Redistributions of source code must retain the above copyright notice, 
  12  # this list of conditions and the following disclaimer. 
  13  # 
  14  # 2. Redistributions in binary form must reproduce the above copyright 
  15  # notice, this list of conditions and the following disclaimer in the 
  16  # documentation and/or other materials provided with the distribution. 
  17  # 
  18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
  19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
  20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
  22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
  23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
  24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
  28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  29   
  30   
  31  """Configuration management for Ganeti 
  32   
  33  This module provides the interface to the Ganeti cluster configuration. 
  34   
  35  The configuration data is stored on every node but is updated on the master 
  36  only. After each update, the master distributes the data to the other nodes. 
  37   
  38  Currently, the data storage format is JSON. YAML was slow and consuming too 
  39  much memory. 
  40   
  41  """ 
  42   
  43  # TODO: Break up this file into multiple chunks - Wconfd RPC calls, local config 
  44  # manipulations, grouped by object they operate on (cluster/instance/disk) 
  45  # pylint: disable=C0302 
  46  # pylint: disable=R0904 
  47  # R0904: Too many public methods 
  48   
  49  import copy 
  50  import os 
  51  import random 
  52  import logging 
  53  import time 
  54  import threading 
  55  import itertools 
  56   
  57  from ganeti.config.temporary_reservations import TemporaryReservationManager 
  58  from ganeti.config.utils import ConfigSync, ConfigManager 
  59  from ganeti.config.verify import (VerifyType, VerifyNic, VerifyIpolicy, 
  60                                    ValidateConfig) 
  61   
  62  from ganeti import errors 
  63  from ganeti import utils 
  64  from ganeti import constants 
  65  import ganeti.wconfd as wc 
  66  from ganeti import objects 
  67  from ganeti import serializer 
  68  from ganeti import uidpool 
  69  from ganeti import netutils 
  70  from ganeti import runtime 
  71  from ganeti import pathutils 
  72  from ganeti import network 
73 74 75 -def GetWConfdContext(ec_id, livelock):
76 """Prepare a context for communication with WConfd. 77 78 WConfd needs to know the identity of each caller to properly manage locks and 79 detect job death. This helper function prepares the identity object given a 80 job ID (optional) and a livelock file. 81 82 @type ec_id: int, or None 83 @param ec_id: the job ID or None, if the caller isn't a job 84 @type livelock: L{ganeti.utils.livelock.LiveLock} 85 @param livelock: a livelock object holding the lockfile needed for WConfd 86 @return: the WConfd context 87 88 """ 89 if ec_id is None: 90 return (threading.current_thread().getName(), 91 livelock.GetPath(), os.getpid()) 92 else: 93 return (ec_id, 94 livelock.GetPath(), os.getpid())
95
96 97 -def GetConfig(ec_id, livelock, **kwargs):
98 """A utility function for constructing instances of ConfigWriter. 99 100 It prepares a WConfd context and uses it to create a ConfigWriter instance. 101 102 @type ec_id: int, or None 103 @param ec_id: the job ID or None, if the caller isn't a job 104 @type livelock: L{ganeti.utils.livelock.LiveLock} 105 @param livelock: a livelock object holding the lockfile needed for WConfd 106 @type kwargs: dict 107 @param kwargs: Any additional arguments for the ConfigWriter constructor 108 @rtype: L{ConfigWriter} 109 @return: the ConfigWriter context 110 111 """ 112 kwargs['wconfdcontext'] = GetWConfdContext(ec_id, livelock) 113 114 # if the config is to be opened in the accept_foreign mode, we should 115 # also tell the RPC client not to check for the master node 116 accept_foreign = kwargs.get('accept_foreign', False) 117 kwargs['wconfd'] = wc.Client(allow_non_master=accept_foreign) 118 119 return ConfigWriter(**kwargs)
120 121 122 # job id used for resource management at config upgrade time 123 _UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
124 125 126 -def _MatchNameComponentIgnoreCase(short_name, names):
127 """Wrapper around L{utils.text.MatchNameComponent}. 128 129 """ 130 return utils.MatchNameComponent(short_name, names, case_sensitive=False)
131
132 133 -def _CheckInstanceDiskIvNames(disks):
134 """Checks if instance's disks' C{iv_name} attributes are in order. 135 136 @type disks: list of L{objects.Disk} 137 @param disks: List of disks 138 @rtype: list of tuples; (int, string, string) 139 @return: List of wrongly named disks, each tuple contains disk index, 140 expected and actual name 141 142 """ 143 result = [] 144 145 for (idx, disk) in enumerate(disks): 146 exp_iv_name = "disk/%s" % idx 147 if disk.iv_name != exp_iv_name: 148 result.append((idx, exp_iv_name, disk.iv_name)) 149 150 return result
151
152 153 -def _UpdateIvNames(base_idx, disks):
154 """Update the C{iv_name} attribute of disks. 155 156 @type disks: list of L{objects.Disk} 157 158 """ 159 for (idx, disk) in enumerate(disks): 160 disk.iv_name = "disk/%s" % (base_idx + idx)
161
162 163 -class ConfigWriter(object):
164 """The interface to the cluster configuration. 165 166 WARNING: The class is no longer thread-safe! 167 Each thread must construct a separate instance. 168 169 @ivar _all_rms: a list of all temporary reservation managers 170 171 Currently the class fulfills 3 main functions: 172 1. lock the configuration for access (monitor) 173 2. reload and write the config if necessary (bridge) 174 3. provide convenient access methods to config data (facade) 175 176 """
177 - def __init__(self, cfg_file=None, offline=False, _getents=runtime.GetEnts, 178 accept_foreign=False, wconfdcontext=None, wconfd=None):
179 self.write_count = 0 180 self._config_data = None 181 self._SetConfigData(None) 182 self._offline = offline 183 if cfg_file is None: 184 self._cfg_file = pathutils.CLUSTER_CONF_FILE 185 else: 186 self._cfg_file = cfg_file 187 self._getents = _getents 188 self._temporary_ids = TemporaryReservationManager() 189 self._all_rms = [self._temporary_ids] 190 # Note: in order to prevent errors when resolving our name later, 191 # we compute it here once and reuse it; it's 192 # better to raise an error before starting to modify the config 193 # file than after it was modified 194 self._my_hostname = netutils.Hostname.GetSysName() 195 self._cfg_id = None 196 self._wconfdcontext = wconfdcontext 197 self._wconfd = wconfd 198 self._accept_foreign = accept_foreign 199 self._lock_count = 0 200 self._lock_current_shared = None 201 self._lock_forced = False
202
203 - def _ConfigData(self):
204 return self._config_data
205
206 - def OutDate(self):
207 self._config_data = None
208
209 - def _SetConfigData(self, cfg):
210 self._config_data = cfg
211
212 - def _GetWConfdContext(self):
213 return self._wconfdcontext
214 215 # this method needs to be static, so that we can call it on the class 216 @staticmethod
217 - def IsCluster():
218 """Check if the cluster is configured. 219 220 """ 221 return os.path.exists(pathutils.CLUSTER_CONF_FILE)
222
223 - def _UnlockedGetNdParams(self, node):
224 nodegroup = self._UnlockedGetNodeGroup(node.group) 225 return self._ConfigData().cluster.FillND(node, nodegroup)
226 227 @ConfigSync(shared=1)
228 - def GetNdParams(self, node):
229 """Get the node params populated with cluster defaults. 230 231 @type node: L{objects.Node} 232 @param node: The node we want to know the params for 233 @return: A dict with the filled in node params 234 235 """ 236 return self._UnlockedGetNdParams(node)
237 238 @ConfigSync(shared=1)
239 - def GetNdGroupParams(self, nodegroup):
240 """Get the node groups params populated with cluster defaults. 241 242 @type nodegroup: L{objects.NodeGroup} 243 @param nodegroup: The node group we want to know the params for 244 @return: A dict with the filled in node group params 245 246 """ 247 return self._UnlockedGetNdGroupParams(nodegroup)
248
249 - def _UnlockedGetNdGroupParams(self, group):
250 """Get the ndparams of the group. 251 252 @type group: L{objects.NodeGroup} 253 @param group: The group we want to know the params for 254 @rtype: dict of str to int 255 @return: A dict with the filled in node group params 256 257 """ 258 return self._ConfigData().cluster.FillNDGroup(group)
259 260 @ConfigSync(shared=1)
261 - def GetGroupSshPorts(self):
262 """Get a map of group UUIDs to SSH ports. 263 264 @rtype: dict of str to int 265 @return: a dict mapping the UUIDs to the SSH ports 266 267 """ 268 port_map = {} 269 for uuid, group in self._config_data.nodegroups.items(): 270 ndparams = self._UnlockedGetNdGroupParams(group) 271 port = ndparams.get(constants.ND_SSH_PORT) 272 port_map[uuid] = port 273 return port_map
274 275 @ConfigSync(shared=1)
276 - def GetInstanceDiskParams(self, instance):
277 """Get the disk params populated with inherit chain. 278 279 @type instance: L{objects.Instance} 280 @param instance: The instance we want to know the params for 281 @return: A dict with the filled in disk params 282 283 """ 284 node = self._UnlockedGetNodeInfo(instance.primary_node) 285 nodegroup = self._UnlockedGetNodeGroup(node.group) 286 return self._UnlockedGetGroupDiskParams(nodegroup)
287
288 - def _UnlockedGetInstanceDisks(self, inst_uuid):
289 """Return the disks' info for the given instance 290 291 @type inst_uuid: string 292 @param inst_uuid: The UUID of the instance we want to know the disks for 293 294 @rtype: List of L{objects.Disk} 295 @return: A list with all the disks' info 296 297 """ 298 instance = self._UnlockedGetInstanceInfo(inst_uuid) 299 if instance is None: 300 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid) 301 302 return [self._UnlockedGetDiskInfo(disk_uuid) 303 for disk_uuid in instance.disks]
304 305 @ConfigSync(shared=1)
306 - def GetInstanceDisks(self, inst_uuid):
307 """Return the disks' info for the given instance 308 309 This is a simple wrapper over L{_UnlockedGetInstanceDisks}. 310 311 """ 312 return self._UnlockedGetInstanceDisks(inst_uuid)
313
314 - def _UnlockedAddDisk(self, disk, replace=False):
315 """Add a disk to the config. 316 317 @type disk: L{objects.Disk} 318 @param disk: The disk object 319 320 """ 321 if not isinstance(disk, objects.Disk): 322 raise errors.ProgrammerError("Invalid type passed to _UnlockedAddDisk") 323 324 logging.info("Adding disk %s to configuration", disk.uuid) 325 326 if replace: 327 self._CheckUUIDpresent(disk) 328 else: 329 self._CheckUniqueUUID(disk, include_temporary=False) 330 disk.serial_no = 1 331 disk.ctime = disk.mtime = time.time() 332 disk.UpgradeConfig() 333 self._ConfigData().disks[disk.uuid] = disk 334 self._ConfigData().cluster.serial_no += 1 335 self._UnlockedReleaseDRBDMinors(disk.uuid)
336
337 - def _UnlockedAttachInstanceDisk(self, inst_uuid, disk_uuid, idx=None):
338 """Attach a disk to an instance. 339 340 @type inst_uuid: string 341 @param inst_uuid: The UUID of the instance object 342 @type disk_uuid: string 343 @param disk_uuid: The UUID of the disk object 344 @type idx: int 345 @param idx: the index of the newly attached disk; if not 346 passed, the disk will be attached as the last one. 347 348 """ 349 instance = self._UnlockedGetInstanceInfo(inst_uuid) 350 if instance is None: 351 raise errors.ConfigurationError("Instance %s doesn't exist" 352 % inst_uuid) 353 if disk_uuid not in self._ConfigData().disks: 354 raise errors.ConfigurationError("Disk %s doesn't exist" % disk_uuid) 355 356 if idx is None: 357 idx = len(instance.disks) 358 else: 359 if idx < 0: 360 raise IndexError("Not accepting negative indices other than -1") 361 elif idx > len(instance.disks): 362 raise IndexError("Got disk index %s, but there are only %s" % 363 (idx, len(instance.disks))) 364 365 # Disk must not be attached anywhere else 366 for inst in self._ConfigData().instances.values(): 367 if disk_uuid in inst.disks: 368 raise errors.ReservationError("Disk %s already attached to instance %s" 369 % (disk_uuid, inst.name)) 370 371 instance.disks.insert(idx, disk_uuid) 372 instance_disks = self._UnlockedGetInstanceDisks(inst_uuid) 373 _UpdateIvNames(idx, instance_disks[idx:]) 374 instance.serial_no += 1 375 instance.mtime = time.time()
376 377 @ConfigSync()
378 - def AddInstanceDisk(self, inst_uuid, disk, idx=None, replace=False):
379 """Add a disk to the config and attach it to instance. 380 381 This is a simple wrapper over L{_UnlockedAddDisk} and 382 L{_UnlockedAttachInstanceDisk}. 383 384 """ 385 self._UnlockedAddDisk(disk, replace=replace) 386 self._UnlockedAttachInstanceDisk(inst_uuid, disk.uuid, idx)
387 388 @ConfigSync()
389 - def AttachInstanceDisk(self, inst_uuid, disk_uuid, idx=None):
390 """Attach an existing disk to an instance. 391 392 This is a simple wrapper over L{_UnlockedAttachInstanceDisk}. 393 394 """ 395 self._UnlockedAttachInstanceDisk(inst_uuid, disk_uuid, idx)
396
397 - def _UnlockedDetachInstanceDisk(self, inst_uuid, disk_uuid):
398 """Detach a disk from an instance. 399 400 @type inst_uuid: string 401 @param inst_uuid: The UUID of the instance object 402 @type disk_uuid: string 403 @param disk_uuid: The UUID of the disk object 404 405 """ 406 instance = self._UnlockedGetInstanceInfo(inst_uuid) 407 if instance is None: 408 raise errors.ConfigurationError("Instance %s doesn't exist" 409 % inst_uuid) 410 if disk_uuid not in self._ConfigData().disks: 411 raise errors.ConfigurationError("Disk %s doesn't exist" % disk_uuid) 412 413 # Check if disk is attached to the instance 414 if disk_uuid not in instance.disks: 415 raise errors.ProgrammerError("Disk %s is not attached to an instance" 416 % disk_uuid) 417 418 idx = instance.disks.index(disk_uuid) 419 instance.disks.remove(disk_uuid) 420 instance_disks = self._UnlockedGetInstanceDisks(inst_uuid) 421 _UpdateIvNames(idx, instance_disks[idx:]) 422 instance.serial_no += 1 423 instance.mtime = time.time()
424
425 - def _UnlockedRemoveDisk(self, disk_uuid):
426 """Remove the disk from the configuration. 427 428 @type disk_uuid: string 429 @param disk_uuid: The UUID of the disk object 430 431 """ 432 if disk_uuid not in self._ConfigData().disks: 433 raise errors.ConfigurationError("Disk %s doesn't exist" % disk_uuid) 434 435 # Disk must not be attached anywhere 436 for inst in self._ConfigData().instances.values(): 437 if disk_uuid in inst.disks: 438 raise errors.ReservationError("Cannot remove disk %s. Disk is" 439 " attached to instance %s" 440 % (disk_uuid, inst.name)) 441 442 # Remove disk from config file 443 del self._ConfigData().disks[disk_uuid] 444 self._ConfigData().cluster.serial_no += 1
445 446 @ConfigSync()
447 - def RemoveInstanceDisk(self, inst_uuid, disk_uuid):
448 """Detach a disk from an instance and remove it from the config. 449 450 This is a simple wrapper over L{_UnlockedDetachInstanceDisk} and 451 L{_UnlockedRemoveDisk}. 452 453 """ 454 self._UnlockedDetachInstanceDisk(inst_uuid, disk_uuid) 455 self._UnlockedRemoveDisk(disk_uuid)
456 457 @ConfigSync()
458 - def DetachInstanceDisk(self, inst_uuid, disk_uuid):
459 """Detach a disk from an instance. 460 461 This is a simple wrapper over L{_UnlockedDetachInstanceDisk}. 462 """ 463 self._UnlockedDetachInstanceDisk(inst_uuid, disk_uuid)
464
465 - def _UnlockedGetDiskInfo(self, disk_uuid):
466 """Returns information about a disk. 467 468 It takes the information from the configuration file. 469 470 @param disk_uuid: UUID of the disk 471 472 @rtype: L{objects.Disk} 473 @return: the disk object 474 475 """ 476 if disk_uuid not in self._ConfigData().disks: 477 return None 478 479 return self._ConfigData().disks[disk_uuid]
480 481 @ConfigSync(shared=1)
482 - def GetDiskInfo(self, disk_uuid):
483 """Returns information about a disk. 484 485 This is a simple wrapper over L{_UnlockedGetDiskInfo}. 486 487 """ 488 return self._UnlockedGetDiskInfo(disk_uuid)
489
490 - def _UnlockedGetDiskInfoByName(self, disk_name):
491 """Return information about a named disk. 492 493 Return disk information from the configuration file, searching with the 494 name of the disk. 495 496 @param disk_name: Name of the disk 497 498 @rtype: L{objects.Disk} 499 @return: the disk object 500 501 """ 502 disk = None 503 count = 0 504 for d in self._ConfigData().disks.itervalues(): 505 if d.name == disk_name: 506 count += 1 507 disk = d 508 509 if count > 1: 510 raise errors.ConfigurationError("There are %s disks with this name: %s" 511 % (count, disk_name)) 512 513 return disk
514 515 @ConfigSync(shared=1)
516 - def GetDiskInfoByName(self, disk_name):
517 """Return information about a named disk. 518 519 This is a simple wrapper over L{_UnlockedGetDiskInfoByName}. 520 521 """ 522 return self._UnlockedGetDiskInfoByName(disk_name)
523
524 - def _UnlockedGetDiskList(self):
525 """Get the list of disks. 526 527 @return: array of disks, ex. ['disk2-uuid', 'disk1-uuid'] 528 529 """ 530 return self._ConfigData().disks.keys()
531 532 @ConfigSync(shared=1)
533 - def GetAllDisksInfo(self):
534 """Get the configuration of all disks. 535 536 This is a simple wrapper over L{_UnlockedGetAllDisksInfo}. 537 538 """ 539 return self._UnlockedGetAllDisksInfo()
540
541 - def _UnlockedGetAllDisksInfo(self):
542 """Get the configuration of all disks. 543 544 @rtype: dict 545 @return: dict of (disk, disk_info), where disk_info is what 546 would GetDiskInfo return for the node 547 548 """ 549 my_dict = dict([(disk_uuid, self._UnlockedGetDiskInfo(disk_uuid)) 550 for disk_uuid in self._UnlockedGetDiskList()]) 551 return my_dict
552
553 - def _AllInstanceNodes(self, inst_uuid):
554 """Compute the set of all disk-related nodes for an instance. 555 556 This abstracts away some work from '_UnlockedGetInstanceNodes' 557 and '_UnlockedGetInstanceSecondaryNodes'. 558 559 @type inst_uuid: string 560 @param inst_uuid: The UUID of the instance we want to get nodes for 561 @rtype: set of strings 562 @return: A set of names for all the nodes of the instance 563 564 """ 565 instance = self._UnlockedGetInstanceInfo(inst_uuid) 566 if instance is None: 567 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid) 568 569 instance_disks = self._UnlockedGetInstanceDisks(inst_uuid) 570 all_nodes = [] 571 for disk in instance_disks: 572 all_nodes.extend(disk.all_nodes) 573 return (set(all_nodes), instance)
574
575 - def _UnlockedGetInstanceNodes(self, inst_uuid):
576 """Get all disk-related nodes for an instance. 577 578 For non-DRBD instances, this will contain only the instance's primary node, 579 whereas for DRBD instances, it will contain both the primary and the 580 secondaries. 581 582 @type inst_uuid: string 583 @param inst_uuid: The UUID of the instance we want to get nodes for 584 @rtype: list of strings 585 @return: A list of names for all the nodes of the instance 586 587 """ 588 (all_nodes, instance) = self._AllInstanceNodes(inst_uuid) 589 # ensure that primary node is always the first 590 all_nodes.discard(instance.primary_node) 591 return (instance.primary_node, ) + tuple(all_nodes)
592 593 @ConfigSync(shared=1)
594 - def GetInstanceNodes(self, inst_uuid):
595 """Get all disk-related nodes for an instance. 596 597 This is just a wrapper over L{_UnlockedGetInstanceNodes} 598 599 """ 600 return self._UnlockedGetInstanceNodes(inst_uuid)
601
602 - def _UnlockedGetInstanceSecondaryNodes(self, inst_uuid):
603 """Get the list of secondary nodes. 604 605 @type inst_uuid: string 606 @param inst_uuid: The UUID of the instance we want to get nodes for 607 @rtype: list of strings 608 @return: A tuple of names for all the secondary nodes of the instance 609 610 """ 611 (all_nodes, instance) = self._AllInstanceNodes(inst_uuid) 612 all_nodes.discard(instance.primary_node) 613 return tuple(all_nodes)
614 615 @ConfigSync(shared=1)
616 - def GetInstanceSecondaryNodes(self, inst_uuid):
617 """Get the list of secondary nodes. 618 619 This is a simple wrapper over L{_UnlockedGetInstanceSecondaryNodes}. 620 621 """ 622 return self._UnlockedGetInstanceSecondaryNodes(inst_uuid)
623
624 - def _UnlockedGetInstanceLVsByNode(self, inst_uuid, lvmap=None):
625 """Provide a mapping of node to LVs a given instance owns. 626 627 @type inst_uuid: string 628 @param inst_uuid: The UUID of the instance we want to 629 compute the LVsByNode for 630 @type lvmap: dict 631 @param lvmap: Optional dictionary to receive the 632 'node' : ['lv', ...] data. 633 @rtype: dict or None 634 @return: None if lvmap arg is given, otherwise, a dictionary of 635 the form { 'node_uuid' : ['volume1', 'volume2', ...], ... }; 636 volumeN is of the form "vg_name/lv_name", compatible with 637 GetVolumeList() 638 639 """ 640 def _MapLVsByNode(lvmap, devices, node_uuid): 641 """Recursive helper function.""" 642 if not node_uuid in lvmap: 643 lvmap[node_uuid] = [] 644 645 for dev in devices: 646 if dev.dev_type == constants.DT_PLAIN: 647 if not dev.forthcoming: 648 lvmap[node_uuid].append(dev.logical_id[0] + "/" + dev.logical_id[1]) 649 650 elif dev.dev_type in constants.DTS_DRBD: 651 if dev.children: 652 _MapLVsByNode(lvmap, dev.children, dev.logical_id[0]) 653 _MapLVsByNode(lvmap, dev.children, dev.logical_id[1]) 654 655 elif dev.children: 656 _MapLVsByNode(lvmap, dev.children, node_uuid)
657 658 instance = self._UnlockedGetInstanceInfo(inst_uuid) 659 if instance is None: 660 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid) 661 662 if lvmap is None: 663 lvmap = {} 664 ret = lvmap 665 else: 666 ret = None 667 668 _MapLVsByNode(lvmap, 669 self._UnlockedGetInstanceDisks(instance.uuid), 670 instance.primary_node) 671 return ret
672 673 @ConfigSync(shared=1)
674 - def GetInstanceLVsByNode(self, inst_uuid, lvmap=None):
675 """Provide a mapping of node to LVs a given instance owns. 676 677 This is a simple wrapper over L{_UnlockedGetInstanceLVsByNode} 678 679 """ 680 return self._UnlockedGetInstanceLVsByNode(inst_uuid, lvmap=lvmap)
681 682 @ConfigSync(shared=1)
683 - def GetGroupDiskParams(self, group):
684 """Get the disk params populated with inherit chain. 685 686 @type group: L{objects.NodeGroup} 687 @param group: The group we want to know the params for 688 @return: A dict with the filled in disk params 689 690 """ 691 return self._UnlockedGetGroupDiskParams(group)
692
693 - def _UnlockedGetGroupDiskParams(self, group):
694 """Get the disk params populated with inherit chain down to node-group. 695 696 @type group: L{objects.NodeGroup} 697 @param group: The group we want to know the params for 698 @return: A dict with the filled in disk params 699 700 """ 701 data = self._ConfigData().cluster.SimpleFillDP(group.diskparams) 702 assert isinstance(data, dict), "Not a dictionary: " + str(data) 703 return data
704 705 @ConfigSync(shared=1)
706 - def GetPotentialMasterCandidates(self):
707 """Gets the list of node names of potential master candidates. 708 709 @rtype: list of str 710 @return: list of node names of potential master candidates 711 712 """ 713 # FIXME: Note that currently potential master candidates are nodes 714 # but this definition will be extended once RAPI-unmodifiable 715 # parameters are introduced. 716 nodes = self._UnlockedGetAllNodesInfo() 717 return [node_info.name for node_info in nodes.values()]
718
719 - def GenerateMAC(self, net_uuid, _ec_id):
720 """Generate a MAC for an instance. 721 722 This should check the current instances for duplicates. 723 724 """ 725 return self._wconfd.GenerateMAC(self._GetWConfdContext(), net_uuid)
726
727 - def ReserveMAC(self, mac, _ec_id):
728 """Reserve a MAC for an instance. 729 730 This only checks instances managed by this cluster, it does not 731 check for potential collisions elsewhere. 732 733 """ 734 self._wconfd.ReserveMAC(self._GetWConfdContext(), mac)
735
736 - def _UnlockedCommitTemporaryIps(self, _ec_id):
737 """Commit all reserved IP address to their respective pools 738 739 """ 740 if self._offline: 741 raise errors.ProgrammerError("Can't call CommitTemporaryIps" 742 " in offline mode") 743 ips = self._wconfd.ListReservedIps(self._GetWConfdContext()) 744 for action, address, net_uuid in ips: 745 self._UnlockedCommitIp(action, net_uuid, address)
746
747 - def _UnlockedCommitIp(self, action, net_uuid, address):
748 """Commit a reserved IP address to an IP pool. 749 750 The IP address is taken from the network's IP pool and marked as free. 751 752 """ 753 nobj = self._UnlockedGetNetwork(net_uuid) 754 if nobj is None: 755 raise errors.ProgrammerError("Network '%s' not found" % (net_uuid, )) 756 pool = network.AddressPool(nobj) 757 if action == constants.RESERVE_ACTION: 758 pool.Reserve(address) 759 elif action == constants.RELEASE_ACTION: 760 pool.Release(address)
761
762 - def ReleaseIp(self, net_uuid, address, _ec_id):
763 """Give a specific IP address back to an IP pool. 764 765 The IP address is returned to the IP pool and marked as reserved. 766 767 """ 768 if net_uuid: 769 if self._offline: 770 raise errors.ProgrammerError("Can't call ReleaseIp in offline mode") 771 self._wconfd.ReleaseIp(self._GetWConfdContext(), net_uuid, address)
772
773 - def GenerateIp(self, net_uuid, _ec_id):
774 """Find a free IPv4 address for an instance. 775 776 """ 777 if self._offline: 778 raise errors.ProgrammerError("Can't call GenerateIp in offline mode") 779 return self._wconfd.GenerateIp(self._GetWConfdContext(), net_uuid)
780
781 - def ReserveIp(self, net_uuid, address, _ec_id, check=True):
782 """Reserve a given IPv4 address for use by an instance. 783 784 """ 785 if self._offline: 786 raise errors.ProgrammerError("Can't call ReserveIp in offline mode") 787 return self._wconfd.ReserveIp(self._GetWConfdContext(), net_uuid, address, 788 check)
789
790 - def ReserveLV(self, lv_name, _ec_id):
791 """Reserve an VG/LV pair for an instance. 792 793 @type lv_name: string 794 @param lv_name: the logical volume name to reserve 795 796 """ 797 return self._wconfd.ReserveLV(self._GetWConfdContext(), lv_name)
798
799 - def GenerateDRBDSecret(self, _ec_id):
800 """Generate a DRBD secret. 801 802 This checks the current disks for duplicates. 803 804 """ 805 return self._wconfd.GenerateDRBDSecret(self._GetWConfdContext())
806 807 # FIXME: After _AllIDs is removed, move it to config_mock.py
808 - def _AllLVs(self):
809 """Compute the list of all LVs. 810 811 """ 812 lvnames = set() 813 for instance in self._ConfigData().instances.values(): 814 node_data = self._UnlockedGetInstanceLVsByNode(instance.uuid) 815 for lv_list in node_data.values(): 816 lvnames.update(lv_list) 817 return lvnames
818
819 - def _AllNICs(self):
820 """Compute the list of all NICs. 821 822 """ 823 nics = [] 824 for instance in self._ConfigData().instances.values(): 825 nics.extend(instance.nics) 826 return nics
827
828 - def _AllIDs(self, include_temporary):
829 """Compute the list of all UUIDs and names we have. 830 831 @type include_temporary: boolean 832 @param include_temporary: whether to include the _temporary_ids set 833 @rtype: set 834 @return: a set of IDs 835 836 """ 837 existing = set() 838 if include_temporary: 839 existing.update(self._temporary_ids.GetReserved()) 840 existing.update(self._AllLVs()) 841 existing.update(self._ConfigData().instances.keys()) 842 existing.update(self._ConfigData().nodes.keys()) 843 existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid]) 844 return existing
845
846 - def _GenerateUniqueID(self, ec_id):
847 """Generate an unique UUID. 848 849 This checks the current node, instances and disk names for 850 duplicates. 851 852 @rtype: string 853 @return: the unique id 854 855 """ 856 existing = self._AllIDs(include_temporary=False) 857 return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
858 859 @ConfigSync(shared=1)
860 - def GenerateUniqueID(self, ec_id):
861 """Generate an unique ID. 862 863 This is just a wrapper over the unlocked version. 864 865 @type ec_id: string 866 @param ec_id: unique id for the job to reserve the id to 867 868 """ 869 return self._GenerateUniqueID(ec_id)
870
871 - def _AllMACs(self):
872 """Return all MACs present in the config. 873 874 @rtype: list 875 @return: the list of all MACs 876 877 """ 878 result = [] 879 for instance in self._ConfigData().instances.values(): 880 for nic in instance.nics: 881 result.append(nic.mac) 882 883 return result
884
885 - def _AllDRBDSecrets(self):
886 """Return all DRBD secrets present in the config. 887 888 @rtype: list 889 @return: the list of all DRBD secrets 890 891 """ 892 def helper(disk, result): 893 """Recursively gather secrets from this disk.""" 894 if disk.dev_type == constants.DT_DRBD8: 895 result.append(disk.logical_id[5]) 896 if disk.children: 897 for child in disk.children: 898 helper(child, result)
899 900 result = [] 901 for disk in self._ConfigData().disks.values(): 902 helper(disk, result) 903 904 return result 905 906 @staticmethod
907 - def _VerifyDisks(data, result):
908 """Per-disk verification checks 909 910 Extends L{result} with diagnostic information about the disks. 911 912 @type data: see L{_ConfigData} 913 @param data: configuration data 914 915 @type result: list of strings 916 @param result: list containing diagnostic messages 917 918 """ 919 for disk_uuid in data.disks: 920 disk = data.disks[disk_uuid] 921 result.extend(["disk %s error: %s" % (disk.uuid, msg) 922 for msg in disk.Verify()]) 923 if disk.uuid != disk_uuid: 924 result.append("disk '%s' is indexed by wrong UUID '%s'" % 925 (disk.name, disk_uuid))
926
927 - def _UnlockedVerifyConfig(self):
928 """Verify function. 929 930 @rtype: list 931 @return: a list of error messages; a non-empty list signifies 932 configuration errors 933 934 """ 935 # pylint: disable=R0914 936 result = [] 937 seen_macs = [] 938 ports = {} 939 data = self._ConfigData() 940 cluster = data.cluster 941 942 # First call WConfd to perform its checks, if we're not offline 943 if not self._offline: 944 try: 945 self._wconfd.VerifyConfig() 946 except errors.ConfigVerifyError, err: 947 try: 948 for msg in err.args[1]: 949 result.append(msg) 950 except IndexError: 951 pass 952 953 # check cluster parameters 954 VerifyType("cluster", "beparams", cluster.SimpleFillBE({}), 955 constants.BES_PARAMETER_TYPES, result.append) 956 VerifyType("cluster", "nicparams", cluster.SimpleFillNIC({}), 957 constants.NICS_PARAMETER_TYPES, result.append) 958 VerifyNic("cluster", cluster.SimpleFillNIC({}), result.append) 959 VerifyType("cluster", "ndparams", cluster.SimpleFillND({}), 960 constants.NDS_PARAMETER_TYPES, result.append) 961 VerifyIpolicy("cluster", cluster.ipolicy, True, result.append) 962 963 for disk_template in cluster.diskparams: 964 if disk_template not in constants.DTS_HAVE_ACCESS: 965 continue 966 967 access = cluster.diskparams[disk_template].get(constants.LDP_ACCESS, 968 constants.DISK_KERNELSPACE) 969 if access not in constants.DISK_VALID_ACCESS_MODES: 970 result.append( 971 "Invalid value of '%s:%s': '%s' (expected one of %s)" % ( 972 disk_template, constants.LDP_ACCESS, access, 973 utils.CommaJoin(constants.DISK_VALID_ACCESS_MODES) 974 ) 975 ) 976 977 self._VerifyDisks(data, result) 978 979 # per-instance checks 980 for instance_uuid in data.instances: 981 instance = data.instances[instance_uuid] 982 if instance.uuid != instance_uuid: 983 result.append("instance '%s' is indexed by wrong UUID '%s'" % 984 (instance.name, instance_uuid)) 985 if instance.primary_node not in data.nodes: 986 result.append("instance '%s' has invalid primary node '%s'" % 987 (instance.name, instance.primary_node)) 988 for snode in self._UnlockedGetInstanceSecondaryNodes(instance.uuid): 989 if snode not in data.nodes: 990 result.append("instance '%s' has invalid secondary node '%s'" % 991 (instance.name, snode)) 992 for idx, nic in enumerate(instance.nics): 993 if nic.mac in seen_macs: 994 result.append("instance '%s' has NIC %d mac %s duplicate" % 995 (instance.name, idx, nic.mac)) 996 else: 997 seen_macs.append(nic.mac) 998 if nic.nicparams: 999 filled = cluster.SimpleFillNIC(nic.nicparams) 1000 owner = "instance %s nic %d" % (instance.name, idx) 1001 VerifyType(owner, "nicparams", 1002 filled, constants.NICS_PARAMETER_TYPES, result.append) 1003 VerifyNic(owner, filled, result.append) 1004 1005 # parameter checks 1006 if instance.beparams: 1007 VerifyType("instance %s" % instance.name, "beparams", 1008 cluster.FillBE(instance), constants.BES_PARAMETER_TYPES, 1009 result.append) 1010 1011 # check that disks exists 1012 for disk_uuid in instance.disks: 1013 if disk_uuid not in data.disks: 1014 result.append("Instance '%s' has invalid disk '%s'" % 1015 (instance.name, disk_uuid)) 1016 1017 instance_disks = self._UnlockedGetInstanceDisks(instance.uuid) 1018 # gather the drbd ports for duplicate checks 1019 for (idx, dsk) in enumerate(instance_disks): 1020 if dsk.dev_type in constants.DTS_DRBD: 1021 tcp_port = dsk.logical_id[2] 1022 if tcp_port not in ports: 1023 ports[tcp_port] = [] 1024 ports[tcp_port].append((instance.name, "drbd disk %s" % idx)) 1025 # gather network port reservation 1026 net_port = getattr(instance, "network_port", None) 1027 if net_port is not None: 1028 if net_port not in ports: 1029 ports[net_port] = [] 1030 ports[net_port].append((instance.name, "network port")) 1031 1032 wrong_names = _CheckInstanceDiskIvNames(instance_disks) 1033 if wrong_names: 1034 tmp = "; ".join(("name of disk %s should be '%s', but is '%s'" % 1035 (idx, exp_name, actual_name)) 1036 for (idx, exp_name, actual_name) in wrong_names) 1037 1038 result.append("Instance '%s' has wrongly named disks: %s" % 1039 (instance.name, tmp)) 1040 1041 # cluster-wide pool of free ports 1042 for free_port in cluster.tcpudp_port_pool: 1043 if free_port not in ports: 1044 ports[free_port] = [] 1045 ports[free_port].append(("cluster", "port marked as free")) 1046 1047 # compute tcp/udp duplicate ports 1048 keys = ports.keys() 1049 keys.sort() 1050 for pnum in keys: 1051 pdata = ports[pnum] 1052 if len(pdata) > 1: 1053 txt = utils.CommaJoin(["%s/%s" % val for val in pdata]) 1054 result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt)) 1055 1056 # highest used tcp port check 1057 if keys: 1058 if keys[-1] > cluster.highest_used_port: 1059 result.append("Highest used port mismatch, saved %s, computed %s" % 1060 (cluster.highest_used_port, keys[-1])) 1061 1062 if not data.nodes[cluster.master_node].master_candidate: 1063 result.append("Master node is not a master candidate") 1064 1065 # master candidate checks 1066 mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats() 1067 if mc_now < mc_max: 1068 result.append("Not enough master candidates: actual %d, target %d" % 1069 (mc_now, mc_max)) 1070 1071 # node checks 1072 for node_uuid, node in data.nodes.items(): 1073 if node.uuid != node_uuid: 1074 result.append("Node '%s' is indexed by wrong UUID '%s'" % 1075 (node.name, node_uuid)) 1076 if [node.master_candidate, node.drained, node.offline].count(True) > 1: 1077 result.append("Node %s state is invalid: master_candidate=%s," 1078 " drain=%s, offline=%s" % 1079 (node.name, node.master_candidate, node.drained, 1080 node.offline)) 1081 if node.group not in data.nodegroups: 1082 result.append("Node '%s' has invalid group '%s'" % 1083 (node.name, node.group)) 1084 else: 1085 VerifyType("node %s" % node.name, "ndparams", 1086 cluster.FillND(node, data.nodegroups[node.group]), 1087 constants.NDS_PARAMETER_TYPES, result.append) 1088 used_globals = constants.NDC_GLOBALS.intersection(node.ndparams) 1089 if used_globals: 1090 result.append("Node '%s' has some global parameters set: %s" % 1091 (node.name, utils.CommaJoin(used_globals))) 1092 1093 # nodegroups checks 1094 nodegroups_names = set() 1095 for nodegroup_uuid in data.nodegroups: 1096 nodegroup = data.nodegroups[nodegroup_uuid] 1097 if nodegroup.uuid != nodegroup_uuid: 1098 result.append("node group '%s' (uuid: '%s') indexed by wrong uuid '%s'" 1099 % (nodegroup.name, nodegroup.uuid, nodegroup_uuid)) 1100 if utils.UUID_RE.match(nodegroup.name.lower()): 1101 result.append("node group '%s' (uuid: '%s') has uuid-like name" % 1102 (nodegroup.name, nodegroup.uuid)) 1103 if nodegroup.name in nodegroups_names: 1104 result.append("duplicate node group name '%s'" % nodegroup.name) 1105 else: 1106 nodegroups_names.add(nodegroup.name) 1107 group_name = "group %s" % nodegroup.name 1108 VerifyIpolicy(group_name, cluster.SimpleFillIPolicy(nodegroup.ipolicy), 1109 False, result.append) 1110 if nodegroup.ndparams: 1111 VerifyType(group_name, "ndparams", 1112 cluster.SimpleFillND(nodegroup.ndparams), 1113 constants.NDS_PARAMETER_TYPES, result.append) 1114 1115 # drbd minors check 1116 # FIXME: The check for DRBD map needs to be implemented in WConfd 1117 1118 # IP checks 1119 default_nicparams = cluster.nicparams[constants.PP_DEFAULT] 1120 ips = {} 1121 1122 def _AddIpAddress(ip, name): 1123 ips.setdefault(ip, []).append(name)
1124 1125 _AddIpAddress(cluster.master_ip, "cluster_ip") 1126 1127 for node in data.nodes.values(): 1128 _AddIpAddress(node.primary_ip, "node:%s/primary" % node.name) 1129 if node.secondary_ip != node.primary_ip: 1130 _AddIpAddress(node.secondary_ip, "node:%s/secondary" % node.name) 1131 1132 for instance in data.instances.values(): 1133 for idx, nic in enumerate(instance.nics): 1134 if nic.ip is None: 1135 continue 1136 1137 nicparams = objects.FillDict(default_nicparams, nic.nicparams) 1138 nic_mode = nicparams[constants.NIC_MODE] 1139 nic_link = nicparams[constants.NIC_LINK] 1140 1141 if nic_mode == constants.NIC_MODE_BRIDGED: 1142 link = "bridge:%s" % nic_link 1143 elif nic_mode == constants.NIC_MODE_ROUTED: 1144 link = "route:%s" % nic_link 1145 elif nic_mode == constants.NIC_MODE_OVS: 1146 link = "ovs:%s" % nic_link 1147 else: 1148 raise errors.ProgrammerError("NIC mode '%s' not handled" % nic_mode) 1149 1150 _AddIpAddress("%s/%s/%s" % (link, nic.ip, nic.network), 1151 "instance:%s/nic:%d" % (instance.name, idx)) 1152 1153 for ip, owners in ips.items(): 1154 if len(owners) > 1: 1155 result.append("IP address %s is used by multiple owners: %s" % 1156 (ip, utils.CommaJoin(owners))) 1157 1158 return result 1159
1160 - def _UnlockedVerifyConfigAndLog(self, feedback_fn=None):
1161 """Verify the configuration and log any errors. 1162 1163 The errors get logged as critical errors and also to the feedback function, 1164 if given. 1165 1166 @param feedback_fn: Callable feedback function 1167 @rtype: list 1168 @return: a list of error messages; a non-empty list signifies 1169 configuration errors 1170 1171 """ 1172 assert feedback_fn is None or callable(feedback_fn) 1173 1174 # Warn on config errors, but don't abort the save - the 1175 # configuration has already been modified, and we can't revert; 1176 # the best we can do is to warn the user and save as is, leaving 1177 # recovery to the user 1178 config_errors = self._UnlockedVerifyConfig() 1179 if config_errors: 1180 errmsg = ("Configuration data is not consistent: %s" % 1181 (utils.CommaJoin(config_errors))) 1182 logging.critical(errmsg) 1183 if feedback_fn: 1184 feedback_fn(errmsg) 1185 return config_errors
1186 1187 @ConfigSync(shared=1)
1188 - def VerifyConfig(self):
1189 """Verify function. 1190 1191 This is just a wrapper over L{_UnlockedVerifyConfig}. 1192 1193 @rtype: list 1194 @return: a list of error messages; a non-empty list signifies 1195 configuration errors 1196 1197 """ 1198 return self._UnlockedVerifyConfig()
1199 1200 @ConfigSync()
1201 - def AddTcpUdpPort(self, port):
1202 """Adds a new port to the available port pool. 1203 1204 @warning: this method does not "flush" the configuration (via 1205 L{_WriteConfig}); callers should do that themselves once the 1206 configuration is stable 1207 1208 """ 1209 if not isinstance(port, int): 1210 raise errors.ProgrammerError("Invalid type passed for port") 1211 1212 self._ConfigData().cluster.tcpudp_port_pool.add(port)
1213 1214 @ConfigSync(shared=1)
1215 - def GetPortList(self):
1216 """Returns a copy of the current port list. 1217 1218 """ 1219 return self._ConfigData().cluster.tcpudp_port_pool.copy()
1220 1221 @ConfigSync()
1222 - def AllocatePort(self):
1223 """Allocate a port. 1224 1225 The port will be taken from the available port pool or from the 1226 default port range (and in this case we increase 1227 highest_used_port). 1228 1229 """ 1230 # If there are TCP/IP ports configured, we use them first. 1231 if self._ConfigData().cluster.tcpudp_port_pool: 1232 port = self._ConfigData().cluster.tcpudp_port_pool.pop() 1233 else: 1234 port = self._ConfigData().cluster.highest_used_port + 1 1235 if port >= constants.LAST_DRBD_PORT: 1236 raise errors.ConfigurationError("The highest used port is greater" 1237 " than %s. Aborting." % 1238 constants.LAST_DRBD_PORT) 1239 self._ConfigData().cluster.highest_used_port = port 1240 return port
1241 1242 @ConfigSync(shared=1)
1243 - def ComputeDRBDMap(self):
1244 """Compute the used DRBD minor/nodes. 1245 1246 This is just a wrapper over a call to WConfd. 1247 1248 @return: dictionary of node_uuid: dict of minor: instance_uuid; 1249 the returned dict will have all the nodes in it (even if with 1250 an empty list). 1251 1252 """ 1253 if self._offline: 1254 raise errors.ProgrammerError("Can't call ComputeDRBDMap in offline mode") 1255 else: 1256 return dict(map(lambda (k, v): (k, dict(v)), 1257 self._wconfd.ComputeDRBDMap()))
1258
1259 - def AllocateDRBDMinor(self, node_uuids, disk_uuid):
1260 """Allocate a drbd minor. 1261 1262 This is just a wrapper over a call to WConfd. 1263 1264 The free minor will be automatically computed from the existing 1265 devices. A node can not be given multiple times. 1266 The result is the list of minors, in the same 1267 order as the passed nodes. 1268 1269 @type node_uuids: list of strings 1270 @param node_uuids: the nodes in which we allocate minors 1271 @type disk_uuid: string 1272 @param disk_uuid: the disk for which we allocate minors 1273 @rtype: list of ints 1274 @return: A list of minors in the same order as the passed nodes 1275 1276 """ 1277 assert isinstance(disk_uuid, basestring), \ 1278 "Invalid argument '%s' passed to AllocateDRBDMinor" % disk_uuid 1279 1280 if self._offline: 1281 raise errors.ProgrammerError("Can't call AllocateDRBDMinor" 1282 " in offline mode") 1283 1284 result = self._wconfd.AllocateDRBDMinor(disk_uuid, node_uuids) 1285 logging.debug("Request to allocate drbd minors, input: %s, returning %s", 1286 node_uuids, result) 1287 return result
1288
1289 - def _UnlockedReleaseDRBDMinors(self, disk_uuid):
1290 """Release temporary drbd minors allocated for a given disk. 1291 1292 This is just a wrapper over a call to WConfd. 1293 1294 @type disk_uuid: string 1295 @param disk_uuid: the disk for which temporary minors should be released 1296 1297 """ 1298 assert isinstance(disk_uuid, basestring), \ 1299 "Invalid argument passed to ReleaseDRBDMinors" 1300 # in offline mode we allow the calls to release DRBD minors, 1301 # because then nothing can be allocated anyway; 1302 # this is useful for testing 1303 if not self._offline: 1304 self._wconfd.ReleaseDRBDMinors(disk_uuid)
1305 1306 @ConfigSync()
1307 - def ReleaseDRBDMinors(self, disk_uuid):
1308 """Release temporary drbd minors allocated for a given disk. 1309 1310 This should be called on the error paths, on the success paths 1311 it's automatically called by the ConfigWriter add and update 1312 functions. 1313 1314 This function is just a wrapper over L{_UnlockedReleaseDRBDMinors}. 1315 1316 @type disk_uuid: string 1317 @param disk_uuid: the disk for which temporary minors should be released 1318 1319 """ 1320 self._UnlockedReleaseDRBDMinors(disk_uuid)
1321 1322 @ConfigSync(shared=1)
1323 - def GetInstanceDiskTemplate(self, inst_uuid):
1324 """Return the disk template of an instance. 1325 1326 This corresponds to the currently attached disks. If no disks are attached, 1327 it is L{constants.DT_DISKLESS}, if homogeneous disk types are attached, 1328 that type is returned, if that isn't the case, L{constants.DT_MIXED} is 1329 returned. 1330 1331 @type inst_uuid: str 1332 @param inst_uuid: The uuid of the instance. 1333 """ 1334 return utils.GetDiskTemplate(self._UnlockedGetInstanceDisks(inst_uuid))
1335 1336 @ConfigSync(shared=1)
1337 - def GetConfigVersion(self):
1338 """Get the configuration version. 1339 1340 @return: Config version 1341 1342 """ 1343 return self._ConfigData().version
1344 1345 @ConfigSync(shared=1)
1346 - def GetClusterName(self):
1347 """Get cluster name. 1348 1349 @return: Cluster name 1350 1351 """ 1352 return self._ConfigData().cluster.cluster_name
1353 1354 @ConfigSync(shared=1)
1355 - def GetMasterNode(self):
1356 """Get the UUID of the master node for this cluster. 1357 1358 @return: Master node UUID 1359 1360 """ 1361 return self._ConfigData().cluster.master_node
1362 1363 @ConfigSync(shared=1)
1364 - def GetMasterNodeName(self):
1365 """Get the hostname of the master node for this cluster. 1366 1367 @return: Master node hostname 1368 1369 """ 1370 return self._UnlockedGetNodeName(self._ConfigData().cluster.master_node)
1371 1372 @ConfigSync(shared=1)
1373 - def GetMasterNodeInfo(self):
1374 """Get the master node information for this cluster. 1375 1376 @rtype: objects.Node 1377 @return: Master node L{objects.Node} object 1378 1379 """ 1380 return self._UnlockedGetNodeInfo(self._ConfigData().cluster.master_node)
1381 1382 @ConfigSync(shared=1)
1383 - def GetMasterIP(self):
1384 """Get the IP of the master node for this cluster. 1385 1386 @return: Master IP 1387 1388 """ 1389 return self._ConfigData().cluster.master_ip
1390 1391 @ConfigSync(shared=1)
1392 - def GetMasterNetdev(self):
1393 """Get the master network device for this cluster. 1394 1395 """ 1396 return self._ConfigData().cluster.master_netdev
1397 1398 @ConfigSync(shared=1)
1399 - def GetMasterNetmask(self):
1400 """Get the netmask of the master node for this cluster. 1401 1402 """ 1403 return self._ConfigData().cluster.master_netmask
1404 1405 @ConfigSync(shared=1)
1406 - def GetUseExternalMipScript(self):
1407 """Get flag representing whether to use the external master IP setup script. 1408 1409 """ 1410 return self._ConfigData().cluster.use_external_mip_script
1411 1412 @ConfigSync(shared=1)
1413 - def GetFileStorageDir(self):
1414 """Get the file storage dir for this cluster. 1415 1416 """ 1417 return self._ConfigData().cluster.file_storage_dir
1418 1419 @ConfigSync(shared=1)
1420 - def GetSharedFileStorageDir(self):
1421 """Get the shared file storage dir for this cluster. 1422 1423 """ 1424 return self._ConfigData().cluster.shared_file_storage_dir
1425 1426 @ConfigSync(shared=1)
1427 - def GetGlusterStorageDir(self):
1428 """Get the Gluster storage dir for this cluster. 1429 1430 """ 1431 return self._ConfigData().cluster.gluster_storage_dir
1432 1433 @ConfigSync(shared=1)
1434 - def GetHypervisorType(self):
1435 """Get the hypervisor type for this cluster. 1436 1437 """ 1438 return self._ConfigData().cluster.enabled_hypervisors[0]
1439 1440 @ConfigSync(shared=1)
1441 - def GetRsaHostKey(self):
1442 """Return the rsa hostkey from the config. 1443 1444 @rtype: string 1445 @return: the rsa hostkey 1446 1447 """ 1448 return self._ConfigData().cluster.rsahostkeypub
1449 1450 @ConfigSync(shared=1)
1451 - def GetDsaHostKey(self):
1452 """Return the dsa hostkey from the config. 1453 1454 @rtype: string 1455 @return: the dsa hostkey 1456 1457 """ 1458 return self._ConfigData().cluster.dsahostkeypub
1459 1460 @ConfigSync(shared=1)
1461 - def GetDefaultIAllocator(self):
1462 """Get the default instance allocator for this cluster. 1463 1464 """ 1465 return self._ConfigData().cluster.default_iallocator
1466 1467 @ConfigSync(shared=1)
1468 - def GetDefaultIAllocatorParameters(self):
1469 """Get the default instance allocator parameters for this cluster. 1470 1471 @rtype: dict 1472 @return: dict of iallocator parameters 1473 1474 """ 1475 return self._ConfigData().cluster.default_iallocator_params
1476 1477 @ConfigSync(shared=1)
1478 - def GetPrimaryIPFamily(self):
1479 """Get cluster primary ip family. 1480 1481 @return: primary ip family 1482 1483 """ 1484 return self._ConfigData().cluster.primary_ip_family
1485 1486 @ConfigSync(shared=1)
1487 - def GetMasterNetworkParameters(self):
1488 """Get network parameters of the master node. 1489 1490 @rtype: L{object.MasterNetworkParameters} 1491 @return: network parameters of the master node 1492 1493 """ 1494 cluster = self._ConfigData().cluster 1495 result = objects.MasterNetworkParameters( 1496 uuid=cluster.master_node, ip=cluster.master_ip, 1497 netmask=cluster.master_netmask, netdev=cluster.master_netdev, 1498 ip_family=cluster.primary_ip_family) 1499 1500 return result
1501 1502 @ConfigSync(shared=1)
1503 - def GetInstallImage(self):
1504 """Get the install image location 1505 1506 @rtype: string 1507 @return: location of the install image 1508 1509 """ 1510 return self._ConfigData().cluster.install_image
1511 1512 @ConfigSync()
1513 - def SetInstallImage(self, install_image):
1514 """Set the install image location 1515 1516 @type install_image: string 1517 @param install_image: location of the install image 1518 1519 """ 1520 self._ConfigData().cluster.install_image = install_image
1521 1522 @ConfigSync(shared=1)
1523 - def GetInstanceCommunicationNetwork(self):
1524 """Get cluster instance communication network 1525 1526 @rtype: string 1527 @return: instance communication network, which is the name of the 1528 network used for instance communication 1529 1530 """ 1531 return self._ConfigData().cluster.instance_communication_network
1532 1533 @ConfigSync()
1534 - def SetInstanceCommunicationNetwork(self, network_name):
1535 """Set cluster instance communication network 1536 1537 @type network_name: string 1538 @param network_name: instance communication network, which is the name of 1539 the network used for instance communication 1540 1541 """ 1542 self._ConfigData().cluster.instance_communication_network = network_name
1543 1544 @ConfigSync(shared=1)
1545 - def GetZeroingImage(self):
1546 """Get the zeroing image location 1547 1548 @rtype: string 1549 @return: the location of the zeroing image 1550 1551 """ 1552 return self._config_data.cluster.zeroing_image
1553 1554 @ConfigSync(shared=1)
1555 - def GetCompressionTools(self):
1556 """Get cluster compression tools 1557 1558 @rtype: list of string 1559 @return: a list of tools that are cleared for use in this cluster for the 1560 purpose of compressing data 1561 1562 """ 1563 return self._ConfigData().cluster.compression_tools
1564 1565 @ConfigSync()
1566 - def SetCompressionTools(self, tools):
1567 """Set cluster compression tools 1568 1569 @type tools: list of string 1570 @param tools: a list of tools that are cleared for use in this cluster for 1571 the purpose of compressing data 1572 1573 """ 1574 self._ConfigData().cluster.compression_tools = tools
1575 1576 @ConfigSync()
1577 - def AddNodeGroup(self, group, ec_id, check_uuid=True):
1578 """Add a node group to the configuration. 1579 1580 This method calls group.UpgradeConfig() to fill any missing attributes 1581 according to their default values. 1582 1583 @type group: L{objects.NodeGroup} 1584 @param group: the NodeGroup object to add 1585 @type ec_id: string 1586 @param ec_id: unique id for the job to use when creating a missing UUID 1587 @type check_uuid: bool 1588 @param check_uuid: add an UUID to the group if it doesn't have one or, if 1589 it does, ensure that it does not exist in the 1590 configuration already 1591 1592 """ 1593 self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1594
1595 - def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
1596 """Add a node group to the configuration. 1597 1598 """ 1599 logging.info("Adding node group %s to configuration", group.name) 1600 1601 # Some code might need to add a node group with a pre-populated UUID 1602 # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass 1603 # the "does this UUID" exist already check. 1604 if check_uuid: 1605 self._EnsureUUID(group, ec_id) 1606 1607 try: 1608 existing_uuid = self._UnlockedLookupNodeGroup(group.name) 1609 except errors.OpPrereqError: 1610 pass 1611 else: 1612 raise errors.OpPrereqError("Desired group name '%s' already exists as a" 1613 " node group (UUID: %s)" % 1614 (group.name, existing_uuid), 1615 errors.ECODE_EXISTS) 1616 1617 group.serial_no = 1 1618 group.ctime = group.mtime = time.time() 1619 group.UpgradeConfig() 1620 1621 self._ConfigData().nodegroups[group.uuid] = group 1622 self._ConfigData().cluster.serial_no += 1
1623 1624 @ConfigSync()
1625 - def RemoveNodeGroup(self, group_uuid):
1626 """Remove a node group from the configuration. 1627 1628 @type group_uuid: string 1629 @param group_uuid: the UUID of the node group to remove 1630 1631 """ 1632 logging.info("Removing node group %s from configuration", group_uuid) 1633 1634 if group_uuid not in self._ConfigData().nodegroups: 1635 raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid) 1636 1637 assert len(self._ConfigData().nodegroups) != 1, \ 1638 "Group '%s' is the only group, cannot be removed" % group_uuid 1639 1640 del self._ConfigData().nodegroups[group_uuid] 1641 self._ConfigData().cluster.serial_no += 1
1642
1643 - def _UnlockedLookupNodeGroup(self, target):
1644 """Lookup a node group's UUID. 1645 1646 @type target: string or None 1647 @param target: group name or UUID or None to look for the default 1648 @rtype: string 1649 @return: nodegroup UUID 1650 @raises errors.OpPrereqError: when the target group cannot be found 1651 1652 """ 1653 if target is None: 1654 if len(self._ConfigData().nodegroups) != 1: 1655 raise errors.OpPrereqError("More than one node group exists. Target" 1656 " group must be specified explicitly.") 1657 else: 1658 return self._ConfigData().nodegroups.keys()[0] 1659 if target in self._ConfigData().nodegroups: 1660 return target 1661 for nodegroup in self._ConfigData().nodegroups.values(): 1662 if nodegroup.name == target: 1663 return nodegroup.uuid 1664 raise errors.OpPrereqError("Node group '%s' not found" % target, 1665 errors.ECODE_NOENT)
1666 1667 @ConfigSync(shared=1)
1668 - def LookupNodeGroup(self, target):
1669 """Lookup a node group's UUID. 1670 1671 This function is just a wrapper over L{_UnlockedLookupNodeGroup}. 1672 1673 @type target: string or None 1674 @param target: group name or UUID or None to look for the default 1675 @rtype: string 1676 @return: nodegroup UUID 1677 1678 """ 1679 return self._UnlockedLookupNodeGroup(target)
1680
1681 - def _UnlockedGetNodeGroup(self, uuid):
1682 """Lookup a node group. 1683 1684 @type uuid: string 1685 @param uuid: group UUID 1686 @rtype: L{objects.NodeGroup} or None 1687 @return: nodegroup object, or None if not found 1688 1689 """ 1690 if uuid not in self._ConfigData().nodegroups: 1691 return None 1692 1693 return self._ConfigData().nodegroups[uuid]
1694 1695 @ConfigSync(shared=1)
1696 - def GetNodeGroup(self, uuid):
1697 """Lookup a node group. 1698 1699 @type uuid: string 1700 @param uuid: group UUID 1701 @rtype: L{objects.NodeGroup} or None 1702 @return: nodegroup object, or None if not found 1703 1704 """ 1705 return self._UnlockedGetNodeGroup(uuid)
1706
1707 - def _UnlockedGetAllNodeGroupsInfo(self):
1708 """Get the configuration of all node groups. 1709 1710 """ 1711 return dict(self._ConfigData().nodegroups)
1712 1713 @ConfigSync(shared=1)
1714 - def GetAllNodeGroupsInfo(self):
1715 """Get the configuration of all node groups. 1716 1717 """ 1718 return self._UnlockedGetAllNodeGroupsInfo()
1719 1720 @ConfigSync(shared=1)
1721 - def GetAllNodeGroupsInfoDict(self):
1722 """Get the configuration of all node groups expressed as a dictionary of 1723 dictionaries. 1724 1725 """ 1726 return dict(map(lambda (uuid, ng): (uuid, ng.ToDict()), 1727 self._UnlockedGetAllNodeGroupsInfo().items()))
1728 1729 @ConfigSync(shared=1)
1730 - def GetNodeGroupList(self):
1731 """Get a list of node groups. 1732 1733 """ 1734 return self._ConfigData().nodegroups.keys()
1735 1736 @ConfigSync(shared=1)
1737 - def GetNodeGroupMembersByNodes(self, nodes):
1738 """Get nodes which are member in the same nodegroups as the given nodes. 1739 1740 """ 1741 ngfn = lambda node_uuid: self._UnlockedGetNodeInfo(node_uuid).group 1742 return frozenset(member_uuid 1743 for node_uuid in nodes 1744 for member_uuid in 1745 self._UnlockedGetNodeGroup(ngfn(node_uuid)).members)
1746 1747 @ConfigSync(shared=1)
1748 - def GetMultiNodeGroupInfo(self, group_uuids):
1749 """Get the configuration of multiple node groups. 1750 1751 @param group_uuids: List of node group UUIDs 1752 @rtype: list 1753 @return: List of tuples of (group_uuid, group_info) 1754 1755 """ 1756 return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1757
1758 - def AddInstance(self, instance, _ec_id, replace=False):
1759 """Add an instance to the config. 1760 1761 This should be used after creating a new instance. 1762 1763 @type instance: L{objects.Instance} 1764 @param instance: the instance object 1765 @type replace: bool 1766 @param replace: if true, expect the instance to be present and 1767 replace rather than add. 1768 1769 """ 1770 if not isinstance(instance, objects.Instance): 1771 raise errors.ProgrammerError("Invalid type passed to AddInstance") 1772 1773 all_macs = self._AllMACs() 1774 for nic in instance.nics: 1775 if nic.mac in all_macs: 1776 raise errors.ConfigurationError("Cannot add instance %s:" 1777 " MAC address '%s' already in use." % 1778 (instance.name, nic.mac)) 1779 1780 if replace: 1781 self._CheckUUIDpresent(instance) 1782 else: 1783 self._CheckUniqueUUID(instance, include_temporary=False) 1784 1785 instance.serial_no = 1 1786 instance.ctime = instance.mtime = time.time() 1787 1788 utils.SimpleRetry(True, self._wconfd.AddInstance, 0.1, 30, 1789 args=[instance.ToDict(), self._GetWConfdContext()]) 1790 self.OutDate()
1791
1792 - def _EnsureUUID(self, item, ec_id):
1793 """Ensures a given object has a valid UUID. 1794 1795 @param item: the instance or node to be checked 1796 @param ec_id: the execution context id for the uuid reservation 1797 1798 """ 1799 if not item.uuid: 1800 item.uuid = self._GenerateUniqueID(ec_id) 1801 else: 1802 self._CheckUniqueUUID(item, include_temporary=True)
1803
1804 - def _CheckUniqueUUID(self, item, include_temporary):
1805 """Checks that the UUID of the given object is unique. 1806 1807 @param item: the instance or node to be checked 1808 @param include_temporary: whether temporarily generated UUID's should be 1809 included in the check. If the UUID of the item to be checked is 1810 a temporarily generated one, this has to be C{False}. 1811 1812 """ 1813 if not item.uuid: 1814 raise errors.ConfigurationError("'%s' must have an UUID" % (item.name,)) 1815 if item.uuid in self._AllIDs(include_temporary=include_temporary): 1816 raise errors.ConfigurationError("Cannot add '%s': UUID %s already" 1817 " in use" % (item.name, item.uuid))
1818
1819 - def _CheckUUIDpresent(self, item):
1820 """Checks that an object with the given UUID exists. 1821 1822 @param item: the instance or other UUID possessing object to verify that 1823 its UUID is present 1824 1825 """ 1826 if not item.uuid: 1827 raise errors.ConfigurationError("'%s' must have an UUID" % (item.name,)) 1828 if item.uuid not in self._AllIDs(include_temporary=False): 1829 raise errors.ConfigurationError("Cannot replace '%s': UUID %s not present" 1830 % (item.name, item.uuid))
1831
1832 - def _SetInstanceStatus(self, inst_uuid, status, disks_active, 1833 admin_state_source):
1834 """Set the instance's status to a given value. 1835 1836 @rtype: L{objects.Instance} 1837 @return: the updated instance object 1838 1839 """ 1840 if inst_uuid not in self._ConfigData().instances: 1841 raise errors.ConfigurationError("Unknown instance '%s'" % 1842 inst_uuid) 1843 instance = self._ConfigData().instances[inst_uuid] 1844 1845 if status is None: 1846 status = instance.admin_state 1847 if disks_active is None: 1848 disks_active = instance.disks_active 1849 if admin_state_source is None: 1850 admin_state_source = instance.admin_state_source 1851 1852 assert status in constants.ADMINST_ALL, \ 1853 "Invalid status '%s' passed to SetInstanceStatus" % (status,) 1854 1855 if instance.admin_state != status or \ 1856 instance.disks_active != disks_active or \ 1857 instance.admin_state_source != admin_state_source: 1858 instance.admin_state = status 1859 instance.disks_active = disks_active 1860 instance.admin_state_source = admin_state_source 1861 instance.serial_no += 1 1862 instance.mtime = time.time() 1863 return instance
1864 1865 @ConfigSync()
1866 - def MarkInstanceUp(self, inst_uuid):
1867 """Mark the instance status to up in the config. 1868 1869 This also sets the instance disks active flag. 1870 1871 @rtype: L{objects.Instance} 1872 @return: the updated instance object 1873 1874 """ 1875 return self._SetInstanceStatus(inst_uuid, constants.ADMINST_UP, True, 1876 constants.ADMIN_SOURCE)
1877 1878 @ConfigSync()
1879 - def MarkInstanceOffline(self, inst_uuid):
1880 """Mark the instance status to down in the config. 1881 1882 This also clears the instance disks active flag. 1883 1884 @rtype: L{objects.Instance} 1885 @return: the updated instance object 1886 1887 """ 1888 return self._SetInstanceStatus(inst_uuid, constants.ADMINST_OFFLINE, False, 1889 constants.ADMIN_SOURCE)
1890 1891 @ConfigSync()
1892 - def RemoveInstance(self, inst_uuid):
1893 """Remove the instance from the configuration. 1894 1895 """ 1896 if inst_uuid not in self._ConfigData().instances: 1897 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid) 1898 1899 # If a network port has been allocated to the instance, 1900 # return it to the pool of free ports. 1901 inst = self._ConfigData().instances[inst_uuid] 1902 network_port = getattr(inst, "network_port", None) 1903 if network_port is not None: 1904 self._ConfigData().cluster.tcpudp_port_pool.add(network_port) 1905 1906 instance = self._UnlockedGetInstanceInfo(inst_uuid) 1907 1908 # FIXME: After RemoveInstance is moved to WConfd, use its internal 1909 # function from TempRes module. 1910 for nic in instance.nics: 1911 if nic.network and nic.ip: 1912 # Return all IP addresses to the respective address pools 1913 self._UnlockedCommitIp(constants.RELEASE_ACTION, nic.network, nic.ip) 1914 1915 del self._ConfigData().instances[inst_uuid] 1916 self._ConfigData().cluster.serial_no += 1
1917 1918 @ConfigSync()
1919 - def RenameInstance(self, inst_uuid, new_name):
1920 """Rename an instance. 1921 1922 This needs to be done in ConfigWriter and not by RemoveInstance 1923 combined with AddInstance as only we can guarantee an atomic 1924 rename. 1925 1926 """ 1927 if inst_uuid not in self._ConfigData().instances: 1928 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid) 1929 1930 inst = self._ConfigData().instances[inst_uuid] 1931 inst.name = new_name 1932 1933 instance_disks = self._UnlockedGetInstanceDisks(inst_uuid) 1934 for (_, disk) in enumerate(instance_disks): 1935 if disk.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]: 1936 # rename the file paths in logical and physical id 1937 file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1])) 1938 disk.logical_id = (disk.logical_id[0], 1939 utils.PathJoin(file_storage_dir, inst.name, 1940 os.path.basename(disk.logical_id[1]))) 1941 1942 # Force update of ssconf files 1943 self._ConfigData().cluster.serial_no += 1
1944 1945 @ConfigSync()
1946 - def MarkInstanceDown(self, inst_uuid):
1947 """Mark the status of an instance to down in the configuration. 1948 1949 This does not touch the instance disks active flag, as shut down instances 1950 can still have active disks. 1951 1952 @rtype: L{objects.Instance} 1953 @return: the updated instance object 1954 1955 """ 1956 return self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None, 1957 constants.ADMIN_SOURCE)
1958 1959 @ConfigSync()
1960 - def MarkInstanceUserDown(self, inst_uuid):
1961 """Mark the status of an instance to user down in the configuration. 1962 1963 This does not touch the instance disks active flag, as user shut 1964 down instances can still have active disks. 1965 1966 """ 1967 1968 self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None, 1969 constants.USER_SOURCE)
1970 1971 @ConfigSync()
1972 - def MarkInstanceDisksActive(self, inst_uuid):
1973 """Mark the status of instance disks active. 1974 1975 @rtype: L{objects.Instance} 1976 @return: the updated instance object 1977 1978 """ 1979 return self._SetInstanceStatus(inst_uuid, None, True, None)
1980 1981 @ConfigSync()
1982 - def MarkInstanceDisksInactive(self, inst_uuid):
1983 """Mark the status of instance disks inactive. 1984 1985 @rtype: L{objects.Instance} 1986 @return: the updated instance object 1987 1988 """ 1989 return self._SetInstanceStatus(inst_uuid, None, False, None)
1990
1991 - def _UnlockedGetInstanceList(self):
1992 """Get the list of instances. 1993 1994 This function is for internal use, when the config lock is already held. 1995 1996 """ 1997 return self._ConfigData().instances.keys()
1998 1999 @ConfigSync(shared=1)
2000 - def GetInstanceList(self):
2001 """Get the list of instances. 2002 2003 @return: array of instances, ex. ['instance2-uuid', 'instance1-uuid'] 2004 2005 """ 2006 return self._UnlockedGetInstanceList()
2007
2008 - def ExpandInstanceName(self, short_name):
2009 """Attempt to expand an incomplete instance name. 2010 2011 """ 2012 # Locking is done in L{ConfigWriter.GetAllInstancesInfo} 2013 all_insts = self.GetAllInstancesInfo().values() 2014 expanded_name = _MatchNameComponentIgnoreCase( 2015 short_name, [inst.name for inst in all_insts]) 2016 2017 if expanded_name is not None: 2018 # there has to be exactly one instance with that name 2019 inst = (filter(lambda n: n.name == expanded_name, all_insts)[0]) 2020 return (inst.uuid, inst.name) 2021 else: 2022 return (None, None)
2023
2024 - def _UnlockedGetInstanceInfo(self, inst_uuid):
2025 """Returns information about an instance. 2026 2027 This function is for internal use, when the config lock is already held. 2028 2029 """ 2030 if inst_uuid not in self._ConfigData().instances: 2031 return None 2032 2033 return self._ConfigData().instances[inst_uuid]
2034 2035 @ConfigSync(shared=1)
2036 - def GetInstanceInfo(self, inst_uuid):
2037 """Returns information about an instance. 2038 2039 It takes the information from the configuration file. Other information of 2040 an instance are taken from the live systems. 2041 2042 @param inst_uuid: UUID of the instance 2043 2044 @rtype: L{objects.Instance} 2045 @return: the instance object 2046 2047 """ 2048 return self._UnlockedGetInstanceInfo(inst_uuid)
2049 2050 @ConfigSync(shared=1)
2051 - def GetInstanceNodeGroups(self, inst_uuid, primary_only=False):
2052 """Returns set of node group UUIDs for instance's nodes. 2053 2054 @rtype: frozenset 2055 2056 """ 2057 instance = self._UnlockedGetInstanceInfo(inst_uuid) 2058 if not instance: 2059 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid) 2060 2061 if primary_only: 2062 nodes = [instance.primary_node] 2063 else: 2064 nodes = self._UnlockedGetInstanceNodes(instance.uuid) 2065 2066 return frozenset(self._UnlockedGetNodeInfo(node_uuid).group 2067 for node_uuid in nodes)
2068 2069 @ConfigSync(shared=1)
2070 - def GetInstanceNetworks(self, inst_uuid):
2071 """Returns set of network UUIDs for instance's nics. 2072 2073 @rtype: frozenset 2074 2075 """ 2076 instance = self._UnlockedGetInstanceInfo(inst_uuid) 2077 if not instance: 2078 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid) 2079 2080 networks = set() 2081 for nic in instance.nics: 2082 if nic.network: 2083 networks.add(nic.network) 2084 2085 return frozenset(networks)
2086 2087 @ConfigSync(shared=1)
2088 - def GetMultiInstanceInfo(self, inst_uuids):
2089 """Get the configuration of multiple instances. 2090 2091 @param inst_uuids: list of instance UUIDs 2092 @rtype: list 2093 @return: list of tuples (instance UUID, instance_info), where 2094 instance_info is what would GetInstanceInfo return for the 2095 node, while keeping the original order 2096 2097 """ 2098 return [(uuid, self._UnlockedGetInstanceInfo(uuid)) for uuid in inst_uuids]
2099 2100 @ConfigSync(shared=1)
2101 - def GetMultiInstanceInfoByName(self, inst_names):
2102 """Get the configuration of multiple instances. 2103 2104 @param inst_names: list of instance names 2105 @rtype: list 2106 @return: list of tuples (instance, instance_info), where 2107 instance_info is what would GetInstanceInfo return for the 2108 node, while keeping the original order 2109 2110 """ 2111 result = [] 2112 for name in inst_names: 2113 instance = self._UnlockedGetInstanceInfoByName(name) 2114 if instance: 2115 result.append((instance.uuid, instance)) 2116 else: 2117 raise errors.ConfigurationError("Instance data of instance '%s'" 2118 " not found." % name) 2119 return result
2120 2121 @ConfigSync(shared=1)
2122 - def GetAllInstancesInfo(self):
2123 """Get the configuration of all instances. 2124 2125 @rtype: dict 2126 @return: dict of (instance, instance_info), where instance_info is what 2127 would GetInstanceInfo return for the node 2128 2129 """ 2130 return self._UnlockedGetAllInstancesInfo()
2131
2132 - def _UnlockedGetAllInstancesInfo(self):
2133 my_dict = dict([(inst_uuid, self._UnlockedGetInstanceInfo(inst_uuid)) 2134 for inst_uuid in self._UnlockedGetInstanceList()]) 2135 return my_dict
2136 2137 @ConfigSync(shared=1)
2138 - def GetInstancesInfoByFilter(self, filter_fn):
2139 """Get instance configuration with a filter. 2140 2141 @type filter_fn: callable 2142 @param filter_fn: Filter function receiving instance object as parameter, 2143 returning boolean. Important: this function is called while the 2144 configuration locks is held. It must not do any complex work or call 2145 functions potentially leading to a deadlock. Ideally it doesn't call any 2146 other functions and just compares instance attributes. 2147 2148 """ 2149 return dict((uuid, inst) 2150 for (uuid, inst) in self._ConfigData().instances.items() 2151 if filter_fn(inst))
2152 2153 @ConfigSync(shared=1)
2154 - def GetInstanceInfoByName(self, inst_name):
2155 """Get the L{objects.Instance} object for a named instance. 2156 2157 @param inst_name: name of the instance to get information for 2158 @type inst_name: string 2159 @return: the corresponding L{objects.Instance} instance or None if no 2160 information is available 2161 2162 """ 2163 return self._UnlockedGetInstanceInfoByName(inst_name)
2164
2165 - def _UnlockedGetInstanceInfoByName(self, inst_name):
2166 for inst in self._UnlockedGetAllInstancesInfo().values(): 2167 if inst.name == inst_name: 2168 return inst 2169 return None
2170
2171 - def _UnlockedGetInstanceName(self, inst_uuid):
2172 inst_info = self._UnlockedGetInstanceInfo(inst_uuid) 2173 if inst_info is None: 2174 raise errors.OpExecError("Unknown instance: %s" % inst_uuid) 2175 return inst_info.name
2176 2177 @ConfigSync(shared=1)
2178 - def GetInstanceName(self, inst_uuid):
2179 """Gets the instance name for the passed instance. 2180 2181 @param inst_uuid: instance UUID to get name for 2182 @type inst_uuid: string 2183 @rtype: string 2184 @return: instance name 2185 2186 """ 2187 return self._UnlockedGetInstanceName(inst_uuid)
2188 2189 @ConfigSync(shared=1)
2190 - def GetInstanceNames(self, inst_uuids):
2191 """Gets the instance names for the passed list of nodes. 2192 2193 @param inst_uuids: list of instance UUIDs to get names for 2194 @type inst_uuids: list of strings 2195 @rtype: list of strings 2196 @return: list of instance names 2197 2198 """ 2199 return self._UnlockedGetInstanceNames(inst_uuids)
2200 2201 @ConfigSync()
2202 - def SetInstancePrimaryNode(self, inst_uuid, target_node_uuid):
2203 """Sets the primary node of an existing instance 2204 2205 @param inst_uuid: instance UUID 2206 @type inst_uuid: string 2207 @param target_node_uuid: the new primary node UUID 2208 @type target_node_uuid: string 2209 2210 """ 2211 self._UnlockedGetInstanceInfo(inst_uuid).primary_node = target_node_uuid
2212 2213 @ConfigSync()
2214 - def SetDiskNodes(self, disk_uuid, nodes):
2215 """Sets the nodes of an existing disk 2216 2217 @param disk_uuid: disk UUID 2218 @type disk_uuid: string 2219 @param nodes: the new nodes for the disk 2220 @type nodes: list of node uuids 2221 2222 """ 2223 self._UnlockedGetDiskInfo(disk_uuid).nodes = nodes
2224 2225 @ConfigSync()
2226 - def SetDiskLogicalID(self, disk_uuid, logical_id):
2227 """Sets the logical_id of an existing disk 2228 2229 @param disk_uuid: disk UUID 2230 @type disk_uuid: string 2231 @param logical_id: the new logical_id for the disk 2232 @type logical_id: tuple 2233 2234 """ 2235 disk = self._UnlockedGetDiskInfo(disk_uuid) 2236 if disk is None: 2237 raise errors.ConfigurationError("Unknown disk UUID '%s'" % disk_uuid) 2238 2239 if len(disk.logical_id) != len(logical_id): 2240 raise errors.ProgrammerError("Logical ID format mismatch\n" 2241 "Existing logical ID: %s\n" 2242 "New logical ID: %s", disk.logical_id, 2243 logical_id) 2244 2245 disk.logical_id = logical_id
2246
2247 - def _UnlockedGetInstanceNames(self, inst_uuids):
2248 return [self._UnlockedGetInstanceName(uuid) for uuid in inst_uuids]
2249
2250 - def _UnlockedAddNode(self, node, ec_id):
2251 """Add a node to the configuration. 2252 2253 @type node: L{objects.Node} 2254 @param node: a Node instance 2255 2256 """ 2257 logging.info("Adding node %s to configuration", node.name) 2258 2259 self._EnsureUUID(node, ec_id) 2260 2261 node.serial_no = 1 2262 node.ctime = node.mtime = time.time() 2263 self._UnlockedAddNodeToGroup(node.uuid, node.group) 2264 assert node.uuid in self._ConfigData().nodegroups[node.group].members 2265 self._ConfigData().nodes[node.uuid] = node 2266 self._ConfigData().cluster.serial_no += 1
2267 2268 @ConfigSync()
2269 - def AddNode(self, node, ec_id):
2270 """Add a node to the configuration. 2271 2272 @type node: L{objects.Node} 2273 @param node: a Node instance 2274 2275 """ 2276 self._UnlockedAddNode(node, ec_id)
2277 2278 @ConfigSync()
2279 - def RemoveNode(self, node_uuid):
2280 """Remove a node from the configuration. 2281 2282 """ 2283 logging.info("Removing node %s from configuration", node_uuid) 2284 2285 if node_uuid not in self._ConfigData().nodes: 2286 raise errors.ConfigurationError("Unknown node '%s'" % node_uuid) 2287 2288 self._UnlockedRemoveNodeFromGroup(self._ConfigData().nodes[node_uuid]) 2289 del self._ConfigData().nodes[node_uuid] 2290 self._ConfigData().cluster.serial_no += 1
2291
2292 - def ExpandNodeName(self, short_name):
2293 """Attempt to expand an incomplete node name into a node UUID. 2294 2295 """ 2296 # Locking is done in L{ConfigWriter.GetAllNodesInfo} 2297 all_nodes = self.GetAllNodesInfo().values() 2298 expanded_name = _MatchNameComponentIgnoreCase( 2299 short_name, [node.name for node in all_nodes]) 2300 2301 if expanded_name is not None: 2302 # there has to be exactly one node with that name 2303 node = (filter(lambda n: n.name == expanded_name, all_nodes)[0]) 2304 return (node.uuid, node.name) 2305 else: 2306 return (None, None)
2307
2308 - def _UnlockedGetNodeInfo(self, node_uuid):
2309 """Get the configuration of a node, as stored in the config. 2310 2311 This function is for internal use, when the config lock is already 2312 held. 2313 2314 @param node_uuid: the node UUID 2315 2316 @rtype: L{objects.Node} 2317 @return: the node object 2318 2319 """ 2320 if node_uuid not in self._ConfigData().nodes: 2321 return None 2322 2323 return self._ConfigData().nodes[node_uuid]
2324 2325 @ConfigSync(shared=1)
2326 - def GetNodeInfo(self, node_uuid):
2327 """Get the configuration of a node, as stored in the config. 2328 2329 This is just a locked wrapper over L{_UnlockedGetNodeInfo}. 2330 2331 @param node_uuid: the node UUID 2332 2333 @rtype: L{objects.Node} 2334 @return: the node object 2335 2336 """ 2337 return self._UnlockedGetNodeInfo(node_uuid)
2338 2339 @ConfigSync(shared=1)
2340 - def GetNodeInstances(self, node_uuid):
2341 """Get the instances of a node, as stored in the config. 2342 2343 @param node_uuid: the node UUID 2344 2345 @rtype: (list, list) 2346 @return: a tuple with two lists: the primary and the secondary instances 2347 2348 """ 2349 pri = [] 2350 sec = [] 2351 for inst in self._ConfigData().instances.values(): 2352 if inst.primary_node == node_uuid: 2353 pri.append(inst.uuid) 2354 if node_uuid in self._UnlockedGetInstanceSecondaryNodes(inst.uuid): 2355 sec.append(inst.uuid) 2356 return (pri, sec)
2357 2358 @ConfigSync(shared=1)
2359 - def GetNodeGroupInstances(self, uuid, primary_only=False):
2360 """Get the instances of a node group. 2361 2362 @param uuid: Node group UUID 2363 @param primary_only: Whether to only consider primary nodes 2364 @rtype: frozenset 2365 @return: List of instance UUIDs in node group 2366 2367 """ 2368 if primary_only: 2369 nodes_fn = lambda inst: [inst.primary_node] 2370 else: 2371 nodes_fn = lambda inst: self._UnlockedGetInstanceNodes(inst.uuid) 2372 2373 return frozenset(inst.uuid 2374 for inst in self._ConfigData().instances.values() 2375 for node_uuid in nodes_fn(inst) 2376 if self._UnlockedGetNodeInfo(node_uuid).group == uuid)
2377
2378 - def _UnlockedGetHvparamsString(self, hvname):
2379 """Return the string representation of the list of hyervisor parameters of 2380 the given hypervisor. 2381 2382 @see: C{GetHvparams} 2383 2384 """ 2385 result = "" 2386 hvparams = self._ConfigData().cluster.hvparams[hvname] 2387 for key in hvparams: 2388 result += "%s=%s\n" % (key, hvparams[key]) 2389 return result
2390 2391 @ConfigSync(shared=1)
2392 - def GetHvparamsString(self, hvname):
2393 """Return the hypervisor parameters of the given hypervisor. 2394 2395 @type hvname: string 2396 @param hvname: name of a hypervisor 2397 @rtype: string 2398 @return: string containing key-value-pairs, one pair on each line; 2399 format: KEY=VALUE 2400 2401 """ 2402 return self._UnlockedGetHvparamsString(hvname)
2403
2404 - def _UnlockedGetNodeList(self):
2405 """Return the list of nodes which are in the configuration. 2406 2407 This function is for internal use, when the config lock is already 2408 held. 2409 2410 @rtype: list 2411 2412 """ 2413 return self._ConfigData().nodes.keys()
2414 2415 @ConfigSync(shared=1)
2416 - def GetNodeList(self):
2417 """Return the list of nodes which are in the configuration. 2418 2419 """ 2420 return self._UnlockedGetNodeList()
2421
2422 - def _UnlockedGetOnlineNodeList(self):
2423 """Return the list of nodes which are online. 2424 2425 """ 2426 all_nodes = [self._UnlockedGetNodeInfo(node) 2427 for node in self._UnlockedGetNodeList()] 2428 return [node.uuid for node in all_nodes if not node.offline]
2429 2430 @ConfigSync(shared=1)
2431 - def GetOnlineNodeList(self):
2432 """Return the list of nodes which are online. 2433 2434 """ 2435 return self._UnlockedGetOnlineNodeList()
2436 2437 @ConfigSync(shared=1)
2438 - def GetVmCapableNodeList(self):
2439 """Return the list of nodes which are not vm capable. 2440 2441 """ 2442 all_nodes = [self._UnlockedGetNodeInfo(node) 2443 for node in self._UnlockedGetNodeList()] 2444 return [node.uuid for node in all_nodes if node.vm_capable]
2445 2446 @ConfigSync(shared=1)
2447 - def GetNonVmCapableNodeList(self):
2448 """Return the list of nodes' uuids which are not vm capable. 2449 2450 """ 2451 all_nodes = [self._UnlockedGetNodeInfo(node) 2452 for node in self._UnlockedGetNodeList()] 2453 return [node.uuid for node in all_nodes if not node.vm_capable]
2454 2455 @ConfigSync(shared=1)
2456 - def GetNonVmCapableNodeNameList(self):
2457 """Return the list of nodes' names which are not vm capable. 2458 2459 """ 2460 all_nodes = [self._UnlockedGetNodeInfo(node) 2461 for node in self._UnlockedGetNodeList()] 2462 return [node.name for node in all_nodes if not node.vm_capable]
2463 2464 @ConfigSync(shared=1)
2465 - def GetMultiNodeInfo(self, node_uuids):
2466 """Get the configuration of multiple nodes. 2467 2468 @param node_uuids: list of node UUIDs 2469 @rtype: list 2470 @return: list of tuples of (node, node_info), where node_info is 2471 what would GetNodeInfo return for the node, in the original 2472 order 2473 2474 """ 2475 return [(uuid, self._UnlockedGetNodeInfo(uuid)) for uuid in node_uuids]
2476
2477 - def _UnlockedGetAllNodesInfo(self):
2478 """Gets configuration of all nodes. 2479 2480 @note: See L{GetAllNodesInfo} 2481 2482 """ 2483 return dict([(node_uuid, self._UnlockedGetNodeInfo(node_uuid)) 2484 for node_uuid in self._UnlockedGetNodeList()])
2485 2486 @ConfigSync(shared=1)
2487 - def GetAllNodesInfo(self):
2488 """Get the configuration of all nodes. 2489 2490 @rtype: dict 2491 @return: dict of (node, node_info), where node_info is what 2492 would GetNodeInfo return for the node 2493 2494 """ 2495 return self._UnlockedGetAllNodesInfo()
2496
2497 - def _UnlockedGetNodeInfoByName(self, node_name):
2498 for node in self._UnlockedGetAllNodesInfo().values(): 2499 if node.name == node_name: 2500 return node 2501 return None
2502 2503 @ConfigSync(shared=1)
2504 - def GetNodeInfoByName(self, node_name):
2505 """Get the L{objects.Node} object for a named node. 2506 2507 @param node_name: name of the node to get information for 2508 @type node_name: string 2509 @return: the corresponding L{objects.Node} instance or None if no 2510 information is available 2511 2512 """ 2513 return self._UnlockedGetNodeInfoByName(node_name)
2514 2515 @ConfigSync(shared=1)
2516 - def GetNodeGroupInfoByName(self, nodegroup_name):
2517 """Get the L{objects.NodeGroup} object for a named node group. 2518 2519 @param nodegroup_name: name of the node group to get information for 2520 @type nodegroup_name: string 2521 @return: the corresponding L{objects.NodeGroup} instance or None if no 2522 information is available 2523 2524 """ 2525 for nodegroup in self._UnlockedGetAllNodeGroupsInfo().values(): 2526 if nodegroup.name == nodegroup_name: 2527 return nodegroup 2528 return None
2529
2530 - def _UnlockedGetNodeName(self, node_spec):
2531 if isinstance(node_spec, objects.Node): 2532 return node_spec.name 2533 elif isinstance(node_spec, basestring): 2534 node_info = self._UnlockedGetNodeInfo(node_spec) 2535 if node_info is None: 2536 raise errors.OpExecError("Unknown node: %s" % node_spec) 2537 return node_info.name 2538 else: 2539 raise errors.ProgrammerError("Can't handle node spec '%s'" % node_spec)
2540 2541 @ConfigSync(shared=1)
2542 - def GetNodeName(self, node_spec):
2543 """Gets the node name for the passed node. 2544 2545 @param node_spec: node to get names for 2546 @type node_spec: either node UUID or a L{objects.Node} object 2547 @rtype: string 2548 @return: node name 2549 2550 """ 2551 return self._UnlockedGetNodeName(node_spec)
2552
2553 - def _UnlockedGetNodeNames(self, node_specs):
2554 return [self._UnlockedGetNodeName(node_spec) for node_spec in node_specs]
2555 2556 @ConfigSync(shared=1)
2557 - def GetNodeNames(self, node_specs):
2558 """Gets the node names for the passed list of nodes. 2559 2560 @param node_specs: list of nodes to get names for 2561 @type node_specs: list of either node UUIDs or L{objects.Node} objects 2562 @rtype: list of strings 2563 @return: list of node names 2564 2565 """ 2566 return self._UnlockedGetNodeNames(node_specs)
2567 2568 @ConfigSync(shared=1)
2569 - def GetNodeGroupsFromNodes(self, node_uuids):
2570 """Returns groups for a list of nodes. 2571 2572 @type node_uuids: list of string 2573 @param node_uuids: List of node UUIDs 2574 @rtype: frozenset 2575 2576 """ 2577 return frozenset(self._UnlockedGetNodeInfo(uuid).group 2578 for uuid in node_uuids)
2579
2580 - def _UnlockedGetMasterCandidateUuids(self):
2581 """Get the list of UUIDs of master candidates. 2582 2583 @rtype: list of strings 2584 @return: list of UUIDs of all master candidates. 2585 2586 """ 2587 return [node.uuid for node in self._ConfigData().nodes.values() 2588 if node.master_candidate]
2589 2590 @ConfigSync(shared=1)
2591 - def GetMasterCandidateUuids(self):
2592 """Get the list of UUIDs of master candidates. 2593 2594 @rtype: list of strings 2595 @return: list of UUIDs of all master candidates. 2596 2597 """ 2598 return self._UnlockedGetMasterCandidateUuids()
2599
2600 - def _UnlockedGetMasterCandidateStats(self, exceptions=None):
2601 """Get the number of current and maximum desired and possible candidates. 2602 2603 @type exceptions: list 2604 @param exceptions: if passed, list of nodes that should be ignored 2605 @rtype: tuple 2606 @return: tuple of (current, desired and possible, possible) 2607 2608 """ 2609 mc_now = mc_should = mc_max = 0 2610 for node in self._ConfigData().nodes.values(): 2611 if exceptions and node.uuid in exceptions: 2612 continue 2613 if not (node.offline or node.drained) and node.master_capable: 2614 mc_max += 1 2615 if node.master_candidate: 2616 mc_now += 1 2617 mc_should = min(mc_max, self._ConfigData().cluster.candidate_pool_size) 2618 return (mc_now, mc_should, mc_max)
2619 2620 @ConfigSync(shared=1)
2621 - def GetMasterCandidateStats(self, exceptions=None):
2622 """Get the number of current and maximum possible candidates. 2623 2624 This is just a wrapper over L{_UnlockedGetMasterCandidateStats}. 2625 2626 @type exceptions: list 2627 @param exceptions: if passed, list of nodes that should be ignored 2628 @rtype: tuple 2629 @return: tuple of (current, max) 2630 2631 """ 2632 return self._UnlockedGetMasterCandidateStats(exceptions)
2633 2634 @ConfigSync()
2635 - def MaintainCandidatePool(self, exception_node_uuids):
2636 """Try to grow the candidate pool to the desired size. 2637 2638 @type exception_node_uuids: list 2639 @param exception_node_uuids: if passed, list of nodes that should be ignored 2640 @rtype: list 2641 @return: list with the adjusted nodes (L{objects.Node} instances) 2642 2643 """ 2644 mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats( 2645 exception_node_uuids) 2646 mod_list = [] 2647 if mc_now < mc_max: 2648 node_list = self._ConfigData().nodes.keys() 2649 random.shuffle(node_list) 2650 for uuid in node_list: 2651 if mc_now >= mc_max: 2652 break 2653 node = self._ConfigData().nodes[uuid] 2654 if (node.master_candidate or node.offline or node.drained or 2655 node.uuid in exception_node_uuids or not node.master_capable): 2656 continue 2657 mod_list.append(node) 2658 node.master_candidate = True 2659 node.serial_no += 1 2660 mc_now += 1 2661 if mc_now != mc_max: 2662 # this should not happen 2663 logging.warning("Warning: MaintainCandidatePool didn't manage to" 2664 " fill the candidate pool (%d/%d)", mc_now, mc_max) 2665 if mod_list: 2666 self._ConfigData().cluster.serial_no += 1 2667 2668 return mod_list
2669
2670 - def _UnlockedAddNodeToGroup(self, node_uuid, nodegroup_uuid):
2671 """Add a given node to the specified group. 2672 2673 """ 2674 if nodegroup_uuid not in self._ConfigData().nodegroups: 2675 # This can happen if a node group gets deleted between its lookup and 2676 # when we're adding the first node to it, since we don't keep a lock in 2677 # the meantime. It's ok though, as we'll fail cleanly if the node group 2678 # is not found anymore. 2679 raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid) 2680 if node_uuid not in self._ConfigData().nodegroups[nodegroup_uuid].members: 2681 self._ConfigData().nodegroups[nodegroup_uuid].members.append(node_uuid)
2682
2683 - def _UnlockedRemoveNodeFromGroup(self, node):
2684 """Remove a given node from its group. 2685 2686 """ 2687 nodegroup = node.group 2688 if nodegroup not in self._ConfigData().nodegroups: 2689 logging.warning("Warning: node '%s' has unknown node group '%s'" 2690 " (while being removed from it)", node.uuid, nodegroup) 2691 nodegroup_obj = self._ConfigData().nodegroups[nodegroup] 2692 if node.uuid not in nodegroup_obj.members: 2693 logging.warning("Warning: node '%s' not a member of its node group '%s'" 2694 " (while being removed from it)", node.uuid, nodegroup) 2695 else: 2696 nodegroup_obj.members.remove(node.uuid)
2697 2698 @ConfigSync()
2699 - def AssignGroupNodes(self, mods):
2700 """Changes the group of a number of nodes. 2701 2702 @type mods: list of tuples; (node name, new group UUID) 2703 @param mods: Node membership modifications 2704 2705 """ 2706 groups = self._ConfigData().nodegroups 2707 nodes = self._ConfigData().nodes 2708 2709 resmod = [] 2710 2711 # Try to resolve UUIDs first 2712 for (node_uuid, new_group_uuid) in mods: 2713 try: 2714 node = nodes[node_uuid] 2715 except KeyError: 2716 raise errors.ConfigurationError("Unable to find node '%s'" % node_uuid) 2717 2718 if node.group == new_group_uuid: 2719 # Node is being assigned to its current group 2720 logging.debug("Node '%s' was assigned to its current group (%s)", 2721 node_uuid, node.group) 2722 continue 2723 2724 # Try to find current group of node 2725 try: 2726 old_group = groups[node.group] 2727 except KeyError: 2728 raise errors.ConfigurationError("Unable to find old group '%s'" % 2729 node.group) 2730 2731 # Try to find new group for node 2732 try: 2733 new_group = groups[new_group_uuid] 2734 except KeyError: 2735 raise errors.ConfigurationError("Unable to find new group '%s'" % 2736 new_group_uuid) 2737 2738 assert node.uuid in old_group.members, \ 2739 ("Inconsistent configuration: node '%s' not listed in members for its" 2740 " old group '%s'" % (node.uuid, old_group.uuid)) 2741 assert node.uuid not in new_group.members, \ 2742 ("Inconsistent configuration: node '%s' already listed in members for" 2743 " its new group '%s'" % (node.uuid, new_group.uuid)) 2744 2745 resmod.append((node, old_group, new_group)) 2746 2747 # Apply changes 2748 for (node, old_group, new_group) in resmod: 2749 assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \ 2750 "Assigning to current group is not possible" 2751 2752 node.group = new_group.uuid 2753 2754 # Update members of involved groups 2755 if node.uuid in old_group.members: 2756 old_group.members.remove(node.uuid) 2757 if node.uuid not in new_group.members: 2758 new_group.members.append(node.uuid) 2759 2760 # Update timestamps and serials (only once per node/group object) 2761 now = time.time() 2762 for obj in frozenset(itertools.chain(*resmod)): # pylint: disable=W0142 2763 obj.serial_no += 1 2764 obj.mtime = now 2765 2766 # Force ssconf update 2767 self._ConfigData().cluster.serial_no += 1
2768
2769 - def _BumpSerialNo(self):
2770 """Bump up the serial number of the config. 2771 2772 """ 2773 self._ConfigData().serial_no += 1 2774 self._ConfigData().mtime = time.time()
2775
2776 - def _AllUUIDObjects(self):
2777 """Returns all objects with uuid attributes. 2778 2779 """ 2780 return (self._ConfigData().instances.values() + 2781 self._ConfigData().nodes.values() + 2782 self._ConfigData().nodegroups.values() + 2783 self._ConfigData().networks.values() + 2784 self._ConfigData().disks.values() + 2785 self._AllNICs() + 2786 [self._ConfigData().cluster])
2787
2788 - def GetConfigManager(self, shared=False, forcelock=False):
2789 """Returns a ConfigManager, which is suitable to perform a synchronized 2790 block of configuration operations. 2791 2792 WARNING: This blocks all other configuration operations, so anything that 2793 runs inside the block should be very fast, preferably not using any IO. 2794 """ 2795 2796 return ConfigManager(self, shared=shared, forcelock=forcelock)
2797
2798 - def _AddLockCount(self, count):
2799 self._lock_count += count 2800 return self._lock_count
2801
2802 - def _LockCount(self):
2803 return self._lock_count
2804
2805 - def _OpenConfig(self, shared, force=False):
2806 """Read the config data from WConfd or disk. 2807 2808 """ 2809 if self._AddLockCount(1) > 1: 2810 if self._lock_current_shared and not shared: 2811 self._AddLockCount(-1) 2812 raise errors.ConfigurationError("Can't request an exclusive" 2813 " configuration lock while holding" 2814 " shared") 2815 elif not force or self._lock_forced or not shared or self._offline: 2816 return # we already have the lock, do nothing 2817 else: 2818 self._lock_current_shared = shared 2819 if force: 2820 self._lock_forced = True 2821 # Read the configuration data. If offline, read the file directly. 2822 # If online, call WConfd. 2823 if self._offline: 2824 try: 2825 raw_data = utils.ReadFile(self._cfg_file) 2826 data_dict = serializer.Load(raw_data) 2827 # Make sure the configuration has the right version 2828 ValidateConfig(data_dict) 2829 data = objects.ConfigData.FromDict(data_dict) 2830 except errors.ConfigVersionMismatch: 2831 raise 2832 except Exception, err: 2833 raise errors.ConfigurationError(err) 2834 2835 self._cfg_id = utils.GetFileID(path=self._cfg_file) 2836 2837 if (not hasattr(data, "cluster") or 2838 not hasattr(data.cluster, "rsahostkeypub")): 2839 raise errors.ConfigurationError("Incomplete configuration" 2840 " (missing cluster.rsahostkeypub)") 2841 2842 if not data.cluster.master_node in data.nodes: 2843 msg = ("The configuration denotes node %s as master, but does not" 2844 " contain information about this node" % 2845 data.cluster.master_node) 2846 raise errors.ConfigurationError(msg) 2847 2848 master_info = data.nodes[data.cluster.master_node] 2849 if master_info.name != self._my_hostname and not self._accept_foreign: 2850 msg = ("The configuration denotes node %s as master, while my" 2851 " hostname is %s; opening a foreign configuration is only" 2852 " possible in accept_foreign mode" % 2853 (master_info.name, self._my_hostname)) 2854 raise errors.ConfigurationError(msg) 2855 2856 self._SetConfigData(data) 2857 2858 # Upgrade configuration if needed 2859 self._UpgradeConfig(saveafter=True) 2860 else: 2861 if shared and not force: 2862 if self._config_data is None: 2863 logging.debug("Requesting config, as I have no up-to-date copy") 2864 dict_data = self._wconfd.ReadConfig() 2865 logging.debug("Configuration received") 2866 else: 2867 dict_data = None 2868 else: 2869 # poll until we acquire the lock 2870 while True: 2871 logging.debug("Receiving config from WConfd.LockConfig [shared=%s]", 2872 bool(shared)) 2873 dict_data = \ 2874 self._wconfd.LockConfig(self._GetWConfdContext(), bool(shared)) 2875 if dict_data is not None: 2876 logging.debug("Received config from WConfd.LockConfig") 2877 break 2878 time.sleep(random.random()) 2879 2880 try: 2881 if dict_data is not None: 2882 self._SetConfigData(objects.ConfigData.FromDict(dict_data)) 2883 self._UpgradeConfig() 2884 except Exception, err: 2885 raise errors.ConfigurationError(err)
2886
2887 - def _CloseConfig(self, save):
2888 """Release resources relating the config data. 2889 2890 """ 2891 if self._AddLockCount(-1) > 0: 2892 return # we still have the lock, do nothing 2893 if save: 2894 try: 2895 logging.debug("Writing configuration and unlocking it") 2896 self._WriteConfig(releaselock=True) 2897 logging.debug("Configuration write, unlock finished") 2898 except Exception, err: 2899 logging.critical("Can't write the configuration: %s", str(err)) 2900 raise 2901 elif not self._offline and \ 2902 not (self._lock_current_shared and not self._lock_forced): 2903 logging.debug("Unlocking configuration without writing") 2904 self._wconfd.UnlockConfig(self._GetWConfdContext()) 2905 self._lock_forced = False
2906 2907 # TODO: To WConfd
2908 - def _UpgradeConfig(self, saveafter=False):
2909 """Run any upgrade steps. 2910 2911 This method performs both in-object upgrades and also update some data 2912 elements that need uniqueness across the whole configuration or interact 2913 with other objects. 2914 2915 @warning: if 'saveafter' is 'True', this function will call 2916 L{_WriteConfig()} so it needs to be called only from a 2917 "safe" place. 2918 2919 """ 2920 # Keep a copy of the persistent part of _config_data to check for changes 2921 # Serialization doesn't guarantee order in dictionaries 2922 if saveafter: 2923 oldconf = copy.deepcopy(self._ConfigData().ToDict()) 2924 else: 2925 oldconf = None 2926 2927 # In-object upgrades 2928 self._ConfigData().UpgradeConfig() 2929 2930 for item in self._AllUUIDObjects(): 2931 if item.uuid is None: 2932 item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID) 2933 if not self._ConfigData().nodegroups: 2934 default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME 2935 default_nodegroup = objects.NodeGroup(name=default_nodegroup_name, 2936 members=[]) 2937 self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True) 2938 for node in self._ConfigData().nodes.values(): 2939 if not node.group: 2940 node.group = self._UnlockedLookupNodeGroup(None) 2941 # This is technically *not* an upgrade, but needs to be done both when 2942 # nodegroups are being added, and upon normally loading the config, 2943 # because the members list of a node group is discarded upon 2944 # serializing/deserializing the object. 2945 self._UnlockedAddNodeToGroup(node.uuid, node.group) 2946 2947 if saveafter: 2948 modified = (oldconf != self._ConfigData().ToDict()) 2949 else: 2950 modified = True # can't prove it didn't change, but doesn't matter 2951 if modified and saveafter: 2952 self._WriteConfig() 2953 self._UnlockedDropECReservations(_UPGRADE_CONFIG_JID) 2954 else: 2955 if self._offline: 2956 self._UnlockedVerifyConfigAndLog()
2957
2958 - def _WriteConfig(self, destination=None, releaselock=False):
2959 """Write the configuration data to persistent storage. 2960 2961 """ 2962 if destination is None: 2963 destination = self._cfg_file 2964 2965 # Save the configuration data. If offline, write the file directly. 2966 # If online, call WConfd. 2967 if self._offline: 2968 self._BumpSerialNo() 2969 txt = serializer.DumpJson( 2970 self._ConfigData().ToDict(_with_private=True), 2971 private_encoder=serializer.EncodeWithPrivateFields 2972 ) 2973 2974 getents = self._getents() 2975 try: 2976 fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt, 2977 close=False, gid=getents.confd_gid, mode=0640) 2978 except errors.LockError: 2979 raise errors.ConfigurationError("The configuration file has been" 2980 " modified since the last write, cannot" 2981 " update") 2982 try: 2983 self._cfg_id = utils.GetFileID(fd=fd) 2984 finally: 2985 os.close(fd) 2986 else: 2987 try: 2988 if releaselock: 2989 res = self._wconfd.WriteConfigAndUnlock(self._GetWConfdContext(), 2990 self._ConfigData().ToDict()) 2991 if not res: 2992 logging.warning("WriteConfigAndUnlock indicates we already have" 2993 " released the lock; assuming this was just a retry" 2994 " and the initial call succeeded") 2995 else: 2996 self._wconfd.WriteConfig(self._GetWConfdContext(), 2997 self._ConfigData().ToDict()) 2998 except errors.LockError: 2999 raise errors.ConfigurationError("The configuration file has been" 3000 " modified since the last write, cannot" 3001 " update") 3002 3003 self.write_count += 1
3004
3005 - def _GetAllHvparamsStrings(self, hypervisors):
3006 """Get the hvparams of all given hypervisors from the config. 3007 3008 @type hypervisors: list of string 3009 @param hypervisors: list of hypervisor names 3010 @rtype: dict of strings 3011 @returns: dictionary mapping the hypervisor name to a string representation 3012 of the hypervisor's hvparams 3013 3014 """ 3015 hvparams = {} 3016 for hv in hypervisors: 3017 hvparams[hv] = self._UnlockedGetHvparamsString(hv) 3018 return hvparams
3019 3020 @staticmethod
3021 - def _ExtendByAllHvparamsStrings(ssconf_values, all_hvparams):
3022 """Extends the ssconf_values dictionary by hvparams. 3023 3024 @type ssconf_values: dict of strings 3025 @param ssconf_values: dictionary mapping ssconf_keys to strings 3026 representing the content of ssconf files 3027 @type all_hvparams: dict of strings 3028 @param all_hvparams: dictionary mapping hypervisor names to a string 3029 representation of their hvparams 3030 @rtype: same as ssconf_values 3031 @returns: the ssconf_values dictionary extended by hvparams 3032 3033 """ 3034 for hv in all_hvparams: 3035 ssconf_key = constants.SS_HVPARAMS_PREF + hv 3036 ssconf_values[ssconf_key] = all_hvparams[hv] 3037 return ssconf_values
3038
3039 - def _UnlockedGetSshPortMap(self, node_infos):
3040 node_ports = dict([(node.name, 3041 self._UnlockedGetNdParams(node).get( 3042 constants.ND_SSH_PORT)) 3043 for node in node_infos]) 3044 return node_ports
3045
3046 - def _UnlockedGetSsconfValues(self):
3047 """Return the values needed by ssconf. 3048 3049 @rtype: dict 3050 @return: a dictionary with keys the ssconf names and values their 3051 associated value 3052 3053 """ 3054 fn = "\n".join 3055 instance_names = utils.NiceSort( 3056 [inst.name for inst in 3057 self._UnlockedGetAllInstancesInfo().values()]) 3058 node_infos = self._UnlockedGetAllNodesInfo().values() 3059 node_names = [node.name for node in node_infos] 3060 node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip) 3061 for ninfo in node_infos] 3062 node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip) 3063 for ninfo in node_infos] 3064 node_vm_capable = ["%s=%s" % (ninfo.name, str(ninfo.vm_capable)) 3065 for ninfo in node_infos] 3066 3067 instance_data = fn(instance_names) 3068 off_data = fn(node.name for node in node_infos if node.offline) 3069 on_data = fn(node.name for node in node_infos if not node.offline) 3070 mc_data = fn(node.name for node in node_infos if node.master_candidate) 3071 mc_ips_data = fn(node.primary_ip for node in node_infos 3072 if node.master_candidate) 3073 node_data = fn(node_names) 3074 node_pri_ips_data = fn(node_pri_ips) 3075 node_snd_ips_data = fn(node_snd_ips) 3076 node_vm_capable_data = fn(node_vm_capable) 3077 3078 cluster = self._ConfigData().cluster 3079 cluster_tags = fn(cluster.GetTags()) 3080 3081 master_candidates_certs = fn("%s=%s" % (mc_uuid, mc_cert) 3082 for mc_uuid, mc_cert 3083 in cluster.candidate_certs.items()) 3084 3085 hypervisor_list = fn(cluster.enabled_hypervisors) 3086 all_hvparams = self._GetAllHvparamsStrings(constants.HYPER_TYPES) 3087 3088 uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n") 3089 3090 nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in 3091 self._ConfigData().nodegroups.values()] 3092 nodegroups_data = fn(utils.NiceSort(nodegroups)) 3093 networks = ["%s %s" % (net.uuid, net.name) for net in 3094 self._ConfigData().networks.values()] 3095 networks_data = fn(utils.NiceSort(networks)) 3096 3097 ssh_ports = fn("%s=%s" % (node_name, port) 3098 for node_name, port 3099 in self._UnlockedGetSshPortMap(node_infos).items()) 3100 3101 ssconf_values = { 3102 constants.SS_CLUSTER_NAME: cluster.cluster_name, 3103 constants.SS_CLUSTER_TAGS: cluster_tags, 3104 constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir, 3105 constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir, 3106 constants.SS_GLUSTER_STORAGE_DIR: cluster.gluster_storage_dir, 3107 constants.SS_MASTER_CANDIDATES: mc_data, 3108 constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data, 3109 constants.SS_MASTER_CANDIDATES_CERTS: master_candidates_certs, 3110 constants.SS_MASTER_IP: cluster.master_ip, 3111 constants.SS_MASTER_NETDEV: cluster.master_netdev, 3112 constants.SS_MASTER_NETMASK: str(cluster.master_netmask), 3113 constants.SS_MASTER_NODE: self._UnlockedGetNodeName(cluster.master_node), 3114 constants.SS_NODE_LIST: node_data, 3115 constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data, 3116 constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data, 3117 constants.SS_NODE_VM_CAPABLE: node_vm_capable_data, 3118 constants.SS_OFFLINE_NODES: off_data, 3119 constants.SS_ONLINE_NODES: on_data, 3120 constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family), 3121 constants.SS_INSTANCE_LIST: instance_data, 3122 constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION, 3123 constants.SS_HYPERVISOR_LIST: hypervisor_list, 3124 constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health), 3125 constants.SS_UID_POOL: uid_pool, 3126 constants.SS_NODEGROUPS: nodegroups_data, 3127 constants.SS_NETWORKS: networks_data, 3128 constants.SS_ENABLED_USER_SHUTDOWN: str(cluster.enabled_user_shutdown), 3129 constants.SS_SSH_PORTS: ssh_ports, 3130 } 3131 ssconf_values = self._ExtendByAllHvparamsStrings(ssconf_values, 3132 all_hvparams) 3133 bad_values = [(k, v) for k, v in ssconf_values.items() 3134 if not isinstance(v, (str, basestring))] 3135 if bad_values: 3136 err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values) 3137 raise errors.ConfigurationError("Some ssconf key(s) have non-string" 3138 " values: %s" % err) 3139 return ssconf_values
3140 3141 @ConfigSync(shared=1)
3142 - def GetSsconfValues(self):
3143 """Wrapper using lock around _UnlockedGetSsconf(). 3144 3145 """ 3146 return self._UnlockedGetSsconfValues()
3147 3148 @ConfigSync(shared=1)
3149 - def GetVGName(self):
3150 """Return the volume group name. 3151 3152 """ 3153 return self._ConfigData().cluster.volume_group_name
3154 3155 @ConfigSync()
3156 - def SetVGName(self, vg_name):
3157 """Set the volume group name. 3158 3159 """ 3160 self._ConfigData().cluster.volume_group_name = vg_name 3161 self._ConfigData().cluster.serial_no += 1
3162 3163 @ConfigSync(shared=1)
3164 - def GetDRBDHelper(self):
3165 """Return DRBD usermode helper. 3166 3167 """ 3168 return self._ConfigData().cluster.drbd_usermode_helper
3169 3170 @ConfigSync()
3171 - def SetDRBDHelper(self, drbd_helper):
3172 """Set DRBD usermode helper. 3173 3174 """ 3175 self._ConfigData().cluster.drbd_usermode_helper = drbd_helper 3176 self._ConfigData().cluster.serial_no += 1
3177 3178 @ConfigSync(shared=1)
3179 - def GetMACPrefix(self):
3180 """Return the mac prefix. 3181 3182 """ 3183 return self._ConfigData().cluster.mac_prefix
3184 3185 @ConfigSync(shared=1)
3186 - def GetClusterInfo(self):
3187 """Returns information about the cluster 3188 3189 @rtype: L{objects.Cluster} 3190 @return: the cluster object 3191 3192 """ 3193 return self._ConfigData().cluster
3194 3195 @ConfigSync(shared=1)
3196 - def DisksOfType(self, dev_type):
3197 """Check if in there is at disk of the given type in the configuration. 3198 3199 """ 3200 return self._ConfigData().DisksOfType(dev_type)
3201 3202 @ConfigSync(shared=1)
3203 - def GetDetachedConfig(self):
3204 """Returns a detached version of a ConfigManager, which represents 3205 a read-only snapshot of the configuration at this particular time. 3206 3207 """ 3208 return DetachedConfig(self._ConfigData())
3209 3210 @ConfigSync()
3211 - def Update(self, target, feedback_fn, ec_id=None):
3212 """Notify function to be called after updates. 3213 3214 This function must be called when an object (as returned by 3215 GetInstanceInfo, GetNodeInfo, GetCluster) has been updated and the 3216 caller wants the modifications saved to the backing store. Note 3217 that all modified objects will be saved, but the target argument 3218 is the one the caller wants to ensure that it's saved. 3219 3220 @param target: an instance of either L{objects.Cluster}, 3221 L{objects.Node} or L{objects.Instance} which is existing in 3222 the cluster 3223 @param feedback_fn: Callable feedback function 3224 3225 """ 3226 if self._ConfigData() is None: 3227 raise errors.ProgrammerError("Configuration file not read," 3228 " cannot save.") 3229 3230 def check_serial(target, current): 3231 if current is None: 3232 raise errors.ConfigurationError("Configuration object unknown") 3233 elif current.serial_no != target.serial_no: 3234 raise errors.ConfigurationError("Configuration object updated since" 3235 " it has been read: %d != %d", 3236 current.serial_no, target.serial_no)
3237 3238 def replace_in(target, tdict): 3239 check_serial(target, tdict.get(target.uuid)) 3240 tdict[target.uuid] = target 3241 3242 update_serial = False 3243 if isinstance(target, objects.Cluster): 3244 check_serial(target, self._ConfigData().cluster) 3245 self._ConfigData().cluster = target 3246 elif isinstance(target, objects.Node): 3247 replace_in(target, self._ConfigData().nodes) 3248 update_serial = True 3249 elif isinstance(target, objects.Instance): 3250 replace_in(target, self._ConfigData().instances) 3251 elif isinstance(target, objects.NodeGroup): 3252 replace_in(target, self._ConfigData().nodegroups) 3253 elif isinstance(target, objects.Network): 3254 replace_in(target, self._ConfigData().networks) 3255 elif isinstance(target, objects.Disk): 3256 replace_in(target, self._ConfigData().disks) 3257 else: 3258 raise errors.ProgrammerError("Invalid object type (%s) passed to" 3259 " ConfigWriter.Update" % type(target)) 3260 target.serial_no += 1 3261 target.mtime = now = time.time() 3262 3263 if update_serial: 3264 # for node updates, we need to increase the cluster serial too 3265 self._ConfigData().cluster.serial_no += 1 3266 self._ConfigData().cluster.mtime = now 3267 3268 if isinstance(target, objects.Disk): 3269 self._UnlockedReleaseDRBDMinors(target.uuid) 3270 3271 if ec_id is not None: 3272 # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams 3273 # FIXME: After RemoveInstance is moved to WConfd, use its internal 3274 # functions from TempRes module. 3275 self._UnlockedCommitTemporaryIps(ec_id) 3276 3277 # Just verify the configuration with our feedback function. 3278 # It will get written automatically by the decorator. 3279 self._UnlockedVerifyConfigAndLog(feedback_fn=feedback_fn) 3280
3281 - def _UnlockedDropECReservations(self, _ec_id):
3282 """Drop per-execution-context reservations 3283 3284 """ 3285 # FIXME: Remove the following two lines after all reservations are moved to 3286 # wconfd. 3287 for rm in self._all_rms: 3288 rm.DropECReservations(_ec_id) 3289 if not self._offline: 3290 self._wconfd.DropAllReservations(self._GetWConfdContext())
3291
3292 - def DropECReservations(self, ec_id):
3293 self._UnlockedDropECReservations(ec_id)
3294 3295 @ConfigSync(shared=1)
3296 - def GetAllNetworksInfo(self):
3297 """Get configuration info of all the networks. 3298 3299 """ 3300 return dict(self._ConfigData().networks)
3301
3302 - def _UnlockedGetNetworkList<