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, 2012, 2013 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=R0903 
  35   
  36  import logging 
  37  import re 
  38  import ipaddr 
  39   
  40  from ganeti import constants 
  41  from ganeti import errors 
  42  from ganeti import ht 
  43  from ganeti import objects 
  44  from ganeti import outils 
  45   
  46   
  47  # Common opcode attributes 
  48   
  49  #: output fields for a query operation 
  50  _POutputFields = ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), 
  51                    "Selected output fields") 
  52   
  53  #: the shutdown timeout 
  54  _PShutdownTimeout = \ 
  55    ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt, 
  56     "How long to wait for instance to shut down") 
  57   
  58  #: the force parameter 
  59  _PForce = ("force", False, ht.TBool, "Whether to force the operation") 
  60   
  61  #: a required instance name (for single-instance LUs) 
  62  _PInstanceName = ("instance_name", ht.NoDefault, ht.TNonEmptyString, 
  63                    "Instance name") 
  64   
  65  #: Whether to ignore offline nodes 
  66  _PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool, 
  67                          "Whether to ignore offline nodes") 
  68   
  69  #: a required node name (for single-node LUs) 
  70  _PNodeName = ("node_name", ht.NoDefault, ht.TNonEmptyString, "Node name") 
  71   
  72  #: a required node group name (for single-group LUs) 
  73  _PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString, "Group name") 
  74   
  75  #: Migration type (live/non-live) 
  76  _PMigrationMode = ("mode", None, 
  77                     ht.TMaybe(ht.TElemOf(constants.HT_MIGRATION_MODES)), 
  78                     "Migration mode") 
  79   
  80  #: Obsolete 'live' migration mode (boolean) 
  81  _PMigrationLive = ("live", None, ht.TMaybeBool, 
  82                     "Legacy setting for live migration, do not use") 
  83   
  84  #: Tag type 
  85  _PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES), 
  86               "Tag kind") 
  87   
  88  #: List of tag strings 
  89  _PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), 
  90            "List of tag names") 
  91   
  92  _PForceVariant = ("force_variant", False, ht.TBool, 
  93                    "Whether to force an unknown OS variant") 
  94   
  95  _PWaitForSync = ("wait_for_sync", True, ht.TBool, 
  96                   "Whether to wait for the disk to synchronize") 
  97   
  98  _PWaitForSyncFalse = ("wait_for_sync", False, ht.TBool, 
  99                        "Whether to wait for the disk to synchronize" 
 100                        " (defaults to false)") 
 101   
 102  _PIgnoreConsistency = ("ignore_consistency", False, ht.TBool, 
 103                         "Whether to ignore disk consistency") 
 104   
 105  _PStorageName = ("name", ht.NoDefault, ht.TMaybeString, "Storage name") 
 106   
 107  _PUseLocking = ("use_locking", False, ht.TBool, 
 108                  "Whether to use synchronization") 
 109   
 110  _PNameCheck = ("name_check", True, ht.TBool, "Whether to check name") 
 111   
 112  _PNodeGroupAllocPolicy = \ 
 113    ("alloc_policy", None, 
 114     ht.TMaybe(ht.TElemOf(constants.VALID_ALLOC_POLICIES)), 
 115     "Instance allocation policy") 
 116   
 117  _PGroupNodeParams = ("ndparams", None, ht.TMaybeDict, 
 118                       "Default node parameters for group") 
 119   
 120  _PQueryWhat = ("what", ht.NoDefault, ht.TElemOf(constants.QR_VIA_OP), 
 121                 "Resource(s) to query for") 
 122   
 123  _PEarlyRelease = ("early_release", False, ht.TBool, 
 124                    "Whether to release locks as soon as possible") 
 125   
 126  _PIpCheckDoc = "Whether to ensure instance's IP address is inactive" 
 127   
 128  #: Do not remember instance state changes 
 129  _PNoRemember = ("no_remember", False, ht.TBool, 
 130                  "Do not remember the state change") 
 131   
 132  #: Target node for instance migration/failover 
 133  _PMigrationTargetNode = ("target_node", None, ht.TMaybeString, 
 134                           "Target node for shared-storage instances") 
 135   
 136  _PStartupPaused = ("startup_paused", False, ht.TBool, 
 137                     "Pause instance at startup") 
 138   
 139  _PVerbose = ("verbose", False, ht.TBool, "Verbose mode") 
 140   
 141  # Parameters for cluster verification 
 142  _PDebugSimulateErrors = ("debug_simulate_errors", False, ht.TBool, 
 143                           "Whether to simulate errors (useful for debugging)") 
 144  _PErrorCodes = ("error_codes", False, ht.TBool, "Error codes") 
 145  _PSkipChecks = ("skip_checks", ht.EmptyList, 
 146                  ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS)), 
 147                  "Which checks to skip") 
 148  _PIgnoreErrors = ("ignore_errors", ht.EmptyList, 
 149                    ht.TListOf(ht.TElemOf(constants.CV_ALL_ECODES_STRINGS)), 
 150                    "List of error codes that should be treated as warnings") 
 151   
 152  # Disk parameters 
 153  _PDiskParams = \ 
 154    ("diskparams", None, 
 155     ht.TMaybe(ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict)), 
 156     "Disk templates' parameter defaults") 
 157   
 158  # Parameters for node resource model 
 159  _PHvState = ("hv_state", None, ht.TMaybeDict, "Set hypervisor states") 
 160  _PDiskState = ("disk_state", None, ht.TMaybeDict, "Set disk states") 
 161   
 162  #: Opportunistic locking 
 163  _POpportunisticLocking = \ 
 164    ("opportunistic_locking", False, ht.TBool, 
 165     ("Whether to employ opportunistic locking for nodes, meaning nodes" 
 166      " already locked by another opcode won't be considered for instance" 
 167      " allocation (only when an iallocator is used)")) 
 168   
 169  _PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool, 
 170                     "Whether to ignore ipolicy violations") 
 171   
 172  # Allow runtime changes while migrating 
 173  _PAllowRuntimeChgs = ("allow_runtime_changes", True, ht.TBool, 
 174                        "Allow runtime changes (eg. memory ballooning)") 
 175   
 176  #: IAllocator field builder 
 177  _PIAllocFromDesc = lambda desc: ("iallocator", None, ht.TMaybeString, desc) 
 178   
 179  #: a required network name 
 180  _PNetworkName = ("network_name", ht.NoDefault, ht.TNonEmptyString, 
 181                   "Set network name") 
 182   
 183  _PTargetGroups = \ 
 184    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), 
 185     "Destination group names or UUIDs (defaults to \"all but current group\")") 
 186   
 187  #: OP_ID conversion regular expression 
 188  _OPID_RE = re.compile("([a-z])([A-Z])") 
 189   
 190  #: Utility function for L{OpClusterSetParams} 
 191  _TestClusterOsListItem = \ 
 192    ht.TAnd(ht.TIsLength(2), ht.TItems([ 
 193      ht.TElemOf(constants.DDMS_VALUES), 
 194      ht.TNonEmptyString, 
 195      ])) 
 196   
 197  _TestClusterOsList = ht.TMaybeListOf(_TestClusterOsListItem) 
 198   
 199  # TODO: Generate check from constants.INIC_PARAMS_TYPES 
 200  #: Utility function for testing NIC definitions 
 201  _TestNicDef = \ 
 202    ht.Comment("NIC parameters")(ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS), 
 203                                            ht.TMaybeString)) 
 204   
 205  _TSetParamsResultItemItems = [ 
 206    ht.Comment("name of changed parameter")(ht.TNonEmptyString), 
 207    ht.Comment("new value")(ht.TAny), 
 208    ] 
 209   
 210  _TSetParamsResult = \ 
 211    ht.TListOf(ht.TAnd(ht.TIsLength(len(_TSetParamsResultItemItems)), 
 212                       ht.TItems(_TSetParamsResultItemItems))) 
 213   
 214  # In the disks option we can provide arbitrary parameters too, which 
 215  # we may not be able to validate at this level, so we just check the 
 216  # format of the dict here and the checks concerning IDISK_PARAMS will 
 217  # happen at the LU level 
 218  _TDiskParams = \ 
 219    ht.Comment("Disk parameters")(ht.TDictOf(ht.TNonEmptyString, 
 220                                             ht.TOr(ht.TNonEmptyString, ht.TInt))) 
 221   
 222  _TQueryRow = \ 
 223    ht.TListOf(ht.TAnd(ht.TIsLength(2), 
 224                       ht.TItems([ht.TElemOf(constants.RS_ALL), 
 225                                  ht.TAny]))) 
 226   
 227  _TQueryResult = ht.TListOf(_TQueryRow) 
 228   
 229  _TOldQueryRow = ht.TListOf(ht.TAny) 
 230   
 231  _TOldQueryResult = ht.TListOf(_TOldQueryRow) 
 232   
 233   
 234  _SUMMARY_PREFIX = { 
 235    "CLUSTER_": "C_", 
 236    "GROUP_": "G_", 
 237    "NODE_": "N_", 
 238    "INSTANCE_": "I_", 
 239    } 
 240   
 241  #: Attribute name for dependencies 
 242  DEPEND_ATTR = "depends" 
 243   
 244  #: Attribute name for comment 
 245  COMMENT_ATTR = "comment" 
246 247 248 -def _NameToId(name):
249 """Convert an opcode class name to an OP_ID. 250 251 @type name: string 252 @param name: the class name, as OpXxxYyy 253 @rtype: string 254 @return: the name in the OP_XXXX_YYYY format 255 256 """ 257 if not name.startswith("Op"): 258 return None 259 # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't 260 # consume any input, and hence we would just have all the elements 261 # in the list, one by one; but it seems that split doesn't work on 262 # non-consuming input, hence we have to process the input string a 263 # bit 264 name = _OPID_RE.sub(r"\1,\2", name) 265 elems = name.split(",") 266 return "_".join(n.upper() for n in elems)
267
268 269 -def _GenerateObjectTypeCheck(obj, fields_types):
270 """Helper to generate type checks for objects. 271 272 @param obj: The object to generate type checks 273 @param fields_types: The fields and their types as a dict 274 @return: A ht type check function 275 276 """ 277 assert set(obj.GetAllSlots()) == set(fields_types.keys()), \ 278 "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys())) 279 return ht.TStrictDict(True, True, fields_types)
280 281 282 _TQueryFieldDef = \ 283 _GenerateObjectTypeCheck(objects.QueryFieldDefinition, { 284 "name": ht.TNonEmptyString, 285 "title": ht.TNonEmptyString, 286 "kind": ht.TElemOf(constants.QFT_ALL), 287 "doc": ht.TNonEmptyString, 288 })
289 290 291 -def RequireFileStorage():
292 """Checks that file storage is enabled. 293 294 While it doesn't really fit into this module, L{utils} was deemed too large 295 of a dependency to be imported for just one or two functions. 296 297 @raise errors.OpPrereqError: when file storage is disabled 298 299 """ 300 if not constants.ENABLE_FILE_STORAGE: 301 raise errors.OpPrereqError("File storage disabled at configure time", 302 errors.ECODE_INVAL)
303
304 305 -def RequireSharedFileStorage():
306 """Checks that shared file storage is enabled. 307 308 While it doesn't really fit into this module, L{utils} was deemed too large 309 of a dependency to be imported for just one or two functions. 310 311 @raise errors.OpPrereqError: when shared file storage is disabled 312 313 """ 314 if not constants.ENABLE_SHARED_FILE_STORAGE: 315 raise errors.OpPrereqError("Shared file storage disabled at" 316 " configure time", errors.ECODE_INVAL)
317
318 319 @ht.WithDesc("CheckFileStorage") 320 -def _CheckFileStorage(value):
321 """Ensures file storage is enabled if used. 322 323 """ 324 if value == constants.DT_FILE: 325 RequireFileStorage() 326 elif value == constants.DT_SHARED_FILE: 327 RequireSharedFileStorage() 328 return True
329
330 331 -def _BuildDiskTemplateCheck(accept_none):
332 """Builds check for disk template. 333 334 @type accept_none: bool 335 @param accept_none: whether to accept None as a correct value 336 @rtype: callable 337 338 """ 339 template_check = ht.TElemOf(constants.DISK_TEMPLATES) 340 341 if accept_none: 342 template_check = ht.TMaybe(template_check) 343 344 return ht.TAnd(template_check, _CheckFileStorage)
345
346 347 -def _CheckStorageType(storage_type):
348 """Ensure a given storage type is valid. 349 350 """ 351 if storage_type not in constants.VALID_STORAGE_TYPES: 352 raise errors.OpPrereqError("Unknown storage type: %s" % storage_type, 353 errors.ECODE_INVAL) 354 if storage_type == constants.ST_FILE: 355 # TODO: What about shared file storage? 356 RequireFileStorage() 357 return True
358 359 360 #: Storage type parameter 361 _PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType, 362 "Storage type")
363 364 365 @ht.WithDesc("IPv4 network") 366 -def _CheckCIDRNetNotation(value):
367 """Ensure a given CIDR notation type is valid. 368 369 """ 370 try: 371 ipaddr.IPv4Network(value) 372 except ipaddr.AddressValueError: 373 return False 374 return True
375
376 377 @ht.WithDesc("IPv4 address") 378 -def _CheckCIDRAddrNotation(value):
379 """Ensure a given CIDR notation type is valid. 380 381 """ 382 try: 383 ipaddr.IPv4Address(value) 384 except ipaddr.AddressValueError: 385 return False 386 return True
387
388 389 @ht.WithDesc("IPv6 address") 390 -def _CheckCIDR6AddrNotation(value):
391 """Ensure a given CIDR notation type is valid. 392 393 """ 394 try: 395 ipaddr.IPv6Address(value) 396 except ipaddr.AddressValueError: 397 return False 398 return True
399
400 401 @ht.WithDesc("IPv6 network") 402 -def _CheckCIDR6NetNotation(value):
403 """Ensure a given CIDR notation type is valid. 404 405 """ 406 try: 407 ipaddr.IPv6Network(value) 408 except ipaddr.AddressValueError: 409 return False 410 return True
411 412 413 _TIpAddress4 = ht.TAnd(ht.TString, _CheckCIDRAddrNotation) 414 _TIpAddress6 = ht.TAnd(ht.TString, _CheckCIDR6AddrNotation) 415 _TIpNetwork4 = ht.TAnd(ht.TString, _CheckCIDRNetNotation) 416 _TIpNetwork6 = ht.TAnd(ht.TString, _CheckCIDR6NetNotation) 417 _TMaybeAddr4List = ht.TMaybe(ht.TListOf(_TIpAddress4))
418 419 420 -class _AutoOpParamSlots(outils.AutoSlots):
421 """Meta class for opcode definitions. 422 423 """
424 - def __new__(mcs, name, bases, attrs):
425 """Called when a class should be created. 426 427 @param mcs: The meta class 428 @param name: Name of created class 429 @param bases: Base classes 430 @type attrs: dict 431 @param attrs: Class attributes 432 433 """ 434 assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name 435 436 slots = mcs._GetSlots(attrs) 437 assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ 438 "Class '%s' uses unknown field in OP_DSC_FIELD" % name 439 assert ("OP_DSC_FORMATTER" not in attrs or 440 callable(attrs["OP_DSC_FORMATTER"])), \ 441 ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" % 442 (name, type(attrs["OP_DSC_FORMATTER"]))) 443 444 attrs["OP_ID"] = _NameToId(name) 445 446 return outils.AutoSlots.__new__(mcs, name, bases, attrs)
447 448 @classmethod
449 - def _GetSlots(mcs, attrs):
450 """Build the slots out of OP_PARAMS. 451 452 """ 453 # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams 454 params = attrs.setdefault("OP_PARAMS", []) 455 456 # Use parameter names as slots 457 return [pname for (pname, _, _, _) in params]
458
459 460 -class BaseOpCode(outils.ValidatedSlots):
461 """A simple serializable object. 462 463 This object serves as a parent class for OpCode without any custom 464 field handling. 465 466 """ 467 # pylint: disable=E1101 468 # as OP_ID is dynamically defined 469 __metaclass__ = _AutoOpParamSlots 470
471 - def __getstate__(self):
472 """Generic serializer. 473 474 This method just returns the contents of the instance as a 475 dictionary. 476 477 @rtype: C{dict} 478 @return: the instance attributes and their values 479 480 """ 481 state = {} 482 for name in self.GetAllSlots(): 483 if hasattr(self, name): 484 state[name] = getattr(self, name) 485 return state
486
487 - def __setstate__(self, state):
488 """Generic unserializer. 489 490 This method just restores from the serialized state the attributes 491 of the current instance. 492 493 @param state: the serialized opcode data 494 @type state: C{dict} 495 496 """ 497 if not isinstance(state, dict): 498 raise ValueError("Invalid data to __setstate__: expected dict, got %s" % 499 type(state)) 500 501 for name in self.GetAllSlots(): 502 if name not in state and hasattr(self, name): 503 delattr(self, name) 504 505 for name in state: 506 setattr(self, name, state[name])
507 508 @classmethod
509 - def GetAllParams(cls):
510 """Compute list of all parameters for an opcode. 511 512 """ 513 slots = [] 514 for parent in cls.__mro__: 515 slots.extend(getattr(parent, "OP_PARAMS", [])) 516 return slots
517
518 - def Validate(self, set_defaults): # pylint: disable=W0221
519 """Validate opcode parameters, optionally setting default values. 520 521 @type set_defaults: bool 522 @param set_defaults: Whether to set default values 523 @raise errors.OpPrereqError: When a parameter value doesn't match 524 requirements 525 526 """ 527 for (attr_name, default, test, _) in self.GetAllParams(): 528 assert test == ht.NoType or callable(test) 529 530 if not hasattr(self, attr_name): 531 if default == ht.NoDefault: 532 raise errors.OpPrereqError("Required parameter '%s.%s' missing" % 533 (self.OP_ID, attr_name), 534 errors.ECODE_INVAL) 535 elif set_defaults: 536 if callable(default): 537 dval = default() 538 else: 539 dval = default 540 setattr(self, attr_name, dval) 541 542 if test == ht.NoType: 543 # no tests here 544 continue 545 546 if set_defaults or hasattr(self, attr_name): 547 attr_val = getattr(self, attr_name) 548 if not test(attr_val): 549 logging.error("OpCode %s, parameter %s, has invalid type %s/value" 550 " '%s' expecting type %s", 551 self.OP_ID, attr_name, type(attr_val), attr_val, test) 552 raise errors.OpPrereqError("Parameter '%s.%s' fails validation" % 553 (self.OP_ID, attr_name), 554 errors.ECODE_INVAL)
555
556 557 -def _BuildJobDepCheck(relative):
558 """Builds check for job dependencies (L{DEPEND_ATTR}). 559 560 @type relative: bool 561 @param relative: Whether to accept relative job IDs (negative) 562 @rtype: callable 563 564 """ 565 if relative: 566 job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId) 567 else: 568 job_id = ht.TJobId 569 570 job_dep = \ 571 ht.TAnd(ht.TOr(ht.TList, ht.TTuple), 572 ht.TIsLength(2), 573 ht.TItems([job_id, 574 ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))])) 575 576 return ht.TMaybeListOf(job_dep)
577 578 579 TNoRelativeJobDependencies = _BuildJobDepCheck(False) 580 581 #: List of submission status and job ID as returned by C{SubmitManyJobs} 582 _TJobIdListItem = \ 583 ht.TAnd(ht.TIsLength(2), 584 ht.TItems([ht.Comment("success")(ht.TBool), 585 ht.Comment("Job ID if successful, error message" 586 " otherwise")(ht.TOr(ht.TString, 587 ht.TJobId))])) 588 TJobIdList = ht.TListOf(_TJobIdListItem) 589 590 #: Result containing only list of submitted jobs 591 TJobIdListOnly = ht.TStrictDict(True, True, { 592 constants.JOB_IDS_KEY: ht.Comment("List of submitted jobs")(TJobIdList), 593 })
594 595 596 -class OpCode(BaseOpCode):
597 """Abstract OpCode. 598 599 This is the root of the actual OpCode hierarchy. All clases derived 600 from this class should override OP_ID. 601 602 @cvar OP_ID: The ID of this opcode. This should be unique amongst all 603 children of this class. 604 @cvar OP_DSC_FIELD: The name of a field whose value will be included in the 605 string returned by Summary(); see the docstring of that 606 method for details). 607 @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if 608 not present, then the field will be simply converted 609 to string 610 @cvar OP_PARAMS: List of opcode attributes, the default values they should 611 get if not already defined, and types they must match. 612 @cvar OP_RESULT: Callable to verify opcode result 613 @cvar WITH_LU: Boolean that specifies whether this should be included in 614 mcpu's dispatch table 615 @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just 616 the check steps 617 @ivar priority: Opcode priority for queue 618 619 """ 620 # pylint: disable=E1101 621 # as OP_ID is dynamically defined 622 WITH_LU = True 623 OP_PARAMS = [ 624 ("dry_run", None, ht.TMaybeBool, "Run checks only, don't execute"), 625 ("debug_level", None, ht.TMaybe(ht.TNonNegativeInt), "Debug level"), 626 ("priority", constants.OP_PRIO_DEFAULT, 627 ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"), 628 (DEPEND_ATTR, None, _BuildJobDepCheck(True), 629 "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)" 630 " job IDs can be used; see :doc:`design document <design-chained-jobs>`" 631 " for details"), 632 (COMMENT_ATTR, None, ht.TMaybeString, 633 "Comment describing the purpose of the opcode"), 634 ] 635 OP_RESULT = None 636
637 - def __getstate__(self):
638 """Specialized getstate for opcodes. 639 640 This method adds to the state dictionary the OP_ID of the class, 641 so that on unload we can identify the correct class for 642 instantiating the opcode. 643 644 @rtype: C{dict} 645 @return: the state as a dictionary 646 647 """ 648 data = BaseOpCode.__getstate__(self) 649 data["OP_ID"] = self.OP_ID 650 return data
651 652 @classmethod
653 - def LoadOpCode(cls, data):
654 """Generic load opcode method. 655 656 The method identifies the correct opcode class from the dict-form 657 by looking for a OP_ID key, if this is not found, or its value is 658 not available in this module as a child of this class, we fail. 659 660 @type data: C{dict} 661 @param data: the serialized opcode 662 663 """ 664 if not isinstance(data, dict): 665 raise ValueError("Invalid data to LoadOpCode (%s)" % type(data)) 666 if "OP_ID" not in data: 667 raise ValueError("Invalid data to LoadOpcode, missing OP_ID") 668 op_id = data["OP_ID"] 669 op_class = None 670 if op_id in OP_MAPPING: 671 op_class = OP_MAPPING[op_id] 672 else: 673 raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" % 674 op_id) 675 op = op_class() 676 new_data = data.copy() 677 del new_data["OP_ID"] 678 op.__setstate__(new_data) 679 return op
680
681 - def Summary(self):
682 """Generates a summary description of this opcode. 683 684 The summary is the value of the OP_ID attribute (without the "OP_" 685 prefix), plus the value of the OP_DSC_FIELD attribute, if one was 686 defined; this field should allow to easily identify the operation 687 (for an instance creation job, e.g., it would be the instance 688 name). 689 690 """ 691 assert self.OP_ID is not None and len(self.OP_ID) > 3 692 # all OP_ID start with OP_, we remove that 693 txt = self.OP_ID[3:] 694 field_name = getattr(self, "OP_DSC_FIELD", None) 695 if field_name: 696 field_value = getattr(self, field_name, None) 697 field_formatter = getattr(self, "OP_DSC_FORMATTER", None) 698 if callable(field_formatter): 699 field_value = field_formatter(field_value) 700 elif isinstance(field_value, (list, tuple)): 701 field_value = ",".join(str(i) for i in field_value) 702 txt = "%s(%s)" % (txt, field_value) 703 return txt
704
705 - def TinySummary(self):
706 """Generates a compact summary description of the opcode. 707 708 """ 709 assert self.OP_ID.startswith("OP_") 710 711 text = self.OP_ID[3:] 712 713 for (prefix, supplement) in _SUMMARY_PREFIX.items(): 714 if text.startswith(prefix): 715 return supplement + text[len(prefix):] 716 717 return text
718
719 720 # cluster opcodes 721 722 -class OpClusterPostInit(OpCode):
723 """Post cluster initialization. 724 725 This opcode does not touch the cluster at all. Its purpose is to run hooks 726 after the cluster has been initialized. 727 728 """ 729 OP_RESULT = ht.TBool
730
731 732 -class OpClusterDestroy(OpCode):
733 """Destroy the cluster. 734 735 This opcode has no other parameters. All the state is irreversibly 736 lost after the execution of this opcode. 737 738 """ 739 OP_RESULT = ht.TNonEmptyString
740
741 742 -class OpClusterQuery(OpCode):
743 """Query cluster information.""" 744 OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TAny)
745
746 747 -class OpClusterVerify(OpCode):
748 """Submits all jobs necessary to verify the cluster. 749 750 """ 751 OP_PARAMS = [ 752 _PDebugSimulateErrors, 753 _PErrorCodes, 754 _PSkipChecks, 755 _PIgnoreErrors, 756 _PVerbose, 757 ("group_name", None, ht.TMaybeString, "Group to verify"), 758 ] 759 OP_RESULT = TJobIdListOnly
760
761 762 -class OpClusterVerifyConfig(OpCode):
763 """Verify the cluster config. 764 765 """ 766 OP_PARAMS = [ 767 _PDebugSimulateErrors, 768 _PErrorCodes, 769 _PIgnoreErrors, 770 _PVerbose, 771 ] 772 OP_RESULT = ht.TBool
773
774 775 -class OpClusterVerifyGroup(OpCode):
776 """Run verify on a node group from the cluster. 777 778 @type skip_checks: C{list} 779 @ivar skip_checks: steps to be skipped from the verify process; this 780 needs to be a subset of 781 L{constants.VERIFY_OPTIONAL_CHECKS}; currently 782 only L{constants.VERIFY_NPLUSONE_MEM} can be passed 783 784 """ 785 OP_DSC_FIELD = "group_name" 786 OP_PARAMS = [ 787 _PGroupName, 788 _PDebugSimulateErrors, 789 _PErrorCodes, 790 _PSkipChecks, 791 _PIgnoreErrors, 792 _PVerbose, 793 ] 794 OP_RESULT = ht.TBool
795
796 797 -class OpClusterVerifyDisks(OpCode):
798 """Verify the cluster disks. 799 800 """ 801 OP_RESULT = TJobIdListOnly
802
803 804 -class OpGroupVerifyDisks(OpCode):
805 """Verifies the status of all disks in a node group. 806 807 Result: a tuple of three elements: 808 - dict of node names with issues (values: error msg) 809 - list of instances with degraded disks (that should be activated) 810 - dict of instances with missing logical volumes (values: (node, vol) 811 pairs with details about the missing volumes) 812 813 In normal operation, all lists should be empty. A non-empty instance 814 list (3rd element of the result) is still ok (errors were fixed) but 815 non-empty node list means some node is down, and probably there are 816 unfixable drbd errors. 817 818 Note that only instances that are drbd-based are taken into 819 consideration. This might need to be revisited in the future. 820 821 """ 822 OP_DSC_FIELD = "group_name" 823 OP_PARAMS = [ 824 _PGroupName, 825 ] 826 OP_RESULT = \ 827 ht.TAnd(ht.TIsLength(3), 828 ht.TItems([ht.TDictOf(ht.TString, ht.TString), 829 ht.TListOf(ht.TString), 830 ht.TDictOf(ht.TString, 831 ht.TListOf(ht.TListOf(ht.TString)))]))
832
833 834 -class OpClusterRepairDiskSizes(OpCode):
835 """Verify the disk sizes of the instances and fixes configuration 836 mimatches. 837 838 Parameters: optional instances list, in case we want to restrict the 839 checks to only a subset of the instances. 840 841 Result: a list of tuples, (instance, disk, new-size) for changed 842 configurations. 843 844 In normal operation, the list should be empty. 845 846 @type instances: list 847 @ivar instances: the list of instances to check, or empty for all instances 848 849 """ 850 OP_PARAMS = [ 851 ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None), 852 ] 853 OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3), 854 ht.TItems([ht.TNonEmptyString, 855 ht.TNonNegativeInt, 856 ht.TNonNegativeInt])))
857
858 859 -class OpClusterConfigQuery(OpCode):
860 """Query cluster configuration values.""" 861 OP_PARAMS = [ 862 _POutputFields, 863 ] 864 OP_RESULT = ht.TListOf(ht.TAny)
865
866 867 -class OpClusterRename(OpCode):
868 """Rename the cluster. 869 870 @type name: C{str} 871 @ivar name: The new name of the cluster. The name and/or the master IP 872 address will be changed to match the new name and its IP 873 address. 874 875 """ 876 OP_DSC_FIELD = "name" 877 OP_PARAMS = [ 878 ("name", ht.NoDefault, ht.TNonEmptyString, None), 879 ] 880 OP_RESULT = ht.TNonEmptyString
881
882 883 -class OpClusterSetParams(OpCode):
884 """Change the parameters of the cluster. 885 886 @type vg_name: C{str} or C{None} 887 @ivar vg_name: The new volume group name or None to disable LVM usage. 888 889 """ 890 OP_PARAMS = [ 891 _PHvState, 892 _PDiskState, 893 ("vg_name", None, ht.TMaybe(ht.TString), "Volume group name"), 894 ("enabled_hypervisors", None, 895 ht.TMaybe(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), 896 ht.TTrue)), 897 "List of enabled hypervisors"), 898 ("hvparams", None, 899 ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)), 900 "Cluster-wide hypervisor parameter defaults, hypervisor-dependent"), 901 ("beparams", None, ht.TMaybeDict, 902 "Cluster-wide backend parameter defaults"), 903 ("os_hvp", None, ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)), 904 "Cluster-wide per-OS hypervisor parameter defaults"), 905 ("osparams", None, 906 ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)), 907 "Cluster-wide OS parameter defaults"), 908 _PDiskParams, 909 ("candidate_pool_size", None, ht.TMaybe(ht.TPositiveInt), 910 "Master candidate pool size"), 911 ("uid_pool", None, ht.NoType, 912 "Set UID pool, must be list of lists describing UID ranges (two items," 913 " start and end inclusive)"), 914 ("add_uids", None, ht.NoType, 915 "Extend UID pool, must be list of lists describing UID ranges (two" 916 " items, start and end inclusive) to be added"), 917 ("remove_uids", None, ht.NoType, 918 "Shrink UID pool, must be list of lists describing UID ranges (two" 919 " items, start and end inclusive) to be removed"), 920 ("maintain_node_health", None, ht.TMaybeBool, 921 "Whether to automatically maintain node health"), 922 ("prealloc_wipe_disks", None, ht.TMaybeBool, 923 "Whether to wipe disks before allocating them to instances"), 924 ("nicparams", None, ht.TMaybeDict, "Cluster-wide NIC parameter defaults"), 925 ("ndparams", None, ht.TMaybeDict, "Cluster-wide node parameter defaults"), 926 ("ipolicy", None, ht.TMaybeDict, 927 "Cluster-wide :ref:`instance policy <rapi-ipolicy>` specs"), 928 ("drbd_helper", None, ht.TMaybe(ht.TString), "DRBD helper program"), 929 ("default_iallocator", None, ht.TMaybe(ht.TString), 930 "Default iallocator for cluster"), 931 ("master_netdev", None, ht.TMaybe(ht.TString), 932 "Master network device"), 933 ("master_netmask", None, ht.TMaybe(ht.TNonNegativeInt), 934 "Netmask of the master IP"), 935 ("reserved_lvs", None, ht.TMaybeListOf(ht.TNonEmptyString), 936 "List of reserved LVs"), 937 ("hidden_os", None, _TestClusterOsList, 938 "Modify list of hidden operating systems: each modification must have" 939 " two items, the operation and the OS name; the operation can be" 940 " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)), 941 ("blacklisted_os", None, _TestClusterOsList, 942 "Modify list of blacklisted operating systems: each modification must" 943 " have two items, the operation and the OS name; the operation can be" 944 " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)), 945 ("use_external_mip_script", None, ht.TMaybeBool, 946 "Whether to use an external master IP address setup script"), 947 ] 948 OP_RESULT = ht.TNone
949
950 951 -class OpClusterRedistConf(OpCode):
952 """Force a full push of the cluster configuration. 953 954 """ 955 OP_RESULT = ht.TNone
956
957 958 -class OpClusterActivateMasterIp(OpCode):
959 """Activate the master IP on the master node. 960 961 """ 962 OP_RESULT = ht.TNone
963
964 965 -class OpClusterDeactivateMasterIp(OpCode):
966 """Deactivate the master IP on the master node. 967 968 """ 969 OP_RESULT = ht.TNone
970
971 972 -class OpQuery(OpCode):
973 """Query for resources/items. 974 975 @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP} 976 @ivar fields: List of fields to retrieve 977 @ivar qfilter: Query filter 978 979 """ 980 OP_DSC_FIELD = "what" 981 OP_PARAMS = [ 982 _PQueryWhat, 983 _PUseLocking, 984 ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), 985 "Requested fields"), 986 ("qfilter", None, ht.TMaybe(ht.TList), 987 "Query filter"), 988 ] 989 OP_RESULT = \ 990 _GenerateObjectTypeCheck(objects.QueryResponse, { 991 "fields": ht.TListOf(_TQueryFieldDef), 992 "data": _TQueryResult, 993 })
994
995 996 -class OpQueryFields(OpCode):
997 """Query for available resource/item fields. 998 999 @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP} 1000 @ivar fields: List of fields to retrieve 1001 1002 """ 1003 OP_DSC_FIELD = "what" 1004 OP_PARAMS = [ 1005 _PQueryWhat, 1006 ("fields", None, ht.TMaybeListOf(ht.TNonEmptyString), 1007 "Requested fields; if not given, all are returned"), 1008 ] 1009 OP_RESULT = \ 1010 _GenerateObjectTypeCheck(objects.QueryFieldsResponse, { 1011 "fields": ht.TListOf(_TQueryFieldDef), 1012 })
1013
1014 1015 -class OpOobCommand(OpCode):
1016 """Interact with OOB.""" 1017 OP_PARAMS = [ 1018 ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), 1019 "List of nodes to run the OOB command against"), 1020 ("command", ht.NoDefault, ht.TElemOf(constants.OOB_COMMANDS), 1021 "OOB command to be run"), 1022 ("timeout", constants.OOB_TIMEOUT, ht.TInt, 1023 "Timeout before the OOB helper will be terminated"), 1024 ("ignore_status", False, ht.TBool, 1025 "Ignores the node offline status for power off"), 1026 ("power_delay", constants.OOB_POWER_DELAY, ht.TNonNegativeFloat, 1027 "Time in seconds to wait between powering on nodes"), 1028 ] 1029 # Fixme: Make it more specific with all the special cases in LUOobCommand 1030 OP_RESULT = _TQueryResult
1031
1032 1033 -class OpRestrictedCommand(OpCode):
1034 """Runs a restricted command on node(s). 1035 1036 """ 1037 OP_PARAMS = [ 1038 _PUseLocking, 1039 ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), 1040 "Nodes on which the command should be run (at least one)"), 1041 ("command", ht.NoDefault, ht.TNonEmptyString, 1042 "Command name (no parameters)"), 1043 ] 1044 1045 _RESULT_ITEMS = [ 1046 ht.Comment("success")(ht.TBool), 1047 ht.Comment("output or error message")(ht.TString), 1048 ] 1049 1050 OP_RESULT = \ 1051 ht.TListOf(ht.TAnd(ht.TIsLength(len(_RESULT_ITEMS)), 1052 ht.TItems(_RESULT_ITEMS)))
1053
1054 1055 # node opcodes 1056 1057 -class OpNodeRemove(OpCode):
1058 """Remove a node. 1059 1060 @type node_name: C{str} 1061 @ivar node_name: The name of the node to remove. If the node still has 1062 instances on it, the operation will fail. 1063 1064 """ 1065 OP_DSC_FIELD = "node_name" 1066 OP_PARAMS = [ 1067 _PNodeName, 1068 ] 1069 OP_RESULT = ht.TNone
1070
1071 1072 -class OpNodeAdd(OpCode):
1073 """Add a node to the cluster. 1074 1075 @type node_name: C{str} 1076 @ivar node_name: The name of the node to add. This can be a short name, 1077 but it will be expanded to the FQDN. 1078 @type primary_ip: IP address 1079 @ivar primary_ip: The primary IP of the node. This will be ignored when the 1080 opcode is submitted, but will be filled during the node 1081 add (so it will be visible in the job query). 1082 @type secondary_ip: IP address 1083 @ivar secondary_ip: The secondary IP of the node. This needs to be passed 1084 if the cluster has been initialized in 'dual-network' 1085 mode, otherwise it must not be given. 1086 @type readd: C{bool} 1087 @ivar readd: Whether to re-add an existing node to the cluster. If 1088 this is not passed, then the operation will abort if the node 1089 name is already in the cluster; use this parameter to 'repair' 1090 a node that had its configuration broken, or was reinstalled 1091 without removal from the cluster. 1092 @type group: C{str} 1093 @ivar group: The node group to which this node will belong. 1094 @type vm_capable: C{bool} 1095 @ivar vm_capable: The vm_capable node attribute 1096 @type master_capable: C{bool} 1097 @ivar master_capable: The master_capable node attribute 1098 1099 """ 1100 OP_DSC_FIELD = "node_name" 1101 OP_PARAMS = [ 1102 _PNodeName, 1103 _PHvState, 1104 _PDiskState, 1105 ("primary_ip", None, ht.NoType, "Primary IP address"), 1106 ("secondary_ip", None, ht.TMaybeString, "Secondary IP address"), 1107 ("readd", False, ht.TBool, "Whether node is re-added to cluster"), 1108 ("group", None, ht.TMaybeString, "Initial node group"), 1109 ("master_capable", None, ht.TMaybeBool, 1110 "Whether node can become master or master candidate"), 1111 ("vm_capable", None, ht.TMaybeBool, 1112 "Whether node can host instances"), 1113 ("ndparams", None, ht.TMaybeDict, "Node parameters"), 1114 ] 1115 OP_RESULT = ht.TNone
1116
1117 1118 -class OpNodeQuery(OpCode):
1119 """Compute the list of nodes.""" 1120 OP_PARAMS = [ 1121 _POutputFields, 1122 _PUseLocking, 1123 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), 1124 "Empty list to query all nodes, node names otherwise"), 1125 ] 1126 OP_RESULT = _TOldQueryResult
1127
1128 1129 -class OpNodeQueryvols(OpCode):
1130 """Get list of volumes on node.""" 1131 OP_PARAMS = [ 1132 _POutputFields, 1133 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), 1134 "Empty list to query all nodes, node names otherwise"), 1135 ] 1136 OP_RESULT = ht.TListOf(ht.TAny)
1137
1138 1139 -class OpNodeQueryStorage(OpCode):
1140 """Get information on storage for node(s).""" 1141 OP_PARAMS = [ 1142 _POutputFields, 1143 _PStorageType, 1144 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"), 1145 ("name", None, ht.TMaybeString, "Storage name"), 1146 ] 1147 OP_RESULT = _TOldQueryResult
1148
1149 1150 -class OpNodeModifyStorage(OpCode):
1151 """Modifies the properies of a storage unit""" 1152 OP_DSC_FIELD = "node_name" 1153 OP_PARAMS = [ 1154 _PNodeName, 1155 _PStorageType, 1156 _PStorageName, 1157 ("changes", ht.NoDefault, ht.TDict, "Requested changes"), 1158 ] 1159 OP_RESULT = ht.TNone
1160
1161 1162 -class OpRepairNodeStorage(OpCode):
1163 """Repairs the volume group on a node.""" 1164 OP_DSC_FIELD = "node_name" 1165 OP_PARAMS = [ 1166 _PNodeName, 1167 _PStorageType, 1168 _PStorageName, 1169 _PIgnoreConsistency, 1170 ] 1171 OP_RESULT = ht.TNone
1172
1173 1174 -class OpNodeSetParams(OpCode):
1175 """Change the parameters of a node.""" 1176 OP_DSC_FIELD = "node_name" 1177 OP_PARAMS = [ 1178 _PNodeName, 1179 _PForce, 1180 _PHvState, 1181 _PDiskState, 1182 ("master_candidate", None, ht.TMaybeBool, 1183 "Whether the node should become a master candidate"), 1184 ("offline", None, ht.TMaybeBool, 1185 "Whether the node should be marked as offline"), 1186 ("drained", None, ht.TMaybeBool, 1187 "Whether the node should be marked as drained"), 1188 ("auto_promote", False, ht.TBool, 1189 "Whether node(s) should be promoted to master candidate if necessary"), 1190 ("master_capable", None, ht.TMaybeBool, 1191 "Denote whether node can become master or master candidate"), 1192 ("vm_capable", None, ht.TMaybeBool, 1193 "Denote whether node can host instances"), 1194 ("secondary_ip", None, ht.TMaybeString, 1195 "Change node's secondary IP address"), 1196 ("ndparams", None, ht.TMaybeDict, "Set node parameters"), 1197 ("powered", None, ht.TMaybeBool, 1198 "Whether the node should be marked as powered"), 1199 ] 1200 OP_RESULT = _TSetParamsResult
1201
1202 1203 -class OpNodePowercycle(OpCode):
1204 """Tries to powercycle a node.""" 1205 OP_DSC_FIELD = "node_name" 1206 OP_PARAMS = [ 1207 _PNodeName, 1208 _PForce, 1209 ] 1210 OP_RESULT = ht.TMaybeString
1211
1212 1213 -class OpNodeMigrate(OpCode):
1214 """Migrate all instances from a node.""" 1215 OP_DSC_FIELD = "node_name" 1216 OP_PARAMS = [ 1217 _PNodeName, 1218 _PMigrationMode, 1219 _PMigrationLive, 1220 _PMigrationTargetNode, 1221 _PAllowRuntimeChgs, 1222 _PIgnoreIpolicy, 1223 _PIAllocFromDesc("Iallocator for deciding the target node" 1224 " for shared-storage instances"), 1225 ] 1226 OP_RESULT = TJobIdListOnly
1227
1228 1229 -class OpNodeEvacuate(OpCode):
1230 """Evacuate instances off a number of nodes.""" 1231 OP_DSC_FIELD = "node_name" 1232 OP_PARAMS = [ 1233 _PEarlyRelease, 1234 _PNodeName, 1235 ("remote_node", None, ht.TMaybeString, "New secondary node"), 1236 _PIAllocFromDesc("Iallocator for computing solution"), 1237 ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES), 1238 "Node evacuation mode"), 1239 ] 1240 OP_RESULT = TJobIdListOnly
1241
1242 1243 # instance opcodes 1244 1245 -class OpInstanceCreate(OpCode):
1246 """Create an instance. 1247 1248 @ivar instance_name: Instance name 1249 @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES}) 1250 @ivar source_handshake: Signed handshake from source (remote import only) 1251 @ivar source_x509_ca: Source X509 CA in PEM format (remote import only) 1252 @ivar source_instance_name: Previous name of instance (remote import only) 1253 @ivar source_shutdown_timeout: Shutdown timeout used for source instance 1254 (remote import only) 1255 1256 """ 1257 OP_DSC_FIELD = "instance_name" 1258 OP_PARAMS = [ 1259 _PInstanceName, 1260 _PForceVariant, 1261 _PWaitForSync, 1262 _PNameCheck, 1263 _PIgnoreIpolicy, 1264 _POpportunisticLocking, 1265 ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"), 1266 ("disks", ht.NoDefault, ht.TListOf(_TDiskParams), 1267 "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;" 1268 " each disk definition must contain a ``%s`` value and" 1269 " can contain an optional ``%s`` value denoting the disk access mode" 1270 " (%s)" % 1271 (constants.IDISK_SIZE, constants.IDISK_SIZE, constants.IDISK_SIZE, 1272 constants.IDISK_MODE, 1273 " or ".join("``%s``" % i for i in sorted(constants.DISK_ACCESS_SET)))), 1274 ("disk_template", ht.NoDefault, _BuildDiskTemplateCheck(True), 1275 "Disk template"), 1276 ("file_driver", None, ht.TMaybe(ht.TElemOf(constants.FILE_DRIVER)), 1277 "Driver for file-backed disks"), 1278 ("file_storage_dir", None, ht.TMaybeString, 1279 "Directory for storing file-backed disks"), 1280 ("hvparams", ht.EmptyDict, ht.TDict, 1281 "Hypervisor parameters for instance, hypervisor-dependent"), 1282 ("hypervisor", None, ht.TMaybeString, "Hypervisor"), 1283 _PIAllocFromDesc("Iallocator for deciding which node(s) to use"), 1284 ("identify_defaults", False, ht.TBool, 1285 "Reset instance parameters to default if equal"), 1286 ("ip_check", True, ht.TBool, _PIpCheckDoc), 1287 ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"), 1288 ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES), 1289 "Instance creation mode"), 1290 ("nics", ht.NoDefault, ht.TListOf(_TestNicDef), 1291 "List of NIC (network interface) definitions, for example" 1292 " ``[{}, {}, {\"%s\": \"198.51.100.4\"}]``; each NIC definition can" 1293 " contain the optional values %s" % 1294 (constants.INIC_IP, 1295 ", ".join("``%s``" % i for i in sorted(constants.INIC_PARAMS)))), 1296 ("no_install", None, ht.TMaybeBool, 1297 "Do not install the OS (will disable automatic start)"), 1298 ("osparams", ht.EmptyDict, ht.TDict, "OS parameters for instance"), 1299 ("os_type", None, ht.TMaybeString, "Operating system"), 1300 ("pnode", None, ht.TMaybeString, "Primary node"), 1301 ("snode", None, ht.TMaybeString, "Secondary node"), 1302 ("source_handshake", None, ht.TMaybe(ht.TList), 1303 "Signed handshake from source (remote import only)"), 1304 ("source_instance_name", None, ht.TMaybeString, 1305 "Source instance name (remote import only)"), 1306 ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, 1307 ht.TNonNegativeInt, 1308 "How long source instance was given to shut down (remote import only)"), 1309 ("source_x509_ca", None, ht.TMaybeString, 1310 "Source X509 CA in PEM format (remote import only)"), 1311 ("src_node", None, ht.TMaybeString, "Source node for import"), 1312 ("src_path", None, ht.TMaybeString, "Source directory for import"), 1313 ("start", True, ht.TBool, "Whether to start instance after creation"), 1314 ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Instance tags"), 1315 ] 1316 OP_RESULT = ht.Comment("instance nodes")(ht.TListOf(ht.TNonEmptyString))
1317
1318 1319 -class OpInstanceMultiAlloc(OpCode):
1320 """Allocates multiple instances. 1321 1322 """ 1323 OP_PARAMS = [ 1324 _POpportunisticLocking, 1325 _PIAllocFromDesc("Iallocator used to allocate all the instances"), 1326 ("instances", ht.EmptyList, ht.TListOf(ht.TInstanceOf(OpInstanceCreate)), 1327 "List of instance create opcodes describing the instances to allocate"), 1328 ] 1329 _JOB_LIST = ht.Comment("List of submitted jobs")(TJobIdList) 1330 ALLOCATABLE_KEY = "allocatable" 1331 FAILED_KEY = "allocatable" 1332 OP_RESULT = ht.TStrictDict(True, True, { 1333 constants.JOB_IDS_KEY: _JOB_LIST, 1334 ALLOCATABLE_KEY: ht.TListOf(ht.TNonEmptyString), 1335 FAILED_KEY: ht.TListOf(ht.TNonEmptyString), 1336 }) 1337
1338 - def __getstate__(self):
1339 """Generic serializer. 1340 1341 """ 1342 state = OpCode.__getstate__(self) 1343 if hasattr(self, "instances"): 1344 # pylint: disable=E1101 1345 state["instances"] = [inst.__getstate__() for inst in self.instances] 1346 return state
1347
1348 - def __setstate__(self, state):
1349 """Generic unserializer. 1350 1351 This method just restores from the serialized state the attributes 1352 of the current instance. 1353 1354 @param state: the serialized opcode data 1355 @type state: C{dict} 1356 1357 """ 1358 if not isinstance(state, dict): 1359 raise ValueError("Invalid data to __setstate__: expected dict, got %s" % 1360 type(state)) 1361 1362 if "instances" in state: 1363 state["instances"] = map(OpCode.LoadOpCode, state["instances"]) 1364 1365 return OpCode.__setstate__(self, state)
1366
1367 - def Validate(self, set_defaults):
1368 """Validates this opcode. 1369 1370 We do this recursively. 1371 1372 """ 1373 OpCode.Validate(self, set_defaults) 1374 1375 for inst in self.instances: # pylint: disable=E1101 1376 inst.Validate(set_defaults)
1377
1378 1379 -class OpInstanceReinstall(OpCode):
1380 """Reinstall an instance's OS.""" 1381 OP_DSC_FIELD = "instance_name" 1382 OP_PARAMS = [ 1383 _PInstanceName, 1384 _PForceVariant, 1385 ("os_type", None, ht.TMaybeString, "Instance operating system"), 1386 ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"), 1387 ] 1388 OP_RESULT = ht.TNone
1389
1390 1391 -class OpInstanceRemove(OpCode):
1392 """Remove an instance.""" 1393 OP_DSC_FIELD = "instance_name" 1394 OP_PARAMS = [ 1395 _PInstanceName, 1396 _PShutdownTimeout, 1397 ("ignore_failures", False, ht.TBool, 1398 "Whether to ignore failures during removal"), 1399 ] 1400 OP_RESULT = ht.TNone
1401
1402 1403 -class OpInstanceRename(OpCode):
1404 """Rename an instance.""" 1405 OP_PARAMS = [ 1406 _PInstanceName, 1407 _PNameCheck, 1408 ("new_name", ht.NoDefault, ht.TNonEmptyString, "New instance name"), 1409 ("ip_check", False, ht.TBool, _PIpCheckDoc), 1410 ] 1411 OP_RESULT = ht.Comment("New instance name")(ht.TNonEmptyString)
1412
1413 1414 -class OpInstanceStartup(OpCode):
1415 """Startup an instance.""" 1416 OP_DSC_FIELD = "instance_name" 1417 OP_PARAMS = [ 1418 _PInstanceName, 1419 _PForce, 1420 _PIgnoreOfflineNodes, 1421 ("hvparams", ht.EmptyDict, ht.TDict, 1422 "Temporary hypervisor parameters, hypervisor-dependent"), 1423 ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"), 1424 _PNoRemember, 1425 _PStartupPaused, 1426 ] 1427 OP_RESULT = ht.TNone
1428
1429 1430 -class OpInstanceShutdown(OpCode):
1431 """Shutdown an instance.""" 1432 OP_DSC_FIELD = "instance_name" 1433 OP_PARAMS = [ 1434 _PInstanceName, 1435 _PForce, 1436 _PIgnoreOfflineNodes, 1437 ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt, 1438 "How long to wait for instance to shut down"), 1439 _PNoRemember, 1440 ] 1441 OP_RESULT = ht.TNone
1442
1443 1444 -class OpInstanceReboot(OpCode):
1445 """Reboot an instance.""" 1446 OP_DSC_FIELD = "instance_name" 1447 OP_PARAMS = [ 1448 _PInstanceName, 1449 _PShutdownTimeout, 1450 ("ignore_secondaries", False, ht.TBool, 1451 "Whether to start the instance even if secondary disks are failing"), 1452 ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES), 1453 "How to reboot instance"), 1454 ] 1455 OP_RESULT = ht.TNone
1456
1457 1458 -class OpInstanceReplaceDisks(OpCode):
1459 """Replace the disks of an instance.""" 1460 OP_DSC_FIELD = "instance_name" 1461 OP_PARAMS = [ 1462 _PInstanceName, 1463 _PEarlyRelease, 1464 _PIgnoreIpolicy, 1465 ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES), 1466 "Replacement mode"), 1467 ("disks", ht.EmptyList, ht.TListOf(ht.TNonNegativeInt), 1468 "Disk indexes"), 1469 ("remote_node", None, ht.TMaybeString, "New secondary node"), 1470 _PIAllocFromDesc("Iallocator for deciding new secondary node"), 1471 ] 1472 OP_RESULT = ht.TNone
1473
1474 1475 -class OpInstanceFailover(OpCode):
1476 """Failover an instance.""" 1477 OP_DSC_FIELD = "instance_name" 1478 OP_PARAMS = [ 1479 _PInstanceName, 1480 _PShutdownTimeout, 1481 _PIgnoreConsistency, 1482 _PMigrationTargetNode, 1483 _PIgnoreIpolicy, 1484 _PIAllocFromDesc("Iallocator for deciding the target node for" 1485 " shared-storage instances"), 1486 ] 1487 OP_RESULT = ht.TNone
1488
1489 1490 -class OpInstanceMigrate(OpCode):
1491 """Migrate an instance. 1492 1493 This migrates (without shutting down an instance) to its secondary 1494 node. 1495 1496 @ivar instance_name: the name of the instance 1497 @ivar mode: the migration mode (live, non-live or None for auto) 1498 1499 """ 1500 OP_DSC_FIELD = "instance_name" 1501 OP_PARAMS = [ 1502 _PInstanceName, 1503 _PMigrationMode, 1504 _PMigrationLive, 1505 _PMigrationTargetNode, 1506 _PAllowRuntimeChgs, 1507 _PIgnoreIpolicy, 1508 ("cleanup", False, ht.TBool, 1509 "Whether a previously failed migration should be cleaned up"), 1510 _PIAllocFromDesc("Iallocator for deciding the target node for" 1511 " shared-storage instances"), 1512 ("allow_failover", False, ht.TBool, 1513 "Whether we can fallback to failover if migration is not possible"), 1514 ] 1515 OP_RESULT = ht.TNone
1516
1517 1518 -class OpInstanceMove(OpCode):
1519 """Move an instance. 1520 1521 This move (with shutting down an instance and data copying) to an 1522 arbitrary node. 1523 1524 @ivar instance_name: the name of the instance 1525 @ivar target_node: the destination node 1526 1527 """ 1528 OP_DSC_FIELD = "instance_name" 1529 OP_PARAMS = [ 1530 _PInstanceName, 1531 _PShutdownTimeout, 1532 _PIgnoreIpolicy, 1533 ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"), 1534 _PIgnoreConsistency, 1535 ] 1536 OP_RESULT = ht.TNone
1537
1538 1539 -class OpInstanceConsole(OpCode):
1540 """Connect to an instance's console.""" 1541 OP_DSC_FIELD = "instance_name" 1542 OP_PARAMS = [ 1543 _PInstanceName, 1544 ] 1545 OP_RESULT = ht.TDict
1546
1547 1548 -class OpInstanceActivateDisks(OpCode):
1549 """Activate an instance's disks.""" 1550 OP_DSC_FIELD = "instance_name" 1551 OP_PARAMS = [ 1552 _PInstanceName, 1553 ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"), 1554 _PWaitForSyncFalse, 1555 ] 1556 OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3), 1557 ht.TItems([ht.TNonEmptyString, 1558 ht.TNonEmptyString, 1559 ht.TNonEmptyString])))
1560
1561 1562 -class OpInstanceDeactivateDisks(OpCode):
1563 """Deactivate an instance's disks.""" 1564 OP_DSC_FIELD = "instance_name" 1565 OP_PARAMS = [ 1566 _PInstanceName, 1567 _PForce, 1568 ] 1569 OP_RESULT = ht.TNone
1570
1571 1572 -class OpInstanceRecreateDisks(OpCode):
1573 """Recreate an instance's disks.""" 1574 _TDiskChanges = \ 1575 ht.TAnd(ht.TIsLength(2), 1576 ht.TItems([ht.Comment("Disk index")(ht.TNonNegativeInt), 1577 ht.Comment("Parameters")(_TDiskParams)])) 1578 1579 OP_DSC_FIELD = "instance_name" 1580 OP_PARAMS = [ 1581 _PInstanceName, 1582 ("disks", ht.EmptyList, 1583 ht.TOr(ht.TListOf(ht.TNonNegativeInt), ht.TListOf(_TDiskChanges)), 1584 "List of disk indexes (deprecated) or a list of tuples containing a disk" 1585 " index and a possibly empty dictionary with disk parameter changes"), 1586 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), 1587 "New instance nodes, if relocation is desired"), 1588 _PIAllocFromDesc("Iallocator for deciding new nodes"), 1589 ] 1590 OP_RESULT = ht.TNone
1591
1592 1593 -class OpInstanceQuery(OpCode):
1594 """Compute the list of instances.""" 1595 OP_PARAMS = [ 1596 _POutputFields, 1597 _PUseLocking, 1598 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), 1599 "Empty list to query all instances, instance names otherwise"), 1600 ] 1601 OP_RESULT = _TOldQueryResult
1602
1603 1604 -class OpInstanceQueryData(OpCode):
1605 """Compute the run-time status of instances.""" 1606 OP_PARAMS = [ 1607 _PUseLocking, 1608 ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), 1609 "Instance names"), 1610 ("static", False, ht.TBool, 1611 "Whether to only return configuration data without querying" 1612 " nodes"), 1613 ] 1614 OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TDict)
1615
1616 1617 -def _TestInstSetParamsModList(fn):
1618 """Generates a check for modification lists. 1619 1620 """ 1621 # Old format 1622 # TODO: Remove in version 2.8 including support in LUInstanceSetParams 1623 old_mod_item_fn = \ 1624 ht.TAnd(ht.TIsLength(2), ht.TItems([ 1625 ht.TOr(ht.TElemOf(constants.DDMS_VALUES), ht.TNonNegativeInt), 1626 fn, 1627 ])) 1628 1629 # New format, supporting adding/removing disks/NICs at arbitrary indices 1630 mod_item_fn = \ 1631 ht.TAnd(ht.TIsLength(3), ht.TItems([ 1632 ht.TElemOf(constants.DDMS_VALUES_WITH_MODIFY), 1633 ht.Comment("Disk index, can be negative, e.g. -1 for last disk")(ht.TInt), 1634 fn, 1635 ])) 1636 1637 return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)), 1638 ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn)))
1639
1640 1641 -class OpInstanceSetParams(OpCode):
1642 """Change the parameters of an instance. 1643 1644 """ 1645 TestNicModifications = _TestInstSetParamsModList(_TestNicDef) 1646 TestDiskModifications = _TestInstSetParamsModList(_TDiskParams) 1647 1648 OP_DSC_FIELD = "instance_name" 1649 OP_PARAMS = [ 1650 _PInstanceName, 1651 _PForce, 1652 _PForceVariant, 1653 _PIgnoreIpolicy, 1654 ("nics", ht.EmptyList, TestNicModifications, 1655 "List of NIC changes: each item is of the form ``(op, index, settings)``," 1656 " ``op`` is one of ``%s``, ``%s`` or ``%s``, ``index`` can be either -1" 1657 " to refer to the last position, or a zero-based index number; a" 1658 " deprecated version of this parameter used the form ``(op, settings)``," 1659 " where ``op`` can be ``%s`` to add a new NIC with the specified" 1660 " settings, ``%s`` to remove the last NIC or a number to modify the" 1661 " settings of the NIC with that index" % 1662 (constants.DDM_ADD, constants.DDM_MODIFY, constants.DDM_REMOVE, 1663 constants.DDM_ADD, constants.DDM_REMOVE)), 1664 ("disks", ht.EmptyList, TestDiskModifications, 1665 "List of disk changes; see ``nics``"), 1666 ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"), 1667 ("runtime_mem", None, ht.TMaybePositiveInt, "New runtime memory"), 1668 ("hvparams", ht.EmptyDict, ht.TDict, 1669 "Per-instance hypervisor parameters, hypervisor-dependent"), 1670 ("disk_template", None, ht.TMaybe(_BuildDiskTemplateCheck(False)), 1671 "Disk template for instance"), 1672 ("remote_node", None, ht.TMaybeString, 1673 "Secondary node (used when changing disk template)"), 1674 ("os_name", None, ht.TMaybeString, 1675 "Change the instance's OS without reinstalling the instance"), 1676 ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"), 1677 ("wait_for_sync", True, ht.TBool, 1678 "Whether to wait for the disk to synchronize, when changing template"), 1679 ("offline", None, ht.TMaybeBool, "Whether to mark instance as offline"), 1680 ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"), 1681 ] 1682 OP_RESULT = _TSetParamsResult
1683
1684 1685 -class OpInstanceGrowDisk(OpCode):
1686 """Grow a disk of an instance.""" 1687 OP_DSC_FIELD = "instance_name" 1688 OP_PARAMS = [ 1689 _PInstanceName, 1690 _PWaitForSync, 1691 ("disk", ht.NoDefault, ht.TInt, "Disk index"), 1692 ("amount", ht.NoDefault, ht.TNonNegativeInt, 1693 "Amount of disk space to add (megabytes)"), 1694 ("absolute", False, ht.TBool, 1695 "Whether the amount parameter is an absolute target or a relative one"), 1696 ] 1697 OP_RESULT = ht.TNone
1698
1699 1700 -class OpInstanceChangeGroup(OpCode):
1701 """Moves an instance to another node group.""" 1702 OP_DSC_FIELD = "instance_name" 1703 OP_PARAMS = [ 1704 _PInstanceName, 1705 _PEarlyRelease, 1706 _PIAllocFromDesc("Iallocator for computing solution"), 1707 _PTargetGroups, 1708 ] 1709 OP_RESULT = TJobIdListOnly
1710
1711 1712 # Node group opcodes 1713 1714 -class OpGroupAdd(OpCode):
1715 """Add a node group to the cluster.""" 1716 OP_DSC_FIELD = "group_name" 1717 OP_PARAMS = [ 1718 _PGroupName, 1719 _PNodeGroupAllocPolicy, 1720 _PGroupNodeParams, 1721 _PDiskParams, 1722 _PHvState, 1723 _PDiskState, 1724 ("ipolicy", None, ht.TMaybeDict, 1725 "Group-wide :ref:`instance policy <rapi-ipolicy>` specs"), 1726 ] 1727 OP_RESULT = ht.TNone
1728
1729 1730 -class OpGroupAssignNodes(OpCode):
1731 """Assign nodes to a node group.""" 1732 OP_DSC_FIELD = "group_name" 1733 OP_PARAMS = [ 1734 _PGroupName, 1735 _PForce, 1736 ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), 1737 "List of nodes to assign"), 1738 ] 1739 OP_RESULT = ht.TNone
1740
1741 1742 -class OpGroupQuery(OpCode):
1743 """Compute the list of node groups.""" 1744 OP_PARAMS = [ 1745 _POutputFields, 1746 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), 1747 "Empty list to query all groups, group names otherwise"), 1748 ] 1749 OP_RESULT = _TOldQueryResult
1750
1751 1752 -class OpGroupSetParams(OpCode):
1753 """Change the parameters of a node group.""" 1754 OP_DSC_FIELD = "group_name" 1755 OP_PARAMS = [ 1756 _PGroupName, 1757 _PNodeGroupAllocPolicy, 1758 _PGroupNodeParams, 1759 _PDiskParams, 1760 _PHvState, 1761 _PDiskState, 1762 ("ipolicy", None, ht.TMaybeDict, "Group-wide instance policy specs"), 1763 ] 1764 OP_RESULT = _TSetParamsResult
1765
1766 1767 -class OpGroupRemove(OpCode):
1768 """Remove a node group from the cluster.""" 1769 OP_DSC_FIELD = "group_name" 1770 OP_PARAMS = [ 1771 _PGroupName, 1772 ] 1773 OP_RESULT = ht.TNone
1774
1775 1776 -class OpGroupRename(OpCode):
1777 """Rename a node group in the cluster.""" 1778 OP_PARAMS = [ 1779 _PGroupName, 1780 ("new_name", ht.NoDefault, ht.TNonEmptyString, "New group name"), 1781 ] 1782 OP_RESULT = ht.Comment("New group name")(ht.TNonEmptyString)
1783
1784 1785 -class OpGroupEvacuate(OpCode):
1786 """Evacuate a node group in the cluster.""" 1787 OP_DSC_FIELD = "group_name" 1788 OP_PARAMS = [ 1789 _PGroupName, 1790 _PEarlyRelease, 1791 _PIAllocFromDesc("Iallocator for computing solution"), 1792 _PTargetGroups, 1793 ] 1794 OP_RESULT = TJobIdListOnly
1795
1796 1797 # OS opcodes 1798 -class OpOsDiagnose(OpCode):
1799 """Compute the list of guest operating systems.""" 1800 OP_PARAMS = [ 1801 _POutputFields, 1802 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), 1803 "Which operating systems to diagnose"), 1804 ] 1805 OP_RESULT = _TOldQueryResult
1806
1807 1808 # ExtStorage opcodes 1809 -class OpExtStorageDiagnose(OpCode):
1810 """Compute the list of external storage providers.""" 1811 OP_PARAMS = [ 1812 _POutputFields, 1813 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), 1814 "Which ExtStorage Provider to diagnose"), 1815 ] 1816 OP_RESULT = _TOldQueryResult
1817
1818 1819 # Exports opcodes 1820 -class OpBackupQuery(OpCode):
1821 """Compute the list of exported images.""" 1822 OP_PARAMS = [ 1823 _PUseLocking, 1824 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), 1825 "Empty list to query all nodes, node names otherwise"), 1826 ] 1827 OP_RESULT = ht.TDictOf(ht.TNonEmptyString, 1828 ht.TOr(ht.Comment("False on error")(ht.TBool), 1829 ht.TListOf(ht.TNonEmptyString)))
1830
1831 1832 -class OpBackupPrepare(OpCode):
1833 """Prepares an instance export. 1834 1835 @ivar instance_name: Instance name 1836 @ivar mode: Export mode (one of L{constants.EXPORT_MODES}) 1837 1838 """ 1839 OP_DSC_FIELD = "instance_name" 1840 OP_PARAMS = [ 1841 _PInstanceName, 1842 ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES), 1843 "Export mode"), 1844 ] 1845 OP_RESULT = ht.TMaybeDict
1846
1847 1848 -class OpBackupExport(OpCode):
1849 """Export an instance. 1850 1851 For local exports, the export destination is the node name. For 1852 remote exports, the export destination is a list of tuples, each 1853 consisting of hostname/IP address, port, magic, HMAC and HMAC 1854 salt. The HMAC is calculated using the cluster domain secret over 1855 the value "${index}:${hostname}:${port}". The destination X509 CA 1856 must be a signed certificate. 1857 1858 @ivar mode: Export mode (one of L{constants.EXPORT_MODES}) 1859 @ivar target_node: Export destination 1860 @ivar x509_key_name: X509 key to use (remote export only) 1861 @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export 1862 only) 1863 1864 """ 1865 OP_DSC_FIELD = "instance_name" 1866 OP_PARAMS = [ 1867 _PInstanceName, 1868 _PShutdownTimeout, 1869 # TODO: Rename target_node as it changes meaning for different export modes 1870 # (e.g. "destination") 1871 ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList), 1872 "Destination information, depends on export mode"), 1873 ("shutdown", True, ht.TBool, "Whether to shutdown instance before export"), 1874 ("remove_instance", False, ht.TBool, 1875 "Whether to remove instance after export"), 1876 ("ignore_remove_failures", False, ht.TBool, 1877 "Whether to ignore failures while removing instances"), 1878 ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES), 1879 "Export mode"), 1880 ("x509_key_name", None, ht.TMaybe(ht.TList), 1881 "Name of X509 key (remote export only)"), 1882 ("destination_x509_ca", None, ht.TMaybeString, 1883 "Destination X509 CA (remote export only)"), 1884 ] 1885 OP_RESULT = \ 1886 ht.TAnd(ht.TIsLength(2), ht.TItems([ 1887 ht.Comment("Finalizing status")(ht.TBool), 1888 ht.Comment("Status for every exported disk")(ht.TListOf(ht.TBool)), 1889 ]))
1890
1891 1892 -class OpBackupRemove(OpCode):
1893 """Remove an instance's export.""" 1894 OP_DSC_FIELD = "instance_name" 1895 OP_PARAMS = [ 1896 _PInstanceName, 1897 ] 1898 OP_RESULT = ht.TNone
1899
1900 1901 # Tags opcodes 1902 -class OpTagsGet(OpCode):
1903 """Returns the tags of the given object.""" 1904 OP_DSC_FIELD = "name" 1905