| # |
| # |
| |
| # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc. |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # 1. Redistributions of source code must retain the above copyright notice, |
| # this list of conditions and the following disclaimer. |
| # |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| |
| """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 <design-chained-jobs>`" |
| " for details"), |
| (opcodes_base.COMMENT_ATTR, None, ht.TMaybe(ht.TString), |
| "Comment describing the purpose of the opcode"), |
| (constants.OPCODE_REASON, [], 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) |