Package ganeti :: Module opcodes
[hide private]
[frames] | no frames]

Source Code for Module ganeti.opcodes

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Google Inc. 
   5  # 
   6  # This program is free software; you can redistribute it and/or modify 
   7  # it under the terms of the GNU General Public License as published by 
   8  # the Free Software Foundation; either version 2 of the License, or 
   9  # (at your option) any later version. 
  10  # 
  11  # This program is distributed in the hope that it will be useful, but 
  12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
  13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  14  # General Public License for more details. 
  15  # 
  16  # You should have received a copy of the GNU General Public License 
  17  # along with this program; if not, write to the Free Software 
  18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
  19  # 02110-1301, USA. 
  20   
  21   
  22  """OpCodes module 
  23   
  24  This module implements the data structures which define the cluster 
  25  operations - the so-called opcodes. 
  26   
  27  Every operation which modifies the cluster state is expressed via 
  28  opcodes. 
  29   
  30  """ 
  31   
  32  # this are practically structures, so disable the message about too 
  33  # few public methods: 
  34  # pylint: disable-msg=R0903 
  35   
  36  import logging 
  37  import re 
  38   
  39  from ganeti import constants 
  40  from ganeti import errors 
  41  from ganeti import ht 
  42   
  43   
  44  # Common opcode attributes 
  45   
  46  #: output fields for a query operation 
  47  _POutputFields = ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)) 
  48   
  49  #: the shutdown timeout 
  50  _PShutdownTimeout = ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, 
  51                       ht.TPositiveInt) 
  52   
  53  #: the force parameter 
  54  _PForce = ("force", False, ht.TBool) 
  55   
  56  #: a required instance name (for single-instance LUs) 
  57  _PInstanceName = ("instance_name", ht.NoDefault, ht.TNonEmptyString) 
  58   
  59  #: Whether to ignore offline nodes 
  60  _PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool) 
  61   
  62  #: a required node name (for single-node LUs) 
  63  _PNodeName = ("node_name", ht.NoDefault, ht.TNonEmptyString) 
  64   
  65  #: a required node group name (for single-group LUs) 
  66  _PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString) 
  67   
  68  #: Migration type (live/non-live) 
  69  _PMigrationMode = ("mode", None, 
  70                     ht.TOr(ht.TNone, ht.TElemOf(constants.HT_MIGRATION_MODES))) 
  71   
  72  #: Obsolete 'live' migration mode (boolean) 
  73  _PMigrationLive = ("live", None, ht.TMaybeBool) 
  74   
  75  #: Tag type 
  76  _PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES)) 
  77   
  78  #: List of tag strings 
  79  _PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)) 
  80   
  81  #: Ignore consistency 
  82  _PIgnoreConsistency = ("ignore_consistency", False, ht.TBool) 
  83   
  84  #: Do not remember instance state changes 
  85  _PNoRemember = ("no_remember", False, ht.TBool) 
  86   
  87  #: OP_ID conversion regular expression 
  88  _OPID_RE = re.compile("([a-z])([A-Z])") 
89 90 91 -def _NameToId(name):
92 """Convert an opcode class name to an OP_ID. 93 94 @type name: string 95 @param name: the class name, as OpXxxYyy 96 @rtype: string 97 @return: the name in the OP_XXXX_YYYY format 98 99 """ 100 if not name.startswith("Op"): 101 return None 102 # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't 103 # consume any input, and hence we would just have all the elements 104 # in the list, one by one; but it seems that split doesn't work on 105 # non-consuming input, hence we have to process the input string a 106 # bit 107 name = _OPID_RE.sub(r"\1,\2", name) 108 elems = name.split(",") 109 return "_".join(n.upper() for n in elems)
110
111 112 -def RequireFileStorage():
113 """Checks that file storage is enabled. 114 115 While it doesn't really fit into this module, L{utils} was deemed too large 116 of a dependency to be imported for just one or two functions. 117 118 @raise errors.OpPrereqError: when file storage is disabled 119 120 """ 121 if not constants.ENABLE_FILE_STORAGE: 122 raise errors.OpPrereqError("File storage disabled at configure time", 123 errors.ECODE_INVAL)
124
125 126 -def _CheckDiskTemplate(template):
127 """Ensure a given disk template is valid. 128 129 """ 130 if template not in constants.DISK_TEMPLATES: 131 # Using str.join directly to avoid importing utils for CommaJoin 132 msg = ("Invalid disk template name '%s', valid templates are: %s" % 133 (template, ", ".join(constants.DISK_TEMPLATES))) 134 raise errors.OpPrereqError(msg, errors.ECODE_INVAL) 135 if template == constants.DT_FILE: 136 RequireFileStorage() 137 return True
138
139 140 -def _CheckStorageType(storage_type):
141 """Ensure a given storage type is valid. 142 143 """ 144 if storage_type not in constants.VALID_STORAGE_TYPES: 145 raise errors.OpPrereqError("Unknown storage type: %s" % storage_type, 146 errors.ECODE_INVAL) 147 if storage_type == constants.ST_FILE: 148 RequireFileStorage() 149 return True
150 151 152 #: Storage type parameter 153 _PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType)
154 155 156 -class _AutoOpParamSlots(type):
157 """Meta class for opcode definitions. 158 159 """
160 - def __new__(mcs, name, bases, attrs):
161 """Called when a class should be created. 162 163 @param mcs: The meta class 164 @param name: Name of created class 165 @param bases: Base classes 166 @type attrs: dict 167 @param attrs: Class attributes 168 169 """ 170 assert "__slots__" not in attrs, \ 171 "Class '%s' defines __slots__ when it should use OP_PARAMS" % name 172 assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name 173 174 attrs["OP_ID"] = _NameToId(name) 175 176 # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams 177 params = attrs.setdefault("OP_PARAMS", []) 178 179 # Use parameter names as slots 180 slots = [pname for (pname, _, _) in params] 181 182 assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ 183 "Class '%s' uses unknown field in OP_DSC_FIELD" % name 184 185 attrs["__slots__"] = slots 186 187 return type.__new__(mcs, name, bases, attrs)
188
189 190 -class BaseOpCode(object):
191 """A simple serializable object. 192 193 This object serves as a parent class for OpCode without any custom 194 field handling. 195 196 """ 197 # pylint: disable-msg=E1101 198 # as OP_ID is dynamically defined 199 __metaclass__ = _AutoOpParamSlots 200
201 - def __init__(self, **kwargs):
202 """Constructor for BaseOpCode. 203 204 The constructor takes only keyword arguments and will set 205 attributes on this object based on the passed arguments. As such, 206 it means that you should not pass arguments which are not in the 207 __slots__ attribute for this class. 208 209 """ 210 slots = self._all_slots() 211 for key in kwargs: 212 if key not in slots: 213 raise TypeError("Object %s doesn't support the parameter '%s'" % 214 (self.__class__.__name__, key)) 215 setattr(self, key, kwargs[key])
216
217 - def __getstate__(self):
218 """Generic serializer. 219 220 This method just returns the contents of the instance as a 221 dictionary. 222 223 @rtype: C{dict} 224 @return: the instance attributes and their values 225 226 """ 227 state = {} 228 for name in self._all_slots(): 229 if hasattr(self, name): 230 state[name] = getattr(self, name) 231 return state
232
233 - def __setstate__(self, state):
234 """Generic unserializer. 235 236 This method just restores from the serialized state the attributes 237 of the current instance. 238 239 @param state: the serialized opcode data 240 @type state: C{dict} 241 242 """ 243 if not isinstance(state, dict): 244 raise ValueError("Invalid data to __setstate__: expected dict, got %s" % 245 type(state)) 246 247 for name in self._all_slots(): 248 if name not in state and hasattr(self, name): 249 delattr(self, name) 250 251 for name in state: 252 setattr(self, name, state[name])
253 254 @classmethod
255 - def _all_slots(cls):
256 """Compute the list of all declared slots for a class. 257 258 """ 259 slots = [] 260 for parent in cls.__mro__: 261 slots.extend(getattr(parent, "__slots__", [])) 262 return slots
263 264 @classmethod
265 - def GetAllParams(cls):
266 """Compute list of all parameters for an opcode. 267 268 """ 269 slots = [] 270 for parent in cls.__mro__: 271 slots.extend(getattr(parent, "OP_PARAMS", [])) 272 return slots
273
274 - def Validate(self, set_defaults):
275 """Validate opcode parameters, optionally setting default values. 276 277 @type set_defaults: bool 278 @param set_defaults: Whether to set default values 279 @raise errors.OpPrereqError: When a parameter value doesn't match 280 requirements 281 282 """ 283 for (attr_name, default, test) in self.GetAllParams(): 284 assert test == ht.NoType or callable(test) 285 286 if not hasattr(self, attr_name): 287 if default == ht.NoDefault: 288 raise errors.OpPrereqError("Required parameter '%s.%s' missing" % 289 (self.OP_ID, attr_name), 290 errors.ECODE_INVAL) 291 elif set_defaults: 292 if callable(default): 293 dval = default() 294 else: 295 dval = default 296 setattr(self, attr_name, dval) 297 298 if test == ht.NoType: 299 # no tests here 300 continue 301 302 if set_defaults or hasattr(self, attr_name): 303 attr_val = getattr(self, attr_name) 304 if not test(attr_val): 305 logging.error("OpCode %s, parameter %s, has invalid type %s/value %s", 306 self.OP_ID, attr_name, type(attr_val), attr_val) 307 raise errors.OpPrereqError("Parameter '%s.%s' fails validation" % 308 (self.OP_ID, attr_name), 309 errors.ECODE_INVAL)
310
311 312 -class OpCode(BaseOpCode):
313 """Abstract OpCode. 314 315 This is the root of the actual OpCode hierarchy. All clases derived 316 from this class should override OP_ID. 317 318 @cvar OP_ID: The ID of this opcode. This should be unique amongst all 319 children of this class. 320 @cvar OP_DSC_FIELD: The name of a field whose value will be included in the 321 string returned by Summary(); see the docstring of that 322 method for details). 323 @cvar OP_PARAMS: List of opcode attributes, the default values they should 324 get if not already defined, and types they must match. 325 @cvar WITH_LU: Boolean that specifies whether this should be included in 326 mcpu's dispatch table 327 @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just 328 the check steps 329 @ivar priority: Opcode priority for queue 330 331 """ 332 # pylint: disable-msg=E1101 333 # as OP_ID is dynamically defined 334 WITH_LU = True 335 OP_PARAMS = [ 336 ("dry_run", None, ht.TMaybeBool), 337 ("debug_level", None, ht.TOr(ht.TNone, ht.TPositiveInt)), 338 ("priority", constants.OP_PRIO_DEFAULT, 339 ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID)), 340 ] 341
342 - def __getstate__(self):
343 """Specialized getstate for opcodes. 344 345 This method adds to the state dictionary the OP_ID of the class, 346 so that on unload we can identify the correct class for 347 instantiating the opcode. 348 349 @rtype: C{dict} 350 @return: the state as a dictionary 351 352 """ 353 data = BaseOpCode.__getstate__(self) 354 data["OP_ID"] = self.OP_ID 355 return data
356 357 @classmethod
358 - def LoadOpCode(cls, data):
359 """Generic load opcode method. 360 361 The method identifies the correct opcode class from the dict-form 362 by looking for a OP_ID key, if this is not found, or its value is 363 not available in this module as a child of this class, we fail. 364 365 @type data: C{dict} 366 @param data: the serialized opcode 367 368 """ 369 if not isinstance(data, dict): 370 raise ValueError("Invalid data to LoadOpCode (%s)" % type(data)) 371 if "OP_ID" not in data: 372 raise ValueError("Invalid data to LoadOpcode, missing OP_ID") 373 op_id = data["OP_ID"] 374 op_class = None 375 if op_id in OP_MAPPING: 376 op_class = OP_MAPPING[op_id] 377 else: 378 raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" % 379 op_id) 380 op = op_class() 381 new_data = data.copy() 382 del new_data["OP_ID"] 383 op.__setstate__(new_data) 384 return op
385
386 - def Summary(self):
387 """Generates a summary description of this opcode. 388 389 The summary is the value of the OP_ID attribute (without the "OP_" 390 prefix), plus the value of the OP_DSC_FIELD attribute, if one was 391 defined; this field should allow to easily identify the operation 392 (for an instance creation job, e.g., it would be the instance 393 name). 394 395 """ 396 assert self.OP_ID is not None and len(self.OP_ID) > 3 397 # all OP_ID start with OP_, we remove that 398 txt = self.OP_ID[3:] 399 field_name = getattr(self, "OP_DSC_FIELD", None) 400 if field_name: 401 field_value = getattr(self, field_name, None) 402 if isinstance(field_value, (list, tuple)): 403 field_value = ",".join(str(i) for i in field_value) 404 txt = "%s(%s)" % (txt, field_value) 405 return txt
406
407 408 # cluster opcodes 409 410 -class OpClusterPostInit(OpCode):
411 """Post cluster initialization. 412 413 This opcode does not touch the cluster at all. Its purpose is to run hooks 414 after the cluster has been initialized. 415 416 """
417
418 419 -class OpClusterDestroy(OpCode):
420 """Destroy the cluster. 421 422 This opcode has no other parameters. All the state is irreversibly 423 lost after the execution of this opcode. 424 425 """
426
427 428 -class OpClusterQuery(OpCode):
429 """Query cluster information."""
430
431 432 -class OpClusterVerify(OpCode):
433 """Verify the cluster state. 434 435 @type skip_checks: C{list} 436 @ivar skip_checks: steps to be skipped from the verify process; this 437 needs to be a subset of 438 L{constants.VERIFY_OPTIONAL_CHECKS}; currently 439 only L{constants.VERIFY_NPLUSONE_MEM} can be passed 440 441 """ 442 OP_PARAMS = [ 443 ("skip_checks", ht.EmptyList, 444 ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS))), 445 ("verbose", False, ht.TBool), 446 ("error_codes", False, ht.TBool), 447 ("debug_simulate_errors", False, ht.TBool), 448 ]
449
450 451 -class OpClusterVerifyDisks(OpCode):
452 """Verify the cluster disks. 453 454 Parameters: none 455 456 Result: a tuple of four elements: 457 - list of node names with bad data returned (unreachable, etc.) 458 - dict of node names with broken volume groups (values: error msg) 459 - list of instances with degraded disks (that should be activated) 460 - dict of instances with missing logical volumes (values: (node, vol) 461 pairs with details about the missing volumes) 462 463 In normal operation, all lists should be empty. A non-empty instance 464 list (3rd element of the result) is still ok (errors were fixed) but 465 non-empty node list means some node is down, and probably there are 466 unfixable drbd errors. 467 468 Note that only instances that are drbd-based are taken into 469 consideration. This might need to be revisited in the future. 470 471 """
472
473 474 -class OpClusterRepairDiskSizes(OpCode):
475 """Verify the disk sizes of the instances and fixes configuration 476 mimatches. 477 478 Parameters: optional instances list, in case we want to restrict the 479 checks to only a subset of the instances. 480 481 Result: a list of tuples, (instance, disk, new-size) for changed 482 configurations. 483 484 In normal operation, the list should be empty. 485 486 @type instances: list 487 @ivar instances: the list of instances to check, or empty for all instances 488 489 """ 490 OP_PARAMS = [ 491 ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 492 ]
493
494 495 -class OpClusterConfigQuery(OpCode):
496 """Query cluster configuration values.""" 497 OP_PARAMS = [ 498 _POutputFields 499 ]
500
501 502 -class OpClusterRename(OpCode):
503 """Rename the cluster. 504 505 @type name: C{str} 506 @ivar name: The new name of the cluster. The name and/or the master IP 507 address will be changed to match the new name and its IP 508 address. 509 510 """ 511 OP_DSC_FIELD = "name" 512 OP_PARAMS = [ 513 ("name", ht.NoDefault, ht.TNonEmptyString), 514 ]
515
516 517 -class OpClusterSetParams(OpCode):
518 """Change the parameters of the cluster. 519 520 @type vg_name: C{str} or C{None} 521 @ivar vg_name: The new volume group name or None to disable LVM usage. 522 523 """ 524 OP_PARAMS = [ 525 ("vg_name", None, ht.TMaybeString), 526 ("enabled_hypervisors", None, 527 ht.TOr(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), ht.TTrue), 528 ht.TNone)), 529 ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict), 530 ht.TNone)), 531 ("beparams", None, ht.TOr(ht.TDict, ht.TNone)), 532 ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict), 533 ht.TNone)), 534 ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict), 535 ht.TNone)), 536 ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone)), 537 ("uid_pool", None, ht.NoType), 538 ("add_uids", None, ht.NoType), 539 ("remove_uids", None, ht.NoType), 540 ("maintain_node_health", None, ht.TMaybeBool), 541 ("prealloc_wipe_disks", None, ht.TMaybeBool), 542 ("nicparams", None, ht.TMaybeDict), 543 ("ndparams", None, ht.TMaybeDict), 544 ("drbd_helper", None, ht.TOr(ht.TString, ht.TNone)), 545 ("default_iallocator", None, ht.TOr(ht.TString, ht.TNone)), 546 ("master_netdev", None, ht.TOr(ht.TString, ht.TNone)), 547 ("reserved_lvs", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone)), 548 ("hidden_os", None, ht.TOr(ht.TListOf( 549 ht.TAnd(ht.TList, 550 ht.TIsLength(2), 551 ht.TMap(lambda v: v[0], ht.TElemOf(constants.DDMS_VALUES)))), 552 ht.TNone)), 553 ("blacklisted_os", None, ht.TOr(ht.TListOf( 554 ht.TAnd(ht.TList, 555 ht.TIsLength(2), 556 ht.TMap(lambda v: v[0], ht.TElemOf(constants.DDMS_VALUES)))), 557 ht.TNone)), 558 ]
559
560 561 -class OpClusterRedistConf(OpCode):
562 """Force a full push of the cluster configuration. 563 564 """
565
566 567 -class OpQuery(OpCode):
568 """Query for resources/items. 569 570 @ivar what: Resources to query for, must be one of L{constants.QR_OP_QUERY} 571 @ivar fields: List of fields to retrieve 572 @ivar filter: Query filter 573 574 """ 575 OP_PARAMS = [ 576 ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY)), 577 ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)), 578 ("filter", None, ht.TOr(ht.TNone, 579 ht.TListOf(ht.TOr(ht.TNonEmptyString, ht.TList)))), 580 ]
581
582 583 -class OpQueryFields(OpCode):
584 """Query for available resource/item fields. 585 586 @ivar what: Resources to query for, must be one of L{constants.QR_OP_QUERY} 587 @ivar fields: List of fields to retrieve 588 589 """ 590 OP_PARAMS = [ 591 ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY)), 592 ("fields", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString))), 593 ]
594
595 596 -class OpOobCommand(OpCode):
597 """Interact with OOB.""" 598 OP_PARAMS = [ 599 ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 600 ("command", None, ht.TElemOf(constants.OOB_COMMANDS)), 601 ("timeout", constants.OOB_TIMEOUT, ht.TInt), 602 ]
603
604 605 # node opcodes 606 607 -class OpNodeRemove(OpCode):
608 """Remove a node. 609 610 @type node_name: C{str} 611 @ivar node_name: The name of the node to remove. If the node still has 612 instances on it, the operation will fail. 613 614 """ 615 OP_DSC_FIELD = "node_name" 616 OP_PARAMS = [ 617 _PNodeName, 618 ]
619
620 621 -class OpNodeAdd(OpCode):
622 """Add a node to the cluster. 623 624 @type node_name: C{str} 625 @ivar node_name: The name of the node to add. This can be a short name, 626 but it will be expanded to the FQDN. 627 @type primary_ip: IP address 628 @ivar primary_ip: The primary IP of the node. This will be ignored when the 629 opcode is submitted, but will be filled during the node 630 add (so it will be visible in the job query). 631 @type secondary_ip: IP address 632 @ivar secondary_ip: The secondary IP of the node. This needs to be passed 633 if the cluster has been initialized in 'dual-network' 634 mode, otherwise it must not be given. 635 @type readd: C{bool} 636 @ivar readd: Whether to re-add an existing node to the cluster. If 637 this is not passed, then the operation will abort if the node 638 name is already in the cluster; use this parameter to 'repair' 639 a node that had its configuration broken, or was reinstalled 640 without removal from the cluster. 641 @type group: C{str} 642 @ivar group: The node group to which this node will belong. 643 @type vm_capable: C{bool} 644 @ivar vm_capable: The vm_capable node attribute 645 @type master_capable: C{bool} 646 @ivar master_capable: The master_capable node attribute 647 648 """ 649 OP_DSC_FIELD = "node_name" 650 OP_PARAMS = [ 651 _PNodeName, 652 ("primary_ip", None, ht.NoType), 653 ("secondary_ip", None, ht.TMaybeString), 654 ("readd", False, ht.TBool), 655 ("group", None, ht.TMaybeString), 656 ("master_capable", None, ht.TMaybeBool), 657 ("vm_capable", None, ht.TMaybeBool), 658 ("ndparams", None, ht.TMaybeDict), 659 ]
660
661 662 -class OpNodeQuery(OpCode):
663 """Compute the list of nodes.""" 664 OP_PARAMS = [ 665 _POutputFields, 666 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 667 ("use_locking", False, ht.TBool), 668 ]
669
670 671 -class OpNodeQueryvols(OpCode):
672 """Get list of volumes on node.""" 673 OP_PARAMS = [ 674 _POutputFields, 675 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 676 ]
677
678 679 -class OpNodeQueryStorage(OpCode):
680 """Get information on storage for node(s).""" 681 OP_PARAMS = [ 682 _POutputFields, 683 _PStorageType, 684 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 685 ("name", None, ht.TMaybeString), 686 ]
687
688 689 -class OpNodeModifyStorage(OpCode):
690 """Modifies the properies of a storage unit""" 691 OP_PARAMS = [ 692 _PNodeName, 693 _PStorageType, 694 ("name", ht.NoDefault, ht.TNonEmptyString), 695 ("changes", ht.NoDefault, ht.TDict), 696 ]
697
698 699 -class OpRepairNodeStorage(OpCode):
700 """Repairs the volume group on a node.""" 701 OP_DSC_FIELD = "node_name" 702 OP_PARAMS = [ 703 _PNodeName, 704 _PStorageType, 705 _PIgnoreConsistency, 706 ("name", ht.NoDefault, ht.TNonEmptyString), 707 ]
708
709 710 -class OpNodeSetParams(OpCode):
711 """Change the parameters of a node.""" 712 OP_DSC_FIELD = "node_name" 713 OP_PARAMS = [ 714 _PNodeName, 715 _PForce, 716 ("master_candidate", None, ht.TMaybeBool), 717 ("offline", None, ht.TMaybeBool), 718 ("drained", None, ht.TMaybeBool), 719 ("auto_promote", False, ht.TBool), 720 ("master_capable", None, ht.TMaybeBool), 721 ("vm_capable", None, ht.TMaybeBool), 722 ("secondary_ip", None, ht.TMaybeString), 723 ("ndparams", None, ht.TMaybeDict), 724 ("powered", None, ht.TMaybeBool), 725 ]
726
727 728 -class OpNodePowercycle(OpCode):
729 """Tries to powercycle a node.""" 730 OP_DSC_FIELD = "node_name" 731 OP_PARAMS = [ 732 _PNodeName, 733 _PForce, 734 ]
735
736 737 -class OpNodeMigrate(OpCode):
738 """Migrate all instances from a node.""" 739 OP_DSC_FIELD = "node_name" 740 OP_PARAMS = [ 741 _PNodeName, 742 _PMigrationMode, 743 _PMigrationLive, 744 ]
745
746 747 -class OpNodeEvacStrategy(OpCode):
748 """Compute the evacuation strategy for a list of nodes.""" 749 OP_DSC_FIELD = "nodes" 750 OP_PARAMS = [ 751 ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)), 752 ("remote_node", None, ht.TMaybeString), 753 ("iallocator", None, ht.TMaybeString), 754 ]
755
756 757 # instance opcodes 758 759 -class OpInstanceCreate(OpCode):
760 """Create an instance. 761 762 @ivar instance_name: Instance name 763 @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES}) 764 @ivar source_handshake: Signed handshake from source (remote import only) 765 @ivar source_x509_ca: Source X509 CA in PEM format (remote import only) 766 @ivar source_instance_name: Previous name of instance (remote import only) 767 @ivar source_shutdown_timeout: Shutdown timeout used for source instance 768 (remote import only) 769 770 """ 771 OP_DSC_FIELD = "instance_name" 772 OP_PARAMS = [ 773 _PInstanceName, 774 ("beparams", ht.EmptyDict, ht.TDict), 775 ("disks", ht.NoDefault, ht.TListOf(ht.TDict)), 776 ("disk_template", ht.NoDefault, _CheckDiskTemplate), 777 ("file_driver", None, ht.TOr(ht.TNone, ht.TElemOf(constants.FILE_DRIVER))), 778 ("file_storage_dir", None, ht.TMaybeString), 779 ("force_variant", False, ht.TBool), 780 ("hvparams", ht.EmptyDict, ht.TDict), 781 ("hypervisor", None, ht.TMaybeString), 782 ("iallocator", None, ht.TMaybeString), 783 ("identify_defaults", False, ht.TBool), 784 ("ip_check", True, ht.TBool), 785 ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES)), 786 ("name_check", True, ht.TBool), 787 ("nics", ht.NoDefault, ht.TListOf(ht.TDict)), 788 ("no_install", None, ht.TMaybeBool), 789 ("osparams", ht.EmptyDict, ht.TDict), 790 ("os_type", None, ht.TMaybeString), 791 ("pnode", None, ht.TMaybeString), 792 ("snode", None, ht.TMaybeString), 793 ("source_handshake", None, ht.TOr(ht.TList, ht.TNone)), 794 ("source_instance_name", None, ht.TMaybeString), 795 ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, 796 ht.TPositiveInt), 797 ("source_x509_ca", None, ht.TMaybeString), 798 ("src_node", None, ht.TMaybeString), 799 ("src_path", None, ht.TMaybeString), 800 ("start", True, ht.TBool), 801 ("wait_for_sync", True, ht.TBool), 802 ]
803
804 805 -class OpInstanceReinstall(OpCode):
806 """Reinstall an instance's OS.""" 807 OP_DSC_FIELD = "instance_name" 808 OP_PARAMS = [ 809 _PInstanceName, 810 ("os_type", None, ht.TMaybeString), 811 ("force_variant", False, ht.TBool), 812 ("osparams", None, ht.TMaybeDict), 813 ]
814
815 816 -class OpInstanceRemove(OpCode):
817 """Remove an instance.""" 818 OP_DSC_FIELD = "instance_name" 819 OP_PARAMS = [ 820 _PInstanceName, 821 _PShutdownTimeout, 822 ("ignore_failures", False, ht.TBool), 823 ]
824
825 826 -class OpInstanceRename(OpCode):
827 """Rename an instance.""" 828 OP_PARAMS = [ 829 _PInstanceName, 830 ("new_name", ht.NoDefault, ht.TNonEmptyString), 831 ("ip_check", False, ht.TBool), 832 ("name_check", True, ht.TBool), 833 ]
834
835 836 -class OpInstanceStartup(OpCode):
837 """Startup an instance.""" 838 OP_DSC_FIELD = "instance_name" 839 OP_PARAMS = [ 840 _PInstanceName, 841 _PForce, 842 _PIgnoreOfflineNodes, 843 _PNoRemember, 844 ("hvparams", ht.EmptyDict, ht.TDict), 845 ("beparams", ht.EmptyDict, ht.TDict), 846 ]
847
848 849 -class OpInstanceShutdown(OpCode):
850 """Shutdown an instance.""" 851 OP_DSC_FIELD = "instance_name" 852 OP_PARAMS = [ 853 _PInstanceName, 854 _PIgnoreOfflineNodes, 855 _PNoRemember, 856 ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt), 857 ]
858
859 860 -class OpInstanceReboot(OpCode):
861 """Reboot an instance.""" 862 OP_DSC_FIELD = "instance_name" 863 OP_PARAMS = [ 864 _PInstanceName, 865 _PShutdownTimeout, 866 ("ignore_secondaries", False, ht.TBool), 867 ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES)), 868 ]
869
870 871 -class OpInstanceReplaceDisks(OpCode):
872 """Replace the disks of an instance.""" 873 OP_DSC_FIELD = "instance_name" 874 OP_PARAMS = [ 875 _PInstanceName, 876 ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES)), 877 ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt)), 878 ("remote_node", None, ht.TMaybeString), 879 ("iallocator", None, ht.TMaybeString), 880 ("early_release", False, ht.TBool), 881 ]
882
883 884 -class OpInstanceFailover(OpCode):
885 """Failover an instance.""" 886 OP_DSC_FIELD = "instance_name" 887 OP_PARAMS = [ 888 _PInstanceName, 889 _PShutdownTimeout, 890 _PIgnoreConsistency, 891 ]
892
893 894 -class OpInstanceMigrate(OpCode):
895 """Migrate an instance. 896 897 This migrates (without shutting down an instance) to its secondary 898 node. 899 900 @ivar instance_name: the name of the instance 901 @ivar mode: the migration mode (live, non-live or None for auto) 902 903 """ 904 OP_DSC_FIELD = "instance_name" 905 OP_PARAMS = [ 906 _PInstanceName, 907 _PMigrationMode, 908 _PMigrationLive, 909 ("cleanup", False, ht.TBool), 910 ]
911
912 913 -class OpInstanceMove(OpCode):
914 """Move an instance. 915 916 This move (with shutting down an instance and data copying) to an 917 arbitrary node. 918 919 @ivar instance_name: the name of the instance 920 @ivar target_node: the destination node 921 922 """ 923 OP_DSC_FIELD = "instance_name" 924 OP_PARAMS = [ 925 _PInstanceName, 926 _PShutdownTimeout, 927 _PIgnoreConsistency, 928 ("target_node", ht.NoDefault, ht.TNonEmptyString), 929 ]
930
931 932 -class OpInstanceConsole(OpCode):
933 """Connect to an instance's console.""" 934 OP_DSC_FIELD = "instance_name" 935 OP_PARAMS = [ 936 _PInstanceName 937 ]
938
939 940 -class OpInstanceActivateDisks(OpCode):
941 """Activate an instance's disks.""" 942 OP_DSC_FIELD = "instance_name" 943 OP_PARAMS = [ 944 _PInstanceName, 945 ("ignore_size", False, ht.TBool), 946 ]
947
948 949 -class OpInstanceDeactivateDisks(OpCode):
950 """Deactivate an instance's disks.""" 951 OP_DSC_FIELD = "instance_name" 952 OP_PARAMS = [ 953 _PInstanceName, 954 _PForce, 955 ]
956
957 958 -class OpInstanceRecreateDisks(OpCode):
959 """Deactivate an instance's disks.""" 960 OP_DSC_FIELD = "instance_name" 961 OP_PARAMS = [ 962 _PInstanceName, 963 ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt)), 964 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 965 ]
966
967 968 -class OpInstanceQuery(OpCode):
969 """Compute the list of instances.""" 970 OP_PARAMS = [ 971 _POutputFields, 972 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 973 ("use_locking", False, ht.TBool), 974 ]
975
976 977 -class OpInstanceQueryData(OpCode):
978 """Compute the run-time status of instances.""" 979 OP_PARAMS = [ 980 ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 981 ("static", False, ht.TBool), 982 ("use_locking", False, ht.TBool), 983 ]
984
985 986 -class OpInstanceSetParams(OpCode):
987 """Change the parameters of an instance.""" 988 OP_DSC_FIELD = "instance_name" 989 OP_PARAMS = [ 990 _PInstanceName, 991 _PForce, 992 ("nics", ht.EmptyList, ht.TList), 993 ("disks", ht.EmptyList, ht.TList), 994 ("beparams", ht.EmptyDict, ht.TDict), 995 ("hvparams", ht.EmptyDict, ht.TDict), 996 ("disk_template", None, ht.TOr(ht.TNone, _CheckDiskTemplate)), 997 ("remote_node", None, ht.TMaybeString), 998 ("os_name", None, ht.TMaybeString), 999 ("force_variant", False, ht.TBool), 1000 ("osparams", None, ht.TMaybeDict), 1001 ("wait_for_sync", True, ht.TBool), 1002 ]
1003
1004 1005 -class OpInstanceGrowDisk(OpCode):
1006 """Grow a disk of an instance.""" 1007 OP_DSC_FIELD = "instance_name" 1008 OP_PARAMS = [ 1009 _PInstanceName, 1010 ("disk", ht.NoDefault, ht.TInt), 1011 ("amount", ht.NoDefault, ht.TInt), 1012 ("wait_for_sync", True, ht.TBool), 1013 ]
1014
1015 1016 # Node group opcodes 1017 1018 -class OpGroupAdd(OpCode):
1019 """Add a node group to the cluster.""" 1020 OP_DSC_FIELD = "group_name" 1021 OP_PARAMS = [ 1022 _PGroupName, 1023 ("ndparams", None, ht.TMaybeDict), 1024 ("alloc_policy", None, 1025 ht.TOr(ht.TNone, ht.TElemOf(constants.VALID_ALLOC_POLICIES))), 1026 ]
1027
1028 1029 -class OpGroupAssignNodes(OpCode):
1030 """Assign nodes to a node group.""" 1031 OP_DSC_FIELD = "group_name" 1032 OP_PARAMS = [ 1033 _PGroupName, 1034 _PForce, 1035 ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)), 1036 ]
1037
1038 1039 -class OpGroupQuery(OpCode):
1040 """Compute the list of node groups.""" 1041 OP_PARAMS = [ 1042 _POutputFields, 1043 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 1044 ]
1045
1046 1047 -class OpGroupSetParams(OpCode):
1048 """Change the parameters of a node group.""" 1049 OP_DSC_FIELD = "group_name" 1050 OP_PARAMS = [ 1051 _PGroupName, 1052 ("ndparams", None, ht.TMaybeDict), 1053 ("alloc_policy", None, ht.TOr(ht.TNone, 1054 ht.TElemOf(constants.VALID_ALLOC_POLICIES))), 1055 ]
1056
1057 1058 -class OpGroupRemove(OpCode):
1059 """Remove a node group from the cluster.""" 1060 OP_DSC_FIELD = "group_name" 1061 OP_PARAMS = [ 1062 _PGroupName, 1063 ]
1064
1065 1066 -class OpGroupRename(OpCode):
1067 """Rename a node group in the cluster.""" 1068 OP_DSC_FIELD = "old_name" 1069 OP_PARAMS = [ 1070 ("old_name", ht.NoDefault, ht.TNonEmptyString), 1071 ("new_name", ht.NoDefault, ht.TNonEmptyString), 1072 ]
1073
1074 1075 # OS opcodes 1076 -class OpOsDiagnose(OpCode):
1077 """Compute the list of guest operating systems.""" 1078 OP_PARAMS = [ 1079 _POutputFields, 1080 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 1081 ]
1082
1083 1084 # Exports opcodes 1085 -class OpBackupQuery(OpCode):
1086 """Compute the list of exported images.""" 1087 OP_PARAMS = [ 1088 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 1089 ("use_locking", False, ht.TBool), 1090 ]
1091
1092 1093 -class OpBackupPrepare(OpCode):
1094 """Prepares an instance export. 1095 1096 @ivar instance_name: Instance name 1097 @ivar mode: Export mode (one of L{constants.EXPORT_MODES}) 1098 1099 """ 1100 OP_DSC_FIELD = "instance_name" 1101 OP_PARAMS = [ 1102 _PInstanceName, 1103 ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES)), 1104 ]
1105
1106 1107 -class OpBackupExport(OpCode):
1108 """Export an instance. 1109 1110 For local exports, the export destination is the node name. For remote 1111 exports, the export destination is a list of tuples, each consisting of 1112 hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using 1113 the cluster domain secret over the value "${index}:${hostname}:${port}". The 1114 destination X509 CA must be a signed certificate. 1115 1116 @ivar mode: Export mode (one of L{constants.EXPORT_MODES}) 1117 @ivar target_node: Export destination 1118 @ivar x509_key_name: X509 key to use (remote export only) 1119 @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export 1120 only) 1121 1122 """ 1123 OP_DSC_FIELD = "instance_name" 1124 OP_PARAMS = [ 1125 _PInstanceName, 1126 _PShutdownTimeout, 1127 # TODO: Rename target_node as it changes meaning for different export modes 1128 # (e.g. "destination") 1129 ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList)), 1130 ("shutdown", True, ht.TBool), 1131 ("remove_instance", False, ht.TBool), 1132 ("ignore_remove_failures", False, ht.TBool), 1133 ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES)), 1134 ("x509_key_name", None, ht.TOr(ht.TList, ht.TNone)), 1135 ("destination_x509_ca", None, ht.TMaybeString), 1136 ]
1137
1138 1139 -class OpBackupRemove(OpCode):
1140 """Remove an instance's export.""" 1141 OP_DSC_FIELD = "instance_name" 1142 OP_PARAMS = [ 1143 _PInstanceName, 1144 ]
1145
1146 1147 # Tags opcodes 1148 -class OpTagsGet(OpCode):
1149 """Returns the tags of the given object.""" 1150 OP_DSC_FIELD = "name" 1151 OP_PARAMS = [ 1152 _PTagKind, 1153 # Name is only meaningful for nodes and instances 1154 ("name", ht.NoDefault, ht.TMaybeString), 1155 ]
1156
1157 1158 -class OpTagsSearch(OpCode):
1159 """Searches the tags in the cluster for a given pattern.""" 1160 OP_DSC_FIELD = "pattern" 1161 OP_PARAMS = [ 1162 ("pattern", ht.NoDefault, ht.TNonEmptyString), 1163 ]
1164
1165 1166 -class OpTagsSet(OpCode):
1167 """Add a list of tags on a given object.""" 1168 OP_PARAMS = [ 1169 _PTagKind, 1170 _PTags, 1171 # Name is only meaningful for nodes and instances 1172 ("name", ht.NoDefault, ht.TMaybeString), 1173 ]
1174
1175 1176 -class OpTagsDel(OpCode):
1177 """Remove a list of tags from a given object.""" 1178 OP_PARAMS = [ 1179 _PTagKind, 1180 _PTags, 1181 # Name is only meaningful for nodes and instances 1182 ("name", ht.NoDefault, ht.TMaybeString), 1183 ]
1184
1185 # Test opcodes 1186 -class OpTestDelay(OpCode):
1187 """Sleeps for a configured amount of time. 1188 1189 This is used just for debugging and testing. 1190 1191 Parameters: 1192 - duration: the time to sleep 1193 - on_master: if true, sleep on the master 1194 - on_nodes: list of nodes in which to sleep 1195 1196 If the on_master parameter is true, it will execute a sleep on the 1197 master (before any node sleep). 1198 1199 If the on_nodes list is not empty, it will sleep on those nodes 1200 (after the sleep on the master, if that is enabled). 1201 1202 As an additional feature, the case of duration < 0 will be reported 1203 as an execution error, so this opcode can be used as a failure 1204 generator. The case of duration == 0 will not be treated specially. 1205 1206 """ 1207 OP_DSC_FIELD = "duration" 1208 OP_PARAMS = [ 1209 ("duration", ht.NoDefault, ht.TNumber), 1210 ("on_master", True, ht.TBool), 1211 ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 1212 ("repeat", 0, ht.TPositiveInt) 1213 ]
1214
1215 1216 -class OpTestAllocator(OpCode):
1217 """Allocator framework testing. 1218 1219 This opcode has two modes: 1220 - gather and return allocator input for a given mode (allocate new 1221 or replace secondary) and a given instance definition (direction 1222 'in') 1223 - run a selected allocator for a given operation (as above) and 1224 return the allocator output (direction 'out') 1225 1226 """ 1227 OP_DSC_FIELD = "allocator" 1228 OP_PARAMS = [ 1229 ("direction", ht.NoDefault, 1230 ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS)), 1231 ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES)), 1232 ("name", ht.NoDefault, ht.TNonEmptyString), 1233 ("nics", ht.NoDefault, ht.TOr(ht.TNone, ht.TListOf( 1234 ht.TDictOf(ht.TElemOf(["mac", "ip", "bridge"]), 1235 ht.TOr(ht.TNone, ht.TNonEmptyString))))), 1236 ("disks", ht.NoDefault, ht.TOr(ht.TNone, ht.TList)), 1237 ("hypervisor", None, ht.TMaybeString), 1238 ("allocator", None, ht.TMaybeString), 1239 ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)), 1240 ("mem_size", None, ht.TOr(ht.TNone, ht.TPositiveInt)), 1241 ("vcpus", None, ht.TOr(ht.TNone, ht.TPositiveInt)), 1242 ("os", None, ht.TMaybeString), 1243 ("disk_template", None, ht.TMaybeString), 1244 ("evac_nodes", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString))), 1245 ]
1246
1247 1248 -class OpTestJqueue(OpCode):
1249 """Utility opcode to test some aspects of the job queue. 1250 1251 """ 1252 OP_PARAMS = [ 1253 ("notify_waitlock", False, ht.TBool), 1254 ("notify_exec", False, ht.TBool), 1255 ("log_messages", ht.EmptyList, ht.TListOf(ht.TString)), 1256 ("fail", False, ht.TBool), 1257 ]
1258
1259 1260 -class OpTestDummy(OpCode):
1261 """Utility opcode used by unittests. 1262 1263 """ 1264 OP_PARAMS = [ 1265 ("result", ht.NoDefault, ht.NoType), 1266 ("messages", ht.NoDefault, ht.NoType), 1267 ("fail", ht.NoDefault, ht.NoType), 1268 ] 1269 WITH_LU = False
1270
1271 1272 -def _GetOpList():
1273 """Returns list of all defined opcodes. 1274 1275 Does not eliminate duplicates by C{OP_ID}. 1276 1277 """ 1278 return [v for v in globals().values() 1279 if (isinstance(v, type) and issubclass(v, OpCode) and 1280 hasattr(v, "OP_ID") and v is not OpCode)]
1281 1282 1283 OP_MAPPING = dict((v.OP_ID, v) for v in _GetOpList()) 1284