# # # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. """OpCodes module Note that this file is autogenerated using @src/hs2py@ with a header from @lib/opcodes.py.in_before@ and a footer from @lib/opcodes.py.in_after@. This module implements part of the data structures which define the cluster operations - the so-called opcodes. Every operation which modifies the cluster state is expressed via opcodes. """ # this are practically structures, so disable the message about too # few public methods: # pylint: disable=R0903 # pylint: disable=C0301 from ganeti import constants from ganeti import ht from ganeti import opcodes_base class OpCode(opcodes_base.BaseOpCode): """Abstract OpCode. This is the root of the actual OpCode hierarchy. All clases derived from this class should override OP_ID. @cvar OP_ID: The ID of this opcode. This should be unique amongst all children of this class. @cvar OP_DSC_FIELD: The name of a field whose value will be included in the string returned by Summary(); see the docstring of that method for details). @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if not present, then the field will be simply converted to string @cvar OP_PARAMS: List of opcode attributes, the default values they should get if not already defined, and types they must match. @cvar OP_RESULT: Callable to verify opcode result @cvar WITH_LU: Boolean that specifies whether this should be included in mcpu's dispatch table @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just the check steps @ivar priority: Opcode priority for queue """ # pylint: disable=E1101 # as OP_ID is dynamically defined WITH_LU = True OP_PARAMS = [ ("dry_run", None, ht.TMaybe(ht.TBool), "Run checks only, don't execute"), ("debug_level", None, ht.TMaybe(ht.TNonNegative(ht.TInt)), "Debug level"), ("priority", constants.OP_PRIO_DEFAULT, ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"), (opcodes_base.DEPEND_ATTR, None, opcodes_base.BuildJobDepCheck(True), "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)" " job IDs can be used; see :doc:`design document `" " for details"), (opcodes_base.COMMENT_ATTR, None, ht.TMaybe(ht.TString), "Comment describing the purpose of the opcode"), (constants.OPCODE_REASON, None, ht.TMaybe(ht.TListOf(ht.TAny)), "The reason trail, describing why the OpCode is executed"), ] OP_RESULT = None def __getstate__(self): """Specialized getstate for opcodes. This method adds to the state dictionary the OP_ID of the class, so that on unload we can identify the correct class for instantiating the opcode. @rtype: C{dict} @return: the state as a dictionary """ data = opcodes_base.BaseOpCode.__getstate__(self) data["OP_ID"] = self.OP_ID return data @classmethod def LoadOpCode(cls, data): """Generic load opcode method. The method identifies the correct opcode class from the dict-form by looking for a OP_ID key, if this is not found, or its value is not available in this module as a child of this class, we fail. @type data: C{dict} @param data: the serialized opcode """ if not isinstance(data, dict): raise ValueError("Invalid data to LoadOpCode (%s)" % type(data)) if "OP_ID" not in data: raise ValueError("Invalid data to LoadOpcode, missing OP_ID") op_id = data["OP_ID"] op_class = None if op_id in OP_MAPPING: op_class = OP_MAPPING[op_id] else: raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" % op_id) op = op_class() new_data = data.copy() del new_data["OP_ID"] op.__setstate__(new_data) return op def Summary(self): """Generates a summary description of this opcode. The summary is the value of the OP_ID attribute (without the "OP_" prefix), plus the value of the OP_DSC_FIELD attribute, if one was defined; this field should allow to easily identify the operation (for an instance creation job, e.g., it would be the instance name). """ assert self.OP_ID is not None and len(self.OP_ID) > 3 # all OP_ID start with OP_, we remove that txt = self.OP_ID[3:] field_name = getattr(self, "OP_DSC_FIELD", None) if field_name: field_value = getattr(self, field_name, None) field_formatter = getattr(self, "OP_DSC_FORMATTER", None) if callable(field_formatter): field_value = field_formatter(field_value) elif isinstance(field_value, (list, tuple)): field_value = ",".join(str(i) for i in field_value) txt = "%s(%s)" % (txt, field_value) return txt def TinySummary(self): """Generates a compact summary description of the opcode. """ assert self.OP_ID.startswith("OP_") text = self.OP_ID[3:] for (prefix, supplement) in opcodes_base.SUMMARY_PREFIX.items(): if text.startswith(prefix): return supplement + text[len(prefix):] return text class OpInstanceMultiAllocBase(OpCode): """Allocates multiple instances. """ def __getstate__(self): """Generic serializer. """ state = OpCode.__getstate__(self) if hasattr(self, "instances"): # pylint: disable=E1101 state["instances"] = [inst.__getstate__() for inst in self.instances] return state def __setstate__(self, state): """Generic unserializer. This method just restores from the serialized state the attributes of the current instance. @param state: the serialized opcode data @type state: C{dict} """ if not isinstance(state, dict): raise ValueError("Invalid data to __setstate__: expected dict, got %s" % type(state)) if "instances" in state: state["instances"] = map(OpCode.LoadOpCode, state["instances"]) return OpCode.__setstate__(self, state) def Validate(self, set_defaults): """Validates this opcode. We do this recursively. """ OpCode.Validate(self, set_defaults) for inst in self.instances: # pylint: disable=E1101 inst.Validate(set_defaults)