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