Revision 32683096
b/Makefile.am | ||
---|---|---|
225 | 225 |
lib/mcpu.py \ |
226 | 226 |
lib/netutils.py \ |
227 | 227 |
lib/objects.py \ |
228 |
lib/objectutils.py \ |
|
228 | 229 |
lib/opcodes.py \ |
229 | 230 |
lib/ovf.py \ |
230 | 231 |
lib/qlang.py \ |
... | ... | |
877 | 878 |
test/ganeti.mcpu_unittest.py \ |
878 | 879 |
test/ganeti.netutils_unittest.py \ |
879 | 880 |
test/ganeti.objects_unittest.py \ |
881 |
test/ganeti.objectutils_unittest.py \ |
|
880 | 882 |
test/ganeti.opcodes_unittest.py \ |
881 | 883 |
test/ganeti.ovf_unittest.py \ |
882 | 884 |
test/ganeti.qlang_unittest.py \ |
b/lib/objects.py | ||
---|---|---|
44 | 44 |
from ganeti import errors |
45 | 45 |
from ganeti import constants |
46 | 46 |
from ganeti import netutils |
47 |
from ganeti import objectutils |
|
47 | 48 |
from ganeti import utils |
48 | 49 |
|
49 | 50 |
from socket import AF_INET |
... | ... | |
191 | 192 |
]) |
192 | 193 |
|
193 | 194 |
|
194 |
class ConfigObject(object): |
|
195 |
class ConfigObject(objectutils.ValidatedSlots):
|
|
195 | 196 |
"""A generic config object. |
196 | 197 |
|
197 | 198 |
It has the following properties: |
... | ... | |
206 | 207 |
""" |
207 | 208 |
__slots__ = [] |
208 | 209 |
|
209 |
def __init__(self, **kwargs): |
|
210 |
for k, v in kwargs.iteritems(): |
|
211 |
setattr(self, k, v) |
|
212 |
|
|
213 | 210 |
def __getattr__(self, name): |
214 |
if name not in self._all_slots():
|
|
211 |
if name not in self.GetAllSlots():
|
|
215 | 212 |
raise AttributeError("Invalid object attribute %s.%s" % |
216 | 213 |
(type(self).__name__, name)) |
217 | 214 |
return None |
218 | 215 |
|
219 | 216 |
def __setstate__(self, state): |
220 |
slots = self._all_slots()
|
|
217 |
slots = self.GetAllSlots()
|
|
221 | 218 |
for name in state: |
222 | 219 |
if name in slots: |
223 | 220 |
setattr(self, name, state[name]) |
224 | 221 |
|
225 |
@classmethod |
|
226 |
def _all_slots(cls): |
|
227 |
"""Compute the list of all declared slots for a class. |
|
222 |
def Validate(self): |
|
223 |
"""Validates the slots. |
|
228 | 224 |
|
229 | 225 |
""" |
230 |
slots = [] |
|
231 |
for parent in cls.__mro__: |
|
232 |
slots.extend(getattr(parent, "__slots__", [])) |
|
233 |
return slots |
|
234 |
|
|
235 |
#: Public getter for the defined slots |
|
236 |
GetAllSlots = _all_slots |
|
237 | 226 |
|
238 | 227 |
def ToDict(self): |
239 | 228 |
"""Convert to a dict holding only standard python types. |
... | ... | |
246 | 235 |
|
247 | 236 |
""" |
248 | 237 |
result = {} |
249 |
for name in self._all_slots():
|
|
238 |
for name in self.GetAllSlots():
|
|
250 | 239 |
value = getattr(self, name, None) |
251 | 240 |
if value is not None: |
252 | 241 |
result[name] = value |
b/lib/objectutils.py | ||
---|---|---|
1 |
# |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2012 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 |
"""Module for object related utils.""" |
|
22 |
|
|
23 |
|
|
24 |
class AutoSlots(type): |
|
25 |
"""Meta base class for __slots__ definitions. |
|
26 |
|
|
27 |
""" |
|
28 |
def __new__(mcs, name, bases, attrs): |
|
29 |
"""Called when a class should be created. |
|
30 |
|
|
31 |
@param mcs: The meta class |
|
32 |
@param name: Name of created class |
|
33 |
@param bases: Base classes |
|
34 |
@type attrs: dict |
|
35 |
@param attrs: Class attributes |
|
36 |
|
|
37 |
""" |
|
38 |
assert "__slots__" not in attrs, \ |
|
39 |
"Class '%s' defines __slots__ when it should not" % name |
|
40 |
|
|
41 |
attrs["__slots__"] = mcs._GetSlots(attrs) |
|
42 |
|
|
43 |
return type.__new__(mcs, name, bases, attrs) |
|
44 |
|
|
45 |
@classmethod |
|
46 |
def _GetSlots(mcs, attrs): |
|
47 |
"""Used to get the list of defined slots. |
|
48 |
|
|
49 |
@param attrs: The attributes of the class |
|
50 |
|
|
51 |
""" |
|
52 |
raise NotImplementedError |
|
53 |
|
|
54 |
|
|
55 |
class ValidatedSlots(object): |
|
56 |
"""Sets and validates slots. |
|
57 |
|
|
58 |
""" |
|
59 |
__slots__ = [] |
|
60 |
|
|
61 |
def __init__(self, **kwargs): |
|
62 |
"""Constructor for BaseOpCode. |
|
63 |
|
|
64 |
The constructor takes only keyword arguments and will set |
|
65 |
attributes on this object based on the passed arguments. As such, |
|
66 |
it means that you should not pass arguments which are not in the |
|
67 |
__slots__ attribute for this class. |
|
68 |
|
|
69 |
""" |
|
70 |
slots = self.GetAllSlots() |
|
71 |
for (key, value) in kwargs.items(): |
|
72 |
if key not in slots: |
|
73 |
raise TypeError("Object %s doesn't support the parameter '%s'" % |
|
74 |
(self.__class__.__name__, key)) |
|
75 |
setattr(self, key, value) |
|
76 |
|
|
77 |
@classmethod |
|
78 |
def GetAllSlots(cls): |
|
79 |
"""Compute the list of all declared slots for a class. |
|
80 |
|
|
81 |
""" |
|
82 |
slots = [] |
|
83 |
for parent in cls.__mro__: |
|
84 |
slots.extend(getattr(parent, "__slots__", [])) |
|
85 |
return slots |
|
86 |
|
|
87 |
def Validate(self): |
|
88 |
"""Validates the slots. |
|
89 |
|
|
90 |
This method must be implemented by the child classes. |
|
91 |
|
|
92 |
""" |
|
93 |
raise NotImplementedError |
b/lib/opcodes.py | ||
---|---|---|
40 | 40 |
from ganeti import errors |
41 | 41 |
from ganeti import ht |
42 | 42 |
from ganeti import objects |
43 |
from ganeti import objectutils |
|
43 | 44 |
|
44 | 45 |
|
45 | 46 |
# Common opcode attributes |
... | ... | |
342 | 343 |
"Storage type") |
343 | 344 |
|
344 | 345 |
|
345 |
class _AutoOpParamSlots(type):
|
|
346 |
class _AutoOpParamSlots(objectutils.AutoSlots):
|
|
346 | 347 |
"""Meta class for opcode definitions. |
347 | 348 |
|
348 | 349 |
""" |
... | ... | |
356 | 357 |
@param attrs: Class attributes |
357 | 358 |
|
358 | 359 |
""" |
359 |
assert "__slots__" not in attrs, \ |
|
360 |
"Class '%s' defines __slots__ when it should use OP_PARAMS" % name |
|
361 | 360 |
assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name |
362 | 361 |
|
362 |
slots = mcs._GetSlots(attrs) |
|
363 |
assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ |
|
364 |
"Class '%s' uses unknown field in OP_DSC_FIELD" % name |
|
365 |
|
|
363 | 366 |
attrs["OP_ID"] = _NameToId(name) |
364 | 367 |
|
368 |
return objectutils.AutoSlots.__new__(mcs, name, bases, attrs) |
|
369 |
|
|
370 |
@classmethod |
|
371 |
def _GetSlots(mcs, attrs): |
|
372 |
"""Build the slots out of OP_PARAMS. |
|
373 |
|
|
374 |
""" |
|
365 | 375 |
# Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams |
366 | 376 |
params = attrs.setdefault("OP_PARAMS", []) |
367 | 377 |
|
368 | 378 |
# Use parameter names as slots |
369 |
slots = [pname for (pname, _, _, _) in params] |
|
370 |
|
|
371 |
assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ |
|
372 |
"Class '%s' uses unknown field in OP_DSC_FIELD" % name |
|
373 |
|
|
374 |
attrs["__slots__"] = slots |
|
375 |
|
|
376 |
return type.__new__(mcs, name, bases, attrs) |
|
379 |
return [pname for (pname, _, _, _) in params] |
|
377 | 380 |
|
378 | 381 |
|
379 |
class BaseOpCode(object): |
|
382 |
class BaseOpCode(objectutils.ValidatedSlots):
|
|
380 | 383 |
"""A simple serializable object. |
381 | 384 |
|
382 | 385 |
This object serves as a parent class for OpCode without any custom |
... | ... | |
387 | 390 |
# as OP_ID is dynamically defined |
388 | 391 |
__metaclass__ = _AutoOpParamSlots |
389 | 392 |
|
390 |
def __init__(self, **kwargs): |
|
391 |
"""Constructor for BaseOpCode. |
|
392 |
|
|
393 |
The constructor takes only keyword arguments and will set |
|
394 |
attributes on this object based on the passed arguments. As such, |
|
395 |
it means that you should not pass arguments which are not in the |
|
396 |
__slots__ attribute for this class. |
|
397 |
|
|
398 |
""" |
|
399 |
slots = self._all_slots() |
|
400 |
for key in kwargs: |
|
401 |
if key not in slots: |
|
402 |
raise TypeError("Object %s doesn't support the parameter '%s'" % |
|
403 |
(self.__class__.__name__, key)) |
|
404 |
setattr(self, key, kwargs[key]) |
|
405 |
|
|
406 | 393 |
def __getstate__(self): |
407 | 394 |
"""Generic serializer. |
408 | 395 |
|
... | ... | |
414 | 401 |
|
415 | 402 |
""" |
416 | 403 |
state = {} |
417 |
for name in self._all_slots():
|
|
404 |
for name in self.GetAllSlots():
|
|
418 | 405 |
if hasattr(self, name): |
419 | 406 |
state[name] = getattr(self, name) |
420 | 407 |
return state |
... | ... | |
433 | 420 |
raise ValueError("Invalid data to __setstate__: expected dict, got %s" % |
434 | 421 |
type(state)) |
435 | 422 |
|
436 |
for name in self._all_slots():
|
|
423 |
for name in self.GetAllSlots():
|
|
437 | 424 |
if name not in state and hasattr(self, name): |
438 | 425 |
delattr(self, name) |
439 | 426 |
|
... | ... | |
441 | 428 |
setattr(self, name, state[name]) |
442 | 429 |
|
443 | 430 |
@classmethod |
444 |
def _all_slots(cls): |
|
445 |
"""Compute the list of all declared slots for a class. |
|
446 |
|
|
447 |
""" |
|
448 |
slots = [] |
|
449 |
for parent in cls.__mro__: |
|
450 |
slots.extend(getattr(parent, "__slots__", [])) |
|
451 |
return slots |
|
452 |
|
|
453 |
@classmethod |
|
454 | 431 |
def GetAllParams(cls): |
455 | 432 |
"""Compute list of all parameters for an opcode. |
456 | 433 |
|
... | ... | |
460 | 437 |
slots.extend(getattr(parent, "OP_PARAMS", [])) |
461 | 438 |
return slots |
462 | 439 |
|
463 |
def Validate(self, set_defaults): |
|
440 |
def Validate(self, set_defaults): # pylint: disable=W0221
|
|
464 | 441 |
"""Validate opcode parameters, optionally setting default values. |
465 | 442 |
|
466 | 443 |
@type set_defaults: bool |
b/test/ganeti.objectutils_unittest.py | ||
---|---|---|
1 |
#!/usr/bin/python |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2012 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 |
"""Script for unittesting the objectutils module""" |
|
23 |
|
|
24 |
|
|
25 |
import unittest |
|
26 |
|
|
27 |
from ganeti import objectutils |
|
28 |
|
|
29 |
import testutils |
|
30 |
|
|
31 |
|
|
32 |
class SlotsAutoSlot(objectutils.AutoSlots): |
|
33 |
@classmethod |
|
34 |
def _GetSlots(mcs, attr): |
|
35 |
return attr["SLOTS"] |
|
36 |
|
|
37 |
|
|
38 |
class AutoSlotted(object): |
|
39 |
__metaclass__ = SlotsAutoSlot |
|
40 |
|
|
41 |
SLOTS = ["foo", "bar", "baz"] |
|
42 |
|
|
43 |
|
|
44 |
class TestAutoSlot(unittest.TestCase): |
|
45 |
def test(self): |
|
46 |
slotted = AutoSlotted() |
|
47 |
self.assertEqual(slotted.__slots__, AutoSlotted.SLOTS) |
|
48 |
|
|
49 |
if __name__ == "__main__": |
|
50 |
testutils.GanetiTestProgram() |
b/test/ganeti.opcodes_unittest.py | ||
---|---|---|
77 | 77 |
{"dry_run": False, "debug_level": 0, }, |
78 | 78 |
|
79 | 79 |
# All variables |
80 |
dict([(name, False) for name in cls._all_slots()])
|
|
80 |
dict([(name, False) for name in cls.GetAllSlots()])
|
|
81 | 81 |
] |
82 | 82 |
|
83 | 83 |
for i in args: |
... | ... | |
95 | 95 |
self._checkSummary(restored) |
96 | 96 |
|
97 | 97 |
for name in ["x_y_z", "hello_world"]: |
98 |
assert name not in cls._all_slots()
|
|
98 |
assert name not in cls.GetAllSlots()
|
|
99 | 99 |
for value in [None, True, False, [], "Hello World"]: |
100 | 100 |
self.assertRaises(AttributeError, setattr, op, name, value) |
101 | 101 |
|
... | ... | |
158 | 158 |
self.assertTrue(opcodes.OpCode not in opcodes.OP_MAPPING.values()) |
159 | 159 |
|
160 | 160 |
for cls in opcodes.OP_MAPPING.values() + [opcodes.OpCode]: |
161 |
all_slots = cls._all_slots()
|
|
161 |
all_slots = cls.GetAllSlots()
|
|
162 | 162 |
|
163 | 163 |
self.assertEqual(len(set(all_slots) & supported_by_all), 3, |
164 | 164 |
msg=("Opcode %s doesn't support all base" |
b/tools/cfgshell | ||
---|---|---|
95 | 95 |
if isinstance(obj, objects.ConfigObject): |
96 | 96 |
# pylint: disable=W0212 |
97 | 97 |
# yes, we're using a protected member |
98 |
for name in obj._all_slots():
|
|
98 |
for name in obj.GetAllSlots():
|
|
99 | 99 |
child = getattr(obj, name, None) |
100 | 100 |
if isinstance(child, (list, dict, tuple, objects.ConfigObject)): |
101 | 101 |
dirs.append(name) |
Also available in: Unified diff