Revision 8301885b
b/lib/cmdlib.py | ||
---|---|---|
11798 | 11798 |
return result |
11799 | 11799 |
|
11800 | 11800 |
|
11801 |
def PrepareContainerMods(mods, private_fn): |
|
11802 |
"""Prepares a list of container modifications by adding a private data field. |
|
11803 |
|
|
11804 |
@type mods: list of tuples; (operation, index, parameters) |
|
11805 |
@param mods: List of modifications |
|
11806 |
@type private_fn: callable or None |
|
11807 |
@param private_fn: Callable for constructing a private data field for a |
|
11808 |
modification |
|
11809 |
@rtype: list |
|
11810 |
|
|
11811 |
""" |
|
11812 |
if private_fn is None: |
|
11813 |
fn = lambda: None |
|
11814 |
else: |
|
11815 |
fn = private_fn |
|
11816 |
|
|
11817 |
return [(op, idx, params, fn()) for (op, idx, params) in mods] |
|
11818 |
|
|
11819 |
|
|
11820 |
def ApplyContainerMods(kind, container, chgdesc, mods, |
|
11821 |
create_fn, modify_fn, remove_fn): |
|
11822 |
"""Applies descriptions in C{mods} to C{container}. |
|
11823 |
|
|
11824 |
@type kind: string |
|
11825 |
@param kind: One-word item description |
|
11826 |
@type container: list |
|
11827 |
@param container: Container to modify |
|
11828 |
@type chgdesc: None or list |
|
11829 |
@param chgdesc: List of applied changes |
|
11830 |
@type mods: list |
|
11831 |
@param mods: Modifications as returned by L{PrepareContainerMods} |
|
11832 |
@type create_fn: callable |
|
11833 |
@param create_fn: Callback for creating a new item (L{constants.DDM_ADD}); |
|
11834 |
receives absolute item index, parameters, list of applied changes and |
|
11835 |
private data object as added by L{PrepareContainerMods}, returns new item |
|
11836 |
@type modify_fn: callable |
|
11837 |
@param modify_fn: Callback for modifying an existing item |
|
11838 |
(L{constants.DDM_MODIFY}); receives absolute item index, item, parameters, |
|
11839 |
list of applied changes and private data object as added by |
|
11840 |
L{PrepareContainerMods} |
|
11841 |
@type remove_fn: callable |
|
11842 |
@param remove_fn: Callback on removing item; receives absolute item index, |
|
11843 |
item, list of applied changes and private data object as added by |
|
11844 |
L{PrepareContainerMods} |
|
11845 |
|
|
11846 |
""" |
|
11847 |
for (op, idx, params, private) in mods: |
|
11848 |
if idx == -1: |
|
11849 |
# Append |
|
11850 |
absidx = len(container) - 1 |
|
11851 |
elif idx < 0: |
|
11852 |
raise IndexError("Not accepting negative indices") |
|
11853 |
else: |
|
11854 |
absidx = idx |
|
11855 |
|
|
11856 |
if op == constants.DDM_ADD: |
|
11857 |
if create_fn is None: |
|
11858 |
item = params |
|
11859 |
else: |
|
11860 |
item = create_fn(absidx + 1, params, chgdesc, private) |
|
11861 |
|
|
11862 |
if idx == -1: |
|
11863 |
container.append(item) |
|
11864 |
else: |
|
11865 |
assert idx >= 0 |
|
11866 |
# list.insert does so before the specified index |
|
11867 |
container.insert(idx, item) |
|
11868 |
else: |
|
11869 |
# Retrieve existing item |
|
11870 |
try: |
|
11871 |
item = container[absidx] |
|
11872 |
except IndexError: |
|
11873 |
raise IndexError("Invalid %s index %s" % (kind, idx)) |
|
11874 |
|
|
11875 |
if op == constants.DDM_REMOVE: |
|
11876 |
assert not params |
|
11877 |
|
|
11878 |
if remove_fn is not None: |
|
11879 |
remove_fn(absidx, item, chgdesc, private) |
|
11880 |
|
|
11881 |
if chgdesc is not None: |
|
11882 |
chgdesc.append(("%s/%s" % (kind, absidx), "remove")) |
|
11883 |
|
|
11884 |
assert container[absidx] == item |
|
11885 |
del container[absidx] |
|
11886 |
elif op == constants.DDM_MODIFY: |
|
11887 |
if modify_fn is not None: |
|
11888 |
modify_fn(absidx, item, params, chgdesc, private) |
|
11889 |
else: |
|
11890 |
raise errors.ProgrammerError("Unhandled operation '%s'" % op) |
|
11891 |
|
|
11892 |
|
|
11801 | 11893 |
class LUInstanceSetParams(LogicalUnit): |
11802 | 11894 |
"""Modifies an instances's parameters. |
11803 | 11895 |
|
b/test/ganeti.cmdlib_unittest.py | ||
---|---|---|
779 | 779 |
self.assertEqual(self.lu.warning_log, [(msg, ())]) |
780 | 780 |
|
781 | 781 |
|
782 |
class TestApplyContainerMods(unittest.TestCase): |
|
783 |
def testEmptyContainer(self): |
|
784 |
container = [] |
|
785 |
chgdesc = [] |
|
786 |
cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None) |
|
787 |
self.assertEqual(container, []) |
|
788 |
self.assertEqual(chgdesc, []) |
|
789 |
|
|
790 |
def testAdd(self): |
|
791 |
container = [] |
|
792 |
chgdesc = [] |
|
793 |
mods = cmdlib.PrepareContainerMods([ |
|
794 |
(constants.DDM_ADD, -1, "Hello"), |
|
795 |
(constants.DDM_ADD, -1, "World"), |
|
796 |
(constants.DDM_ADD, 0, "Start"), |
|
797 |
(constants.DDM_ADD, -1, "End"), |
|
798 |
], None) |
|
799 |
cmdlib.ApplyContainerMods("test", container, chgdesc, mods, |
|
800 |
None, None, None) |
|
801 |
self.assertEqual(container, ["Start", "Hello", "World", "End"]) |
|
802 |
self.assertEqual(chgdesc, []) |
|
803 |
|
|
804 |
def testRemoveError(self): |
|
805 |
for idx in [0, 1, 2, 100, -1, -4]: |
|
806 |
mods = cmdlib.PrepareContainerMods([ |
|
807 |
(constants.DDM_REMOVE, idx, None), |
|
808 |
], None) |
|
809 |
self.assertRaises(IndexError, cmdlib.ApplyContainerMods, |
|
810 |
"test", [], None, mods, None, None, None) |
|
811 |
|
|
812 |
mods = cmdlib.PrepareContainerMods([ |
|
813 |
(constants.DDM_REMOVE, 0, object()), |
|
814 |
], None) |
|
815 |
self.assertRaises(AssertionError, cmdlib.ApplyContainerMods, |
|
816 |
"test", [""], None, mods, None, None, None) |
|
817 |
|
|
818 |
def testAddError(self): |
|
819 |
for idx in range(-100, -1): |
|
820 |
mods = cmdlib.PrepareContainerMods([ |
|
821 |
(constants.DDM_ADD, idx, None), |
|
822 |
], None) |
|
823 |
self.assertRaises(IndexError, cmdlib.ApplyContainerMods, |
|
824 |
"test", [], None, mods, None, None, None) |
|
825 |
|
|
826 |
def testRemove(self): |
|
827 |
container = ["item 1", "item 2"] |
|
828 |
mods = cmdlib.PrepareContainerMods([ |
|
829 |
(constants.DDM_ADD, -1, "aaa"), |
|
830 |
(constants.DDM_REMOVE, -1, None), |
|
831 |
(constants.DDM_ADD, -1, "bbb"), |
|
832 |
], None) |
|
833 |
chgdesc = [] |
|
834 |
cmdlib.ApplyContainerMods("test", container, chgdesc, mods, |
|
835 |
None, None, None) |
|
836 |
self.assertEqual(container, ["item 1", "item 2", "bbb"]) |
|
837 |
self.assertEqual(chgdesc, [ |
|
838 |
("test/2", "remove"), |
|
839 |
]) |
|
840 |
|
|
841 |
class _PrivateData: |
|
842 |
def __init__(self): |
|
843 |
self.data = None |
|
844 |
|
|
845 |
@staticmethod |
|
846 |
def _CreateTestFn(idx, params, chgdesc, private): |
|
847 |
chgdesc.append(("test/%s" % idx, hex(idx))) |
|
848 |
private.data = ("add", idx, params) |
|
849 |
return (100 * idx, params) |
|
850 |
|
|
851 |
@staticmethod |
|
852 |
def _ModifyTestFn(idx, item, params, chgdesc, private): |
|
853 |
chgdesc.append(("test/%s" % idx, "modify %s" % params)) |
|
854 |
private.data = ("modify", idx, params) |
|
855 |
|
|
856 |
@staticmethod |
|
857 |
def _RemoveTestFn(idx, item, chgdesc, private): |
|
858 |
private.data = ("remove", idx, item) |
|
859 |
|
|
860 |
def testAddWithCreateFunction(self): |
|
861 |
container = [] |
|
862 |
chgdesc = [] |
|
863 |
mods = cmdlib.PrepareContainerMods([ |
|
864 |
(constants.DDM_ADD, -1, "Hello"), |
|
865 |
(constants.DDM_ADD, -1, "World"), |
|
866 |
(constants.DDM_ADD, 0, "Start"), |
|
867 |
(constants.DDM_ADD, -1, "End"), |
|
868 |
(constants.DDM_REMOVE, 2, None), |
|
869 |
(constants.DDM_MODIFY, -1, "foobar"), |
|
870 |
(constants.DDM_REMOVE, 2, None), |
|
871 |
(constants.DDM_ADD, 1, "More"), |
|
872 |
], self._PrivateData) |
|
873 |
cmdlib.ApplyContainerMods("test", container, chgdesc, mods, |
|
874 |
self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn) |
|
875 |
self.assertEqual(container, [ |
|
876 |
(100, "Start"), |
|
877 |
(200, "More"), |
|
878 |
(0, "Hello"), |
|
879 |
]) |
|
880 |
self.assertEqual(chgdesc, [ |
|
881 |
("test/0", "0x0"), |
|
882 |
("test/1", "0x1"), |
|
883 |
("test/1", "0x1"), |
|
884 |
("test/3", "0x3"), |
|
885 |
("test/2", "remove"), |
|
886 |
("test/2", "modify foobar"), |
|
887 |
("test/2", "remove"), |
|
888 |
("test/2", "0x2") |
|
889 |
]) |
|
890 |
self.assertTrue(compat.all(op == private.data[0] |
|
891 |
for (op, _, _, private) in mods)) |
|
892 |
self.assertEqual([private.data for (op, _, _, private) in mods], [ |
|
893 |
("add", 0, "Hello"), |
|
894 |
("add", 1, "World"), |
|
895 |
("add", 1, "Start"), |
|
896 |
("add", 3, "End"), |
|
897 |
("remove", 2, (100, "World")), |
|
898 |
("modify", 2, "foobar"), |
|
899 |
("remove", 2, (300, "End")), |
|
900 |
("add", 2, "More"), |
|
901 |
]) |
|
902 |
|
|
903 |
|
|
782 | 904 |
if __name__ == "__main__": |
783 | 905 |
testutils.GanetiTestProgram() |
Also available in: Unified diff