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