Revision 66222813 test/py/cmdlib/cmdlib_unittest.py

b/test/py/cmdlib/cmdlib_unittest.py
31 31
from ganeti import mcpu
32 32
from ganeti import cmdlib
33 33
from ganeti.cmdlib import cluster
34
from ganeti.cmdlib import group
35 34
from ganeti.cmdlib import instance
36 35
from ganeti.cmdlib import instance_storage
37 36
from ganeti.cmdlib import instance_utils
......
627 626
    self.assertEqual(ret, [])
628 627

  
629 628

  
630
class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
631
  def test(self):
632
    ispec = {
633
      constants.ISPEC_MEM_SIZE: 2048,
634
      constants.ISPEC_CPU_COUNT: 2,
635
      constants.ISPEC_DISK_COUNT: 1,
636
      constants.ISPEC_DISK_SIZE: [512],
637
      constants.ISPEC_NIC_COUNT: 0,
638
      constants.ISPEC_SPINDLE_USE: 1,
639
      }
640
    stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1,
641
                                            constants.DT_PLAIN)
642
    ret = instance._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
643
                                                        constants.DT_PLAIN,
644
                                                        _compute_fn=stub)
645
    self.assertEqual(ret, [])
646

  
647

  
648 629
class _CallRecorder:
649 630
  def __init__(self, return_value=None):
650 631
    self.called = False
......
678 659
    self.assertEqual(ret, [])
679 660

  
680 661

  
681
class _FakeConfigForTargetNodeIPolicy:
682
  def __init__(self, node_info=NotImplemented):
683
    self._node_info = node_info
684

  
685
  def GetNodeInfo(self, _):
686
    return self._node_info
687

  
688

  
689
class TestCheckTargetNodeIPolicy(unittest.TestCase):
690
  def setUp(self):
691
    self.instance = objects.Instance(primary_node="blubb")
692
    self.target_node = objects.Node(group="bar")
693
    node_info = objects.Node(group="foo")
694
    fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
695
    self.lu = _FakeLU(cfg=fake_cfg)
696

  
697
  def testNoViolation(self):
698
    compute_recoder = _CallRecorder(return_value=[])
699
    instance.CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
700
                                    self.target_node, NotImplemented,
701
                                    _compute_fn=compute_recoder)
702
    self.assertTrue(compute_recoder.called)
703
    self.assertEqual(self.lu.warning_log, [])
704

  
705
  def testNoIgnore(self):
706
    compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
707
    self.assertRaises(errors.OpPrereqError, instance.CheckTargetNodeIPolicy,
708
                      self.lu, NotImplemented, self.instance,
709
                      self.target_node, NotImplemented,
710
                      _compute_fn=compute_recoder)
711
    self.assertTrue(compute_recoder.called)
712
    self.assertEqual(self.lu.warning_log, [])
713

  
714
  def testIgnoreViolation(self):
715
    compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
716
    instance.CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
717
                                     self.target_node, NotImplemented,
718
                                     ignore=True, _compute_fn=compute_recoder)
719
    self.assertTrue(compute_recoder.called)
720
    msg = ("Instance does not meet target node group's (bar) instance policy:"
721
           " mem_size not in range")
722
    self.assertEqual(self.lu.warning_log, [(msg, ())])
723

  
724

  
725
class TestApplyContainerMods(unittest.TestCase):
726
  def testEmptyContainer(self):
727
    container = []
728
    chgdesc = []
729
    instance._ApplyContainerMods("test", container, chgdesc, [], None, None,
730
                                None)
731
    self.assertEqual(container, [])
732
    self.assertEqual(chgdesc, [])
733

  
734
  def testAdd(self):
735
    container = []
736
    chgdesc = []
737
    mods = instance._PrepareContainerMods([
738
      (constants.DDM_ADD, -1, "Hello"),
739
      (constants.DDM_ADD, -1, "World"),
740
      (constants.DDM_ADD, 0, "Start"),
741
      (constants.DDM_ADD, -1, "End"),
742
      ], None)
743
    instance._ApplyContainerMods("test", container, chgdesc, mods,
744
                                None, None, None)
745
    self.assertEqual(container, ["Start", "Hello", "World", "End"])
746
    self.assertEqual(chgdesc, [])
747

  
748
    mods = instance._PrepareContainerMods([
749
      (constants.DDM_ADD, 0, "zero"),
750
      (constants.DDM_ADD, 3, "Added"),
751
      (constants.DDM_ADD, 5, "four"),
752
      (constants.DDM_ADD, 7, "xyz"),
753
      ], None)
754
    instance._ApplyContainerMods("test", container, chgdesc, mods,
755
                                None, None, None)
756
    self.assertEqual(container,
757
                     ["zero", "Start", "Hello", "Added", "World", "four",
758
                      "End", "xyz"])
759
    self.assertEqual(chgdesc, [])
760

  
761
    for idx in [-2, len(container) + 1]:
762
      mods = instance._PrepareContainerMods([
763
        (constants.DDM_ADD, idx, "error"),
764
        ], None)
765
      self.assertRaises(IndexError, instance._ApplyContainerMods,
766
                        "test", container, None, mods, None, None, None)
767

  
768
  def testRemoveError(self):
769
    for idx in [0, 1, 2, 100, -1, -4]:
770
      mods = instance._PrepareContainerMods([
771
        (constants.DDM_REMOVE, idx, None),
772
        ], None)
773
      self.assertRaises(IndexError, instance._ApplyContainerMods,
774
                        "test", [], None, mods, None, None, None)
775

  
776
    mods = instance._PrepareContainerMods([
777
      (constants.DDM_REMOVE, 0, object()),
778
      ], None)
779
    self.assertRaises(AssertionError, instance._ApplyContainerMods,
780
                      "test", [""], None, mods, None, None, None)
781

  
782
  def testAddError(self):
783
    for idx in range(-100, -1) + [100]:
784
      mods = instance._PrepareContainerMods([
785
        (constants.DDM_ADD, idx, None),
786
        ], None)
787
      self.assertRaises(IndexError, instance._ApplyContainerMods,
788
                        "test", [], None, mods, None, None, None)
789

  
790
  def testRemove(self):
791
    container = ["item 1", "item 2"]
792
    mods = instance._PrepareContainerMods([
793
      (constants.DDM_ADD, -1, "aaa"),
794
      (constants.DDM_REMOVE, -1, None),
795
      (constants.DDM_ADD, -1, "bbb"),
796
      ], None)
797
    chgdesc = []
798
    instance._ApplyContainerMods("test", container, chgdesc, mods,
799
                                None, None, None)
800
    self.assertEqual(container, ["item 1", "item 2", "bbb"])
801
    self.assertEqual(chgdesc, [
802
      ("test/2", "remove"),
803
      ])
804

  
805
  def testModify(self):
806
    container = ["item 1", "item 2"]
807
    mods = instance._PrepareContainerMods([
808
      (constants.DDM_MODIFY, -1, "a"),
809
      (constants.DDM_MODIFY, 0, "b"),
810
      (constants.DDM_MODIFY, 1, "c"),
811
      ], None)
812
    chgdesc = []
813
    instance._ApplyContainerMods("test", container, chgdesc, mods,
814
                                None, None, None)
815
    self.assertEqual(container, ["item 1", "item 2"])
816
    self.assertEqual(chgdesc, [])
817

  
818
    for idx in [-2, len(container) + 1]:
819
      mods = instance._PrepareContainerMods([
820
        (constants.DDM_MODIFY, idx, "error"),
821
        ], None)
822
      self.assertRaises(IndexError, instance._ApplyContainerMods,
823
                        "test", container, None, mods, None, None, None)
824

  
825
  class _PrivateData:
826
    def __init__(self):
827
      self.data = None
828

  
829
  @staticmethod
830
  def _CreateTestFn(idx, params, private):
831
    private.data = ("add", idx, params)
832
    return ((100 * idx, params), [
833
      ("test/%s" % idx, hex(idx)),
834
      ])
835

  
836
  @staticmethod
837
  def _ModifyTestFn(idx, item, params, private):
838
    private.data = ("modify", idx, params)
839
    return [
840
      ("test/%s" % idx, "modify %s" % params),
841
      ]
842

  
843
  @staticmethod
844
  def _RemoveTestFn(idx, item, private):
845
    private.data = ("remove", idx, item)
846

  
847
  def testAddWithCreateFunction(self):
848
    container = []
849
    chgdesc = []
850
    mods = instance._PrepareContainerMods([
851
      (constants.DDM_ADD, -1, "Hello"),
852
      (constants.DDM_ADD, -1, "World"),
853
      (constants.DDM_ADD, 0, "Start"),
854
      (constants.DDM_ADD, -1, "End"),
855
      (constants.DDM_REMOVE, 2, None),
856
      (constants.DDM_MODIFY, -1, "foobar"),
857
      (constants.DDM_REMOVE, 2, None),
858
      (constants.DDM_ADD, 1, "More"),
859
      ], self._PrivateData)
860
    instance._ApplyContainerMods("test", container, chgdesc, mods,
861
                                self._CreateTestFn, self._ModifyTestFn,
862
                                self._RemoveTestFn)
863
    self.assertEqual(container, [
864
      (000, "Start"),
865
      (100, "More"),
866
      (000, "Hello"),
867
      ])
868
    self.assertEqual(chgdesc, [
869
      ("test/0", "0x0"),
870
      ("test/1", "0x1"),
871
      ("test/0", "0x0"),
872
      ("test/3", "0x3"),
873
      ("test/2", "remove"),
874
      ("test/2", "modify foobar"),
875
      ("test/2", "remove"),
876
      ("test/1", "0x1")
877
      ])
878
    self.assertTrue(compat.all(op == private.data[0]
879
                               for (op, _, _, private) in mods))
880
    self.assertEqual([private.data for (op, _, _, private) in mods], [
881
      ("add", 0, "Hello"),
882
      ("add", 1, "World"),
883
      ("add", 0, "Start"),
884
      ("add", 3, "End"),
885
      ("remove", 2, (100, "World")),
886
      ("modify", 2, "foobar"),
887
      ("remove", 2, (300, "End")),
888
      ("add", 1, "More"),
889
      ])
890

  
891

  
892
class _FakeConfigForGenDiskTemplate:
893
  def __init__(self, enabled_disk_templates):
894
    self._unique_id = itertools.count()
895
    self._drbd_minor = itertools.count(20)
896
    self._port = itertools.count(constants.FIRST_DRBD_PORT)
897
    self._secret = itertools.count()
898
    self._enabled_disk_templates = enabled_disk_templates
899

  
900
  def GetVGName(self):
901
    return "testvg"
902

  
903
  def GenerateUniqueID(self, ec_id):
904
    return "ec%s-uq%s" % (ec_id, self._unique_id.next())
905

  
906
  def AllocateDRBDMinor(self, nodes, instance):
907
    return [self._drbd_minor.next()
908
            for _ in nodes]
909

  
910
  def AllocatePort(self):
911
    return self._port.next()
912

  
913
  def GenerateDRBDSecret(self, ec_id):
914
    return "ec%s-secret%s" % (ec_id, self._secret.next())
915

  
916
  def GetInstanceInfo(self, _):
917
    return "foobar"
918

  
919
  def GetClusterInfo(self):
920
    cluster = objects.Cluster()
921
    cluster.enabled_disk_templates = self._enabled_disk_templates
922
    return cluster
923

  
924

  
925
class _FakeProcForGenDiskTemplate:
926
  def GetECId(self):
927
    return 0
928

  
929

  
930
class TestGenerateDiskTemplate(unittest.TestCase):
931

  
932
  def _SetUpLUWithTemplates(self, enabled_disk_templates):
933
    self._enabled_disk_templates = enabled_disk_templates
934
    cfg = _FakeConfigForGenDiskTemplate(self._enabled_disk_templates)
935
    proc = _FakeProcForGenDiskTemplate()
936

  
937
    self.lu = _FakeLU(cfg=cfg, proc=proc)
938

  
939
  def setUp(self):
940
    nodegroup = objects.NodeGroup(name="ng")
941
    nodegroup.UpgradeConfig()
942

  
943
    self._enabled_disk_templates = list(constants.DISK_TEMPLATES)
944
    self._SetUpLUWithTemplates(self._enabled_disk_templates)
945
    self.nodegroup = nodegroup
946

  
947
  @staticmethod
948
  def GetDiskParams():
949
    return copy.deepcopy(constants.DISK_DT_DEFAULTS)
950

  
951
  def testWrongDiskTemplate(self):
952
    gdt = instance.GenerateDiskTemplate
953
    disk_template = "##unknown##"
954

  
955
    assert disk_template not in constants.DISK_TEMPLATES
956

  
957
    self.assertRaises(errors.OpPrereqError, gdt, self.lu, disk_template,
958
                      "inst26831.example.com", "node30113.example.com", [], [],
959
                      NotImplemented, NotImplemented, 0, self.lu.LogInfo,
960
                      self.GetDiskParams())
961

  
962
  def testDiskless(self):
963
    gdt = instance.GenerateDiskTemplate
964

  
965
    result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
966
                 "node30113.example.com", [], [],
967
                 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
968
                 self.GetDiskParams())
969
    self.assertEqual(result, [])
970

  
971
  def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
972
                       file_storage_dir=NotImplemented,
973
                       file_driver=NotImplemented):
974
    gdt = instance.GenerateDiskTemplate
975

  
976
    map(lambda params: utils.ForceDictType(params,
977
                                           constants.IDISK_PARAMS_TYPES),
978
        disk_info)
979

  
980
    # Check if non-empty list of secondaries is rejected
981
    self.assertRaises(errors.ProgrammerError, gdt, self.lu,
982
                      template, "inst25088.example.com",
983
                      "node185.example.com", ["node323.example.com"], [],
984
                      NotImplemented, NotImplemented, base_index,
985
                      self.lu.LogInfo, self.GetDiskParams())
986

  
987
    result = gdt(self.lu, template, "inst21662.example.com",
988
                 "node21741.example.com", [],
989
                 disk_info, file_storage_dir, file_driver, base_index,
990
                 self.lu.LogInfo, self.GetDiskParams())
991

  
992
    for (idx, disk) in enumerate(result):
993
      self.assertTrue(isinstance(disk, objects.Disk))
994
      self.assertEqual(disk.dev_type, exp_dev_type)
995
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
996
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
997
      self.assertTrue(disk.children is None)
998

  
999
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1000
    instance._UpdateIvNames(base_index, result)
1001
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1002

  
1003
    return result
1004

  
1005
  def _CheckIvNames(self, disks, base_index, end_index):
1006
    self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1007
                     ["disk/%s" % i for i in range(base_index, end_index)])
1008

  
1009
  def testPlain(self):
1010
    disk_info = [{
1011
      constants.IDISK_SIZE: 1024,
1012
      constants.IDISK_MODE: constants.DISK_RDWR,
1013
      }, {
1014
      constants.IDISK_SIZE: 4096,
1015
      constants.IDISK_VG: "othervg",
1016
      constants.IDISK_MODE: constants.DISK_RDWR,
1017
      }]
1018

  
1019
    result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1020
                                   constants.LD_LV)
1021

  
1022
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1023
      ("testvg", "ec0-uq0.disk3"),
1024
      ("othervg", "ec0-uq1.disk4"),
1025
      ])
1026

  
1027
  def testFile(self):
1028
    # anything != DT_FILE would do here
1029
    self._SetUpLUWithTemplates([constants.DT_PLAIN])
1030
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1031
                      constants.DT_FILE, [], 0, NotImplemented)
1032
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1033
                      constants.DT_SHARED_FILE, [], 0, NotImplemented)
1034

  
1035
    for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1036
      disk_info = [{
1037
        constants.IDISK_SIZE: 80 * 1024,
1038
        constants.IDISK_MODE: constants.DISK_RDONLY,
1039
        }, {
1040
        constants.IDISK_SIZE: 4096,
1041
        constants.IDISK_MODE: constants.DISK_RDWR,
1042
        }, {
1043
        constants.IDISK_SIZE: 6 * 1024,
1044
        constants.IDISK_MODE: constants.DISK_RDWR,
1045
        }]
1046

  
1047
      self._SetUpLUWithTemplates([disk_template])
1048
      result = self._TestTrivialDisk(disk_template, disk_info, 2,
1049
        constants.LD_FILE, file_storage_dir="/tmp",
1050
        file_driver=constants.FD_BLKTAP)
1051

  
1052
      self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1053
        (constants.FD_BLKTAP, "/tmp/disk2"),
1054
        (constants.FD_BLKTAP, "/tmp/disk3"),
1055
        (constants.FD_BLKTAP, "/tmp/disk4"),
1056
        ])
1057

  
1058
  def testBlock(self):
1059
    disk_info = [{
1060
      constants.IDISK_SIZE: 8 * 1024,
1061
      constants.IDISK_MODE: constants.DISK_RDWR,
1062
      constants.IDISK_ADOPT: "/tmp/some/block/dev",
1063
      }]
1064

  
1065
    result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1066
                                   constants.LD_BLOCKDEV)
1067

  
1068
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1069
      (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1070
      ])
1071

  
1072
  def testRbd(self):
1073
    disk_info = [{
1074
      constants.IDISK_SIZE: 8 * 1024,
1075
      constants.IDISK_MODE: constants.DISK_RDONLY,
1076
      }, {
1077
      constants.IDISK_SIZE: 100 * 1024,
1078
      constants.IDISK_MODE: constants.DISK_RDWR,
1079
      }]
1080

  
1081
    result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1082
                                   constants.LD_RBD)
1083

  
1084
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1085
      ("rbd", "ec0-uq0.rbd.disk0"),
1086
      ("rbd", "ec0-uq1.rbd.disk1"),
1087
      ])
1088

  
1089
  def testDrbd8(self):
1090
    gdt = instance.GenerateDiskTemplate
1091
    drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1092
    drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1093

  
1094
    disk_info = [{
1095
      constants.IDISK_SIZE: 1024,
1096
      constants.IDISK_MODE: constants.DISK_RDWR,
1097
      }, {
1098
      constants.IDISK_SIZE: 100 * 1024,
1099
      constants.IDISK_MODE: constants.DISK_RDONLY,
1100
      constants.IDISK_METAVG: "metavg",
1101
      }, {
1102
      constants.IDISK_SIZE: 4096,
1103
      constants.IDISK_MODE: constants.DISK_RDWR,
1104
      constants.IDISK_VG: "vgxyz",
1105
      },
1106
      ]
1107

  
1108
    exp_logical_ids = [[
1109
      (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1110
      (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1111
      ], [
1112
      (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1113
      ("metavg", "ec0-uq1.disk1_meta"),
1114
      ], [
1115
      ("vgxyz", "ec0-uq2.disk2_data"),
1116
      (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1117
      ]]
1118

  
1119
    assert len(exp_logical_ids) == len(disk_info)
1120

  
1121
    map(lambda params: utils.ForceDictType(params,
1122
                                           constants.IDISK_PARAMS_TYPES),
1123
        disk_info)
1124

  
1125
    # Check if empty list of secondaries is rejected
1126
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1127
                      "inst827.example.com", "node1334.example.com", [],
1128
                      disk_info, NotImplemented, NotImplemented, 0,
1129
                      self.lu.LogInfo, self.GetDiskParams())
1130

  
1131
    result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1132
                 "node1334.example.com", ["node12272.example.com"],
1133
                 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1134
                 self.GetDiskParams())
1135

  
1136
    for (idx, disk) in enumerate(result):
1137
      self.assertTrue(isinstance(disk, objects.Disk))
1138
      self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1139
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1140
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1141

  
1142
      for child in disk.children:
1143
        self.assertTrue(isinstance(disk, objects.Disk))
1144
        self.assertEqual(child.dev_type, constants.LD_LV)
1145
        self.assertTrue(child.children is None)
1146

  
1147
      self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1148
                       exp_logical_ids[idx])
1149

  
1150
      self.assertEqual(len(disk.children), 2)
1151
      self.assertEqual(disk.children[0].size, disk.size)
1152
      self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1153

  
1154
    self._CheckIvNames(result, 0, len(disk_info))
1155
    instance._UpdateIvNames(0, result)
1156
    self._CheckIvNames(result, 0, len(disk_info))
1157

  
1158
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1159
      ("node1334.example.com", "node12272.example.com",
1160
       constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1161
      ("node1334.example.com", "node12272.example.com",
1162
       constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1163
      ("node1334.example.com", "node12272.example.com",
1164
       constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1165
      ])
1166

  
1167

  
1168
class _ConfigForDiskWipe:
1169
  def __init__(self, exp_node_uuid):
1170
    self._exp_node_uuid = exp_node_uuid
1171

  
1172
  def SetDiskID(self, device, node_uuid):
1173
    assert isinstance(device, objects.Disk)
1174
    assert node_uuid == self._exp_node_uuid
1175

  
1176
  def GetNodeName(self, node_uuid):
1177
    assert node_uuid == self._exp_node_uuid
1178
    return "name.of.expected.node"
1179

  
1180

  
1181
class _RpcForDiskWipe:
1182
  def __init__(self, exp_node, pause_cb, wipe_cb):
1183
    self._exp_node = exp_node
1184
    self._pause_cb = pause_cb
1185
    self._wipe_cb = wipe_cb
1186

  
1187
  def call_blockdev_pause_resume_sync(self, node, disks, pause):
1188
    assert node == self._exp_node
1189
    return rpc.RpcResult(data=self._pause_cb(disks, pause))
1190

  
1191
  def call_blockdev_wipe(self, node, bdev, offset, size):
1192
    assert node == self._exp_node
1193
    return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1194

  
1195

  
1196
class _DiskPauseTracker:
1197
  def __init__(self):
1198
    self.history = []
1199

  
1200
  def __call__(self, (disks, instance), pause):
1201
    assert not (set(disks) - set(instance.disks))
1202

  
1203
    self.history.extend((i.logical_id, i.size, pause)
1204
                        for i in disks)
1205

  
1206
    return (True, [True] * len(disks))
1207

  
1208

  
1209
class _DiskWipeProgressTracker:
1210
  def __init__(self, start_offset):
1211
    self._start_offset = start_offset
1212
    self.progress = {}
1213

  
1214
  def __call__(self, (disk, _), offset, size):
1215
    assert isinstance(offset, (long, int))
1216
    assert isinstance(size, (long, int))
1217

  
1218
    max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1219

  
1220
    assert offset >= self._start_offset
1221
    assert (offset + size) <= disk.size
1222

  
1223
    assert size > 0
1224
    assert size <= constants.MAX_WIPE_CHUNK
1225
    assert size <= max_chunk_size
1226

  
1227
    assert offset == self._start_offset or disk.logical_id in self.progress
1228

  
1229
    # Keep track of progress
1230
    cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1231

  
1232
    assert cur_progress == offset
1233

  
1234
    # Record progress
1235
    self.progress[disk.logical_id] += size
1236

  
1237
    return (True, None)
1238

  
1239

  
1240
class TestWipeDisks(unittest.TestCase):
1241
  def _FailingPauseCb(self, (disks, _), pause):
1242
    self.assertEqual(len(disks), 3)
1243
    self.assertTrue(pause)
1244
    # Simulate an RPC error
1245
    return (False, "error")
1246

  
1247
  def testPauseFailure(self):
1248
    node_name = "node1372.example.com"
1249

  
1250
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1251
                                     NotImplemented),
1252
                 cfg=_ConfigForDiskWipe(node_name))
1253

  
1254
    disks = [
1255
      objects.Disk(dev_type=constants.LD_LV),
1256
      objects.Disk(dev_type=constants.LD_LV),
1257
      objects.Disk(dev_type=constants.LD_LV),
1258
      ]
1259

  
1260
    inst = objects.Instance(name="inst21201",
1261
                            primary_node=node_name,
1262
                            disk_template=constants.DT_PLAIN,
1263
                            disks=disks)
1264

  
1265
    self.assertRaises(errors.OpExecError, instance.WipeDisks, lu, inst)
1266

  
1267
  def _FailingWipeCb(self, (disk, _), offset, size):
1268
    # This should only ever be called for the first disk
1269
    self.assertEqual(disk.logical_id, "disk0")
1270
    return (False, None)
1271

  
1272
  def testFailingWipe(self):
1273
    node_uuid = "node13445-uuid"
1274
    pt = _DiskPauseTracker()
1275

  
1276
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_uuid, pt, self._FailingWipeCb),
1277
                 cfg=_ConfigForDiskWipe(node_uuid))
1278

  
1279
    disks = [
1280
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1281
                   size=100 * 1024),
1282
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1283
                   size=500 * 1024),
1284
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=256),
1285
      ]
1286

  
1287
    inst = objects.Instance(name="inst562",
1288
                            primary_node=node_uuid,
1289
                            disk_template=constants.DT_PLAIN,
1290
                            disks=disks)
1291

  
1292
    try:
1293
      instance.WipeDisks(lu, inst)
1294
    except errors.OpExecError, err:
1295
      self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
1296
    else:
1297
      self.fail("Did not raise exception")
1298

  
1299
    # Check if all disks were paused and resumed
1300
    self.assertEqual(pt.history, [
1301
      ("disk0", 100 * 1024, True),
1302
      ("disk1", 500 * 1024, True),
1303
      ("disk2", 256, True),
1304
      ("disk0", 100 * 1024, False),
1305
      ("disk1", 500 * 1024, False),
1306
      ("disk2", 256, False),
1307
      ])
1308

  
1309
  def _PrepareWipeTest(self, start_offset, disks):
1310
    node_name = "node-with-offset%s.example.com" % start_offset
1311
    pauset = _DiskPauseTracker()
1312
    progresst = _DiskWipeProgressTracker(start_offset)
1313

  
1314
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1315
                 cfg=_ConfigForDiskWipe(node_name))
1316

  
1317
    instance = objects.Instance(name="inst3560",
1318
                                primary_node=node_name,
1319
                                disk_template=constants.DT_PLAIN,
1320
                                disks=disks)
1321

  
1322
    return (lu, instance, pauset, progresst)
1323

  
1324
  def testNormalWipe(self):
1325
    disks = [
1326
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk0", size=1024),
1327
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1328
                   size=500 * 1024),
1329
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=128),
1330
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk3",
1331
                   size=constants.MAX_WIPE_CHUNK),
1332
      ]
1333

  
1334
    (lu, inst, pauset, progresst) = self._PrepareWipeTest(0, disks)
1335

  
1336
    instance.WipeDisks(lu, inst)
1337

  
1338
    self.assertEqual(pauset.history, [
1339
      ("disk0", 1024, True),
1340
      ("disk1", 500 * 1024, True),
1341
      ("disk2", 128, True),
1342
      ("disk3", constants.MAX_WIPE_CHUNK, True),
1343
      ("disk0", 1024, False),
1344
      ("disk1", 500 * 1024, False),
1345
      ("disk2", 128, False),
1346
      ("disk3", constants.MAX_WIPE_CHUNK, False),
1347
      ])
1348

  
1349
    # Ensure the complete disk has been wiped
1350
    self.assertEqual(progresst.progress,
1351
                     dict((i.logical_id, i.size) for i in disks))
1352

  
1353
  def testWipeWithStartOffset(self):
1354
    for start_offset in [0, 280, 8895, 1563204]:
1355
      disks = [
1356
        objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1357
                     size=128),
1358
        objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1359
                     size=start_offset + (100 * 1024)),
1360
        ]
1361

  
1362
      (lu, inst, pauset, progresst) = \
1363
        self._PrepareWipeTest(start_offset, disks)
1364

  
1365
      # Test start offset with only one disk
1366
      instance.WipeDisks(lu, inst,
1367
                         disks=[(1, disks[1], start_offset)])
1368

  
1369
      # Only the second disk may have been paused and wiped
1370
      self.assertEqual(pauset.history, [
1371
        ("disk1", start_offset + (100 * 1024), True),
1372
        ("disk1", start_offset + (100 * 1024), False),
1373
        ])
1374
      self.assertEqual(progresst.progress, {
1375
        "disk1": disks[1].size,
1376
        })
1377

  
1378

  
1379 662
class TestDiskSizeInBytesToMebibytes(unittest.TestCase):
1380 663
  def testLessThanOneMebibyte(self):
1381 664
    for i in [1, 2, 7, 512, 1000, 1023]:
......
1408 691
        self.assertEqual(warnsize, (1024 * 1024) - j)
1409 692

  
1410 693

  
1411
class TestCopyLockList(unittest.TestCase):
1412
  def test(self):
1413
    self.assertEqual(instance.CopyLockList([]), [])
1414
    self.assertEqual(instance.CopyLockList(None), None)
1415
    self.assertEqual(instance.CopyLockList(locking.ALL_SET), locking.ALL_SET)
1416

  
1417
    names = ["foo", "bar"]
1418
    output = instance.CopyLockList(names)
1419
    self.assertEqual(names, output)
1420
    self.assertNotEqual(id(names), id(output), msg="List was not copied")
1421

  
1422

  
1423
class TestCheckOpportunisticLocking(unittest.TestCase):
1424
  class OpTest(opcodes.OpCode):
1425
    OP_PARAMS = [
1426
      ("opportunistic_locking", False, ht.TBool, None),
1427
      ("iallocator", None, ht.TMaybe(ht.TNonEmptyString), "")
1428
      ]
1429

  
1430
  @classmethod
1431
  def _MakeOp(cls, **kwargs):
1432
    op = cls.OpTest(**kwargs)
1433
    op.Validate(True)
1434
    return op
1435

  
1436
  def testMissingAttributes(self):
1437
    self.assertRaises(AttributeError, instance._CheckOpportunisticLocking,
1438
                      object())
1439

  
1440
  def testDefaults(self):
1441
    op = self._MakeOp()
1442
    instance._CheckOpportunisticLocking(op)
1443

  
1444
  def test(self):
1445
    for iallocator in [None, "something", "other"]:
1446
      for opplock in [False, True]:
1447
        op = self._MakeOp(iallocator=iallocator,
1448
                          opportunistic_locking=opplock)
1449
        if opplock and not iallocator:
1450
          self.assertRaises(errors.OpPrereqError,
1451
                            instance._CheckOpportunisticLocking, op)
1452
        else:
1453
          instance._CheckOpportunisticLocking(op)
1454

  
1455

  
1456 694
class _OpTestVerifyErrors(opcodes.OpCode):
1457 695
  OP_PARAMS = [
1458 696
    ("debug_simulate_errors", False, ht.TBool, ""),
......
1798 1036
      self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy, {},
1799 1037
                        bad_policy, group_policy=True)
1800 1038

  
1039

  
1040
class TestCopyLockList(unittest.TestCase):
1041
  def test(self):
1042
    self.assertEqual(instance_utils.CopyLockList([]), [])
1043
    self.assertEqual(instance_utils.CopyLockList(None), None)
1044
    self.assertEqual(instance_utils.CopyLockList(locking.ALL_SET),
1045
                     locking.ALL_SET)
1046

  
1047
    names = ["foo", "bar"]
1048
    output = instance_utils.CopyLockList(names)
1049
    self.assertEqual(names, output)
1050
    self.assertNotEqual(id(names), id(output), msg="List was not copied")
1051

  
1052

  
1801 1053
if __name__ == "__main__":
1802 1054
  testutils.GanetiTestProgram()

Also available in: Unified diff