root / lib / opcodes.py.in_before @ 28d466dc
History | View | Annotate | Download (6.9 kB)
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 |
Note that this file is autogenerated using @src/hs2py@ with a header |
25 |
from @lib/opcodes.py.in_before@ and a footer from @lib/opcodes.py.in_after@. |
26 |
|
27 |
This module implements part of the data structures which define the |
28 |
cluster operations - the so-called opcodes. |
29 |
|
30 |
Every operation which modifies the cluster state is expressed via |
31 |
opcodes. |
32 |
|
33 |
""" |
34 |
|
35 |
# this are practically structures, so disable the message about too |
36 |
# few public methods: |
37 |
# pylint: disable=R0903 |
38 |
# pylint: disable=C0301 |
39 |
|
40 |
from ganeti import constants |
41 |
from ganeti import ht |
42 |
|
43 |
from ganeti import opcodes_base |
44 |
|
45 |
|
46 |
class OpCode(opcodes_base.BaseOpCode): |
47 |
"""Abstract OpCode. |
48 |
|
49 |
This is the root of the actual OpCode hierarchy. All clases derived |
50 |
from this class should override OP_ID. |
51 |
|
52 |
@cvar OP_ID: The ID of this opcode. This should be unique amongst all |
53 |
children of this class. |
54 |
@cvar OP_DSC_FIELD: The name of a field whose value will be included in the |
55 |
string returned by Summary(); see the docstring of that |
56 |
method for details). |
57 |
@cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if |
58 |
not present, then the field will be simply converted |
59 |
to string |
60 |
@cvar OP_PARAMS: List of opcode attributes, the default values they should |
61 |
get if not already defined, and types they must match. |
62 |
@cvar OP_RESULT: Callable to verify opcode result |
63 |
@cvar WITH_LU: Boolean that specifies whether this should be included in |
64 |
mcpu's dispatch table |
65 |
@ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just |
66 |
the check steps |
67 |
@ivar priority: Opcode priority for queue |
68 |
|
69 |
""" |
70 |
# pylint: disable=E1101 |
71 |
# as OP_ID is dynamically defined |
72 |
WITH_LU = True |
73 |
OP_PARAMS = [ |
74 |
("dry_run", None, ht.TMaybe(ht.TBool), "Run checks only, don't execute"), |
75 |
("debug_level", None, ht.TMaybe(ht.TNonNegative(ht.TInt)), "Debug level"), |
76 |
("priority", constants.OP_PRIO_DEFAULT, |
77 |
ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"), |
78 |
(opcodes_base.DEPEND_ATTR, None, opcodes_base.BuildJobDepCheck(True), |
79 |
"Job dependencies; if used through ``SubmitManyJobs`` relative (negative)" |
80 |
" job IDs can be used; see :doc:`design document <design-chained-jobs>`" |
81 |
" for details"), |
82 |
(opcodes_base.COMMENT_ATTR, None, ht.TMaybe(ht.TString), |
83 |
"Comment describing the purpose of the opcode"), |
84 |
(constants.OPCODE_REASON, [], ht.TMaybe(ht.TListOf(ht.TAny)), |
85 |
"The reason trail, describing why the OpCode is executed"), |
86 |
] |
87 |
OP_RESULT = None |
88 |
|
89 |
def __getstate__(self): |
90 |
"""Specialized getstate for opcodes. |
91 |
|
92 |
This method adds to the state dictionary the OP_ID of the class, |
93 |
so that on unload we can identify the correct class for |
94 |
instantiating the opcode. |
95 |
|
96 |
@rtype: C{dict} |
97 |
@return: the state as a dictionary |
98 |
|
99 |
""" |
100 |
data = opcodes_base.BaseOpCode.__getstate__(self) |
101 |
data["OP_ID"] = self.OP_ID |
102 |
return data |
103 |
|
104 |
@classmethod |
105 |
def LoadOpCode(cls, data): |
106 |
"""Generic load opcode method. |
107 |
|
108 |
The method identifies the correct opcode class from the dict-form |
109 |
by looking for a OP_ID key, if this is not found, or its value is |
110 |
not available in this module as a child of this class, we fail. |
111 |
|
112 |
@type data: C{dict} |
113 |
@param data: the serialized opcode |
114 |
|
115 |
""" |
116 |
if not isinstance(data, dict): |
117 |
raise ValueError("Invalid data to LoadOpCode (%s)" % type(data)) |
118 |
if "OP_ID" not in data: |
119 |
raise ValueError("Invalid data to LoadOpcode, missing OP_ID") |
120 |
op_id = data["OP_ID"] |
121 |
op_class = None |
122 |
if op_id in OP_MAPPING: |
123 |
op_class = OP_MAPPING[op_id] |
124 |
else: |
125 |
raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" % |
126 |
op_id) |
127 |
op = op_class() |
128 |
new_data = data.copy() |
129 |
del new_data["OP_ID"] |
130 |
op.__setstate__(new_data) |
131 |
return op |
132 |
|
133 |
def Summary(self): |
134 |
"""Generates a summary description of this opcode. |
135 |
|
136 |
The summary is the value of the OP_ID attribute (without the "OP_" |
137 |
prefix), plus the value of the OP_DSC_FIELD attribute, if one was |
138 |
defined; this field should allow to easily identify the operation |
139 |
(for an instance creation job, e.g., it would be the instance |
140 |
name). |
141 |
|
142 |
""" |
143 |
assert self.OP_ID is not None and len(self.OP_ID) > 3 |
144 |
# all OP_ID start with OP_, we remove that |
145 |
txt = self.OP_ID[3:] |
146 |
field_name = getattr(self, "OP_DSC_FIELD", None) |
147 |
if field_name: |
148 |
field_value = getattr(self, field_name, None) |
149 |
field_formatter = getattr(self, "OP_DSC_FORMATTER", None) |
150 |
if callable(field_formatter): |
151 |
field_value = field_formatter(field_value) |
152 |
elif isinstance(field_value, (list, tuple)): |
153 |
field_value = ",".join(str(i) for i in field_value) |
154 |
txt = "%s(%s)" % (txt, field_value) |
155 |
return txt |
156 |
|
157 |
def TinySummary(self): |
158 |
"""Generates a compact summary description of the opcode. |
159 |
|
160 |
""" |
161 |
assert self.OP_ID.startswith("OP_") |
162 |
|
163 |
text = self.OP_ID[3:] |
164 |
|
165 |
for (prefix, supplement) in opcodes_base.SUMMARY_PREFIX.items(): |
166 |
if text.startswith(prefix): |
167 |
return supplement + text[len(prefix):] |
168 |
|
169 |
return text |
170 |
|
171 |
|
172 |
class OpInstanceMultiAllocBase(OpCode): |
173 |
"""Allocates multiple instances. |
174 |
|
175 |
""" |
176 |
def __getstate__(self): |
177 |
"""Generic serializer. |
178 |
|
179 |
""" |
180 |
state = OpCode.__getstate__(self) |
181 |
if hasattr(self, "instances"): |
182 |
# pylint: disable=E1101 |
183 |
state["instances"] = [inst.__getstate__() for inst in self.instances] |
184 |
return state |
185 |
|
186 |
def __setstate__(self, state): |
187 |
"""Generic unserializer. |
188 |
|
189 |
This method just restores from the serialized state the attributes |
190 |
of the current instance. |
191 |
|
192 |
@param state: the serialized opcode data |
193 |
@type state: C{dict} |
194 |
|
195 |
""" |
196 |
if not isinstance(state, dict): |
197 |
raise ValueError("Invalid data to __setstate__: expected dict, got %s" % |
198 |
type(state)) |
199 |
|
200 |
if "instances" in state: |
201 |
state["instances"] = map(OpCode.LoadOpCode, state["instances"]) |
202 |
|
203 |
return OpCode.__setstate__(self, state) |
204 |
|
205 |
def Validate(self, set_defaults): |
206 |
"""Validates this opcode. |
207 |
|
208 |
We do this recursively. |
209 |
|
210 |
""" |
211 |
OpCode.Validate(self, set_defaults) |
212 |
|
213 |
for inst in self.instances: # pylint: disable=E1101 |
214 |
inst.Validate(set_defaults) |