commit 36e1c7e840711b30fc60fd44cb0e47e4ee5c1133
Merge: d9a2252 473d87a
Author: Iustin Pop <[email protected]>
Date: Fri Feb 15 12:53:30 2013 +0100
Merge branch 'devel-2.7'
* devel-2.7:
Rename lib/objectutils to outils.py
Fix typo in gnt-group manpage
Fix wrong type in a docstring of the RAPI subsystem
Finish the remote→restricted commands rename
Enable use of the priority option in hbal
Add CLI-level option to override the priority
Add functions to parse CLI-level format of priorities
Add a function to change an OpCode's priority
Make hbal opcode annotation more generic
Add unit tests for RADOSBLockDevice
Fix rbd showmapped output parsing
Change default xen root path to /dev/xvda1
Removes check for conflicts from NetworkDisconnect
If _UnlockedLookupNetwork() fails raise error
Force conflicts check in LUNetworkDisconnect
Also updated objects.py with more outils renames.
Signed-off-by: Iustin Pop <[email protected]>
diff --cc lib/objects.py
index 6340d8d,588ef03..fa811a6
--- a/lib/objects.py
+++ b/lib/objects.py
@@@ -406,7 -447,7 +406,7 @@@ class ConfigData(ConfigObject)
mydict = super(ConfigData, self).ToDict()
mydict["cluster"] = mydict["cluster"].ToDict()
for key in "nodes", "instances", "nodegroups", "networks":
- mydict[key] = objectutils.ContainerToDicts(mydict[key])
- mydict[key] = self._ContainerToDicts(mydict[key])
++ mydict[key] = outils.ContainerToDicts(mydict[key])
return mydict
@@@ -417,12 -458,10 +417,12 @@@
"""
obj = super(ConfigData, cls).FromDict(val)
obj.cluster = Cluster.FromDict(obj.cluster)
- obj.nodes = objectutils.ContainerFromDicts(obj.nodes, dict, Node)
- obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
- obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
- obj.nodegroups = cls._ContainerFromDicts(obj.nodegroups, dict, NodeGroup)
- obj.networks = cls._ContainerFromDicts(obj.networks, dict, Network)
++ obj.nodes = outils.ContainerFromDicts(obj.nodes, dict, Node)
+ obj.instances = \
- objectutils.ContainerFromDicts(obj.instances, dict, Instance)
++ outils.ContainerFromDicts(obj.instances, dict, Instance)
+ obj.nodegroups = \
- objectutils.ContainerFromDicts(obj.nodegroups, dict, NodeGroup)
- obj.networks = objectutils.ContainerFromDicts(obj.networks, dict, Network)
++ outils.ContainerFromDicts(obj.nodegroups, dict, NodeGroup)
++ obj.networks = outils.ContainerFromDicts(obj.networks, dict, Network)
return obj
def HasAnyDiskOfType(self, dev_type):
@@@ -732,7 -771,7 +732,7 @@@ class Disk(ConfigObject)
for attr in ("children",):
alist = bo.get(attr, None)
if alist:
- bo[attr] = objectutils.ContainerToDicts(alist)
- bo[attr] = self._ContainerToDicts(alist)
++ bo[attr] = outils.ContainerToDicts(alist)
return bo
@classmethod
@@@ -742,7 -781,7 +742,7 @@@
"""
obj = super(Disk, cls).FromDict(val)
if obj.children:
- obj.children = objectutils.ContainerFromDicts(obj.children, list, Disk)
- obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
++ obj.children = outils.ContainerFromDicts(obj.children, list, Disk)
if obj.logical_id and isinstance(obj.logical_id, list):
obj.logical_id = tuple(obj.logical_id)
if obj.physical_id and isinstance(obj.physical_id, list):
@@@ -1100,7 -1139,7 +1100,7 @@@ class Instance(TaggableObject)
for attr in "nics", "disks":
alist = bo.get(attr, None)
if alist:
- nlist = objectutils.ContainerToDicts(alist)
- nlist = self._ContainerToDicts(alist)
++ nlist = outils.ContainerToDicts(alist)
else:
nlist = []
bo[attr] = nlist
@@@ -1119,8 -1158,8 +1119,8 @@@
if "admin_up" in val:
del val["admin_up"]
obj = super(Instance, cls).FromDict(val)
- obj.nics = objectutils.ContainerFromDicts(obj.nics, list, NIC)
- obj.disks = objectutils.ContainerFromDicts(obj.disks, list, Disk)
- obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
- obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
++ obj.nics = outils.ContainerFromDicts(obj.nics, list, NIC)
++ obj.disks = outils.ContainerFromDicts(obj.disks, list, Disk)
return obj
def UpgradeConfig(self):
@@@ -1314,12 -1353,12 +1314,12 @@@ class Node(TaggableObject)
hv_state = data.get("hv_state", None)
if hv_state is not None:
- data["hv_state"] = objectutils.ContainerToDicts(hv_state)
- data["hv_state"] = self._ContainerToDicts(hv_state)
++ data["hv_state"] = outils.ContainerToDicts(hv_state)
disk_state = data.get("disk_state", None)
if disk_state is not None:
data["disk_state"] = \
- dict((key, objectutils.ContainerToDicts(value))
- dict((key, self._ContainerToDicts(value))
++ dict((key, outils.ContainerToDicts(value))
for (key, value) in disk_state.items())
return data
@@@ -1332,12 -1371,11 +1332,12 @@@
obj = super(Node, cls).FromDict(val)
if obj.hv_state is not None:
- obj.hv_state = cls._ContainerFromDicts(obj.hv_state, dict, NodeHvState)
+ obj.hv_state = \
- objectutils.ContainerFromDicts(obj.hv_state, dict, NodeHvState)
++ outils.ContainerFromDicts(obj.hv_state, dict, NodeHvState)
if obj.disk_state is not None:
obj.disk_state = \
- dict((key, objectutils.ContainerFromDicts(value, dict, NodeDiskState))
- dict((key, cls._ContainerFromDicts(value, dict, NodeDiskState))
++ dict((key, outils.ContainerFromDicts(value, dict, NodeDiskState))
for (key, value) in obj.disk_state.items())
return obj
@@@ -1906,7 -1933,7 +1906,7 @@@ class _QueryResponseBase(ConfigObject)
"""
mydict = super(_QueryResponseBase, self).ToDict()
- mydict["fields"] = objectutils.ContainerToDicts(mydict["fields"])
- mydict["fields"] = self._ContainerToDicts(mydict["fields"])
++ mydict["fields"] = outils.ContainerToDicts(mydict["fields"])
return mydict
@classmethod
@@@ -1915,8 -1942,7 +1915,8 @@@
"""
obj = super(_QueryResponseBase, cls).FromDict(val)
- obj.fields = cls._ContainerFromDicts(obj.fields, list,
QueryFieldDefinition)
+ obj.fields = \
- objectutils.ContainerFromDicts(obj.fields, list, QueryFieldDefinition)
++ outils.ContainerFromDicts(obj.fields, list, QueryFieldDefinition)
return obj
diff --cc lib/outils.py
index 0000000,e2742b1..f0f6558
mode 000000,100644..100644
--- a/lib/outils.py
+++ b/lib/outils.py
@@@ -1,0 -1,93 +1,150 @@@
+ #
+ #
+
+ # Copyright (C) 2012 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.
+
+ """Module for object related utils."""
+
+
++#: Supported container types for serialization/de-serialization (must be a
++#: tuple as it's used as a parameter for C{isinstance})
++_SEQUENCE_TYPES = (list, tuple, set, frozenset)
++
++
+ class AutoSlots(type):
+ """Meta base class for __slots__ definitions.
+
+ """
+ def __new__(mcs, name, bases, attrs):
+ """Called when a class should be created.
+
+ @param mcs: The meta class
+ @param name: Name of created class
+ @param bases: Base classes
+ @type attrs: dict
+ @param attrs: Class attributes
+
+ """
+ assert "__slots__" not in attrs, \
+ "Class '%s' defines __slots__ when it should not" % name
+
+ attrs["__slots__"] = mcs._GetSlots(attrs)
+
+ return type.__new__(mcs, name, bases, attrs)
+
+ @classmethod
+ def _GetSlots(mcs, attrs):
+ """Used to get the list of defined slots.
+
+ @param attrs: The attributes of the class
+
+ """
+ raise NotImplementedError
+
+
+ class ValidatedSlots(object):
+ """Sets and validates slots.
+
+ """
+ __slots__ = []
+
+ def __init__(self, **kwargs):
+ """Constructor for BaseOpCode.
+
+ The constructor takes only keyword arguments and will set
+ attributes on this object based on the passed arguments. As such,
+ it means that you should not pass arguments which are not in the
+ __slots__ attribute for this class.
+
+ """
+ slots = self.GetAllSlots()
+ for (key, value) in kwargs.items():
+ if key not in slots:
+ raise TypeError("Object %s doesn't support the parameter '%s'" %
+ (self.__class__.__name__, key))
+ setattr(self, key, value)
+
+ @classmethod
+ def GetAllSlots(cls):
+ """Compute the list of all declared slots for a class.
+
+ """
+ slots = []
+ for parent in cls.__mro__:
+ slots.extend(getattr(parent, "__slots__", []))
+ return slots
+
+ def Validate(self):
+ """Validates the slots.
+
+ This method must be implemented by the child classes.
+
+ """
+ raise NotImplementedError
++
++
++def ContainerToDicts(container):
++ """Convert the elements of a container to standard Python types.
++
++ This method converts a container with elements to standard Python types. If
++ the input container is of the type C{dict}, only its values are touched.
++ Those values, as well as all elements of input sequences, must support a
++ C{ToDict} method returning a serialized version.
++
++ @type container: dict or sequence (see L{_SEQUENCE_TYPES})
++
++ """
++ if isinstance(container, dict):
++ ret = dict([(k, v.ToDict()) for k, v in container.items()])
++ elif isinstance(container, _SEQUENCE_TYPES):
++ ret = [elem.ToDict() for elem in container]
++ else:
++ raise TypeError("Unknown container type '%s'" % type(container))
++
++ return ret
++
++
++def ContainerFromDicts(source, c_type, e_type):
++ """Convert a container from standard python types.
++
++ This method converts a container with standard Python types to objects. If
++ the container is a dict, we don't touch the keys, only the values.
++
++ @type source: None, dict or sequence (see L{_SEQUENCE_TYPES})
++ @param source: Input data
++ @type c_type: type class
++ @param c_type: Desired type for returned container
++ @type e_type: element type class
++ @param e_type: Item type for elements in returned container (must have a
++ C{FromDict} class method)
++
++ """
++ if not isinstance(c_type, type):
++ raise TypeError("Container type '%s' is not a type" % type(c_type))
++
++ if source is None:
++ source = c_type()
++
++ if c_type is dict:
++ ret = dict([(k, e_type.FromDict(v)) for k, v in source.items()])
++ elif c_type in _SEQUENCE_TYPES:
++ ret = c_type(map(e_type.FromDict, source))
++ else:
++ raise TypeError("Unknown container type '%s'" % c_type)
++
++ return ret
diff --cc test/py/ganeti.outils_unittest.py
index 0000000,93f5212..22d2177
mode 000000,100755..100755
--- a/test/py/ganeti.outils_unittest.py
+++ b/test/py/ganeti.outils_unittest.py
@@@ -1,0 -1,50 +1,107 @@@
+ #!/usr/bin/python
+ #
+
+ # Copyright (C) 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.
+
+
+ """Script for unittesting the outils module"""
+
+
+ import unittest
+
+ from ganeti import outils
+
+ import testutils
+
+
+ class SlotsAutoSlot(outils.AutoSlots):
+ @classmethod
+ def _GetSlots(mcs, attr):
+ return attr["SLOTS"]
+
+
+ class AutoSlotted(object):
+ __metaclass__ = SlotsAutoSlot
+
+ SLOTS = ["foo", "bar", "baz"]
+
+
+ class TestAutoSlot(unittest.TestCase):
+ def test(self):
+ slotted = AutoSlotted()
+ self.assertEqual(slotted.__slots__, AutoSlotted.SLOTS)
+
++
++class TestContainerToDicts(unittest.TestCase):
++ def testUnknownType(self):
++ for value in [None, 19410, "xyz"]:
++ try:
++ outils.ContainerToDicts(value)
++ except TypeError, err:
++ self.assertTrue(str(err).startswith("Unknown container type"))
++ else:
++ self.fail("Exception was not raised")
++
++ def testEmptyDict(self):
++ value = {}
++ self.assertFalse(type(value) in outils._SEQUENCE_TYPES)
++ self.assertEqual(outils.ContainerToDicts(value), {})
++
++ def testEmptySequences(self):
++ for cls in [list, tuple, set, frozenset]:
++ self.assertEqual(outils.ContainerToDicts(cls()), [])
++
++
++class _FakeWithFromDict:
++ def FromDict(self, _):
++ raise NotImplemented
++
++
++class TestContainerFromDicts(unittest.TestCase):
++ def testUnknownType(self):
++ for cls in [str, int, bool]:
++ try:
++ outils.ContainerFromDicts(None, cls, NotImplemented)
++ except TypeError, err:
++ self.assertTrue(str(err).startswith("Unknown container type"))
++ else:
++ self.fail("Exception was not raised")
++
++ try:
++ outils.ContainerFromDicts(None, cls(), NotImplemented)
++ except TypeError, err:
++ self.assertTrue(str(err).endswith("is not a type"))
++ else:
++ self.fail("Exception was not raised")
++
++ def testEmptyDict(self):
++ value = {}
++ self.assertFalse(type(value) in outils._SEQUENCE_TYPES)
++ self.assertEqual(outils.ContainerFromDicts(value, dict,
++ NotImplemented),
++ {})
++
++ def testEmptySequences(self):
++ for cls in [list, tuple, set, frozenset]:
++ self.assertEqual(outils.ContainerFromDicts([], cls,
++ _FakeWithFromDict),
++ cls())
++
++
+ if __name__ == "__main__":
+ testutils.GanetiTestProgram()
--
thanks,
iustin