def test_lvm_resize(self): # LVM resize should work only if a single device is configured. More # than one device should fail. lvm_pass = ["/dev/XXdm-0"] lvm_fail = ["/dev/XXdm-1", "/dev/YYdm-1"] devstat_ret = Bunch(st_mode=25008, st_ino=6078, st_dev=5, st_nlink=1, st_uid=0, st_gid=6, st_size=0, st_atime=0, st_mtime=0, st_ctime=0) real_stat = os.stat resize_calls = [] class myresizer(object): def resize(self, diskdev, partnum, partdev): resize_calls.append((diskdev, partnum, partdev)) if partdev == "/dev/XXdm-0": return (1024, 2048) return (1024, 1024) # old size, new size def mystat(path): if path in lvm_pass or path in lvm_fail: return devstat_ret return real_stat(path) try: opinfo = cc_growpart.device_part_info cc_growpart.device_part_info = simple_device_part_info_lvm os.stat = mystat resized = cc_growpart.resize_devices(myresizer(), lvm_pass) not_resized = cc_growpart.resize_devices(myresizer(), lvm_fail) def find(name, res): for f in res: if f[0] == name: return f return None self.assertEqual(cc_growpart.RESIZE.CHANGED, find("/dev/XXdm-0", resized)[1]) self.assertEqual(cc_growpart.RESIZE.NOCHANGE, find("/dev/XXdm-1", not_resized)[1]) self.assertEqual(cc_growpart.RESIZE.NOCHANGE, find("/dev/YYdm-1", not_resized)[1]) finally: cc_growpart.device_part_info = opinfo os.stat = real_stat
def test_simple_devices(self): # test simple device list # this patches out devent2dev, os.stat, and device_part_info # so in the end, doesn't test a lot devs = ["/dev/XXda1", "/dev/YYda2"] devstat_ret = Bunch(st_mode=25008, st_ino=6078, st_dev=5, st_nlink=1, st_uid=0, st_gid=6, st_size=0, st_atime=0, st_mtime=0, st_ctime=0) enoent = ["/dev/NOENT"] real_stat = os.stat resize_calls = [] class myresizer(object): def resize(self, diskdev, partnum, partdev): resize_calls.append((diskdev, partnum, partdev)) if partdev == "/dev/YYda2": return (1024, 2048) return (1024, 1024) # old size, new size def mystat(path): if path in devs: return devstat_ret if path in enoent: e = OSError("%s: does not exist" % path) e.errno = errno.ENOENT raise e return real_stat(path) try: opinfo = cc_growpart.device_part_info cc_growpart.device_part_info = simple_device_part_info os.stat = mystat resized = cc_growpart.resize_devices(myresizer(), devs + enoent) def find(name, res): for f in res: if f[0] == name: return f return None self.assertEqual(cc_growpart.RESIZE.NOCHANGE, find("/dev/XXda1", resized)[1]) self.assertEqual(cc_growpart.RESIZE.CHANGED, find("/dev/YYda2", resized)[1]) self.assertEqual(cc_growpart.RESIZE.SKIPPED, find(enoent[0], resized)[1]) # self.assertEqual(resize_calls, # [("/dev/XXda", "1", "/dev/XXda1"), # ("/dev/YYda", "2", "/dev/YYda2")]) finally: cc_growpart.device_part_info = opinfo os.stat = real_stat
def test_resize_failed(self, common_mocks, mocker, caplog): def _subp_side_effect(value, **kwargs): if value[0] == "dmsetup": return ("1 dependencies : (vdx1)", ) elif value[0] == "cryptsetup" and "resize" in value: raise subp.ProcessExecutionError() return mock.Mock() self.m_subp = mocker.patch( "cloudinit.config.cc_growpart.subp.subp", side_effect=_subp_side_effect, ) info = cc_growpart.resize_devices(self.resizer, ["/fake_encrypted"]) assert len(info) == 2 assert info[0][0] == "/dev/vdx1" assert info[0][2].startswith("no change necessary") assert info[1][0] == "/fake_encrypted" assert info[1][1] == "FAILED" assert ("Resizing encrypted device (/dev/mapper/fake) failed" in info[1][2]) # Assert we still cleanup all_subp_args = list( chain(*[args[0][0] for args in self.m_subp.call_args_list])) assert "luksKillSlot" in all_subp_args self.m_unlink.assert_called_once()
def test_resize_skipped(self, common_mocks, mocker, caplog): mocker.patch("pathlib.Path.exists", return_value=False) info = cc_growpart.resize_devices(self.resizer, ["/fake_encrypted"]) assert len(info) == 2 assert info[1] == ( "/fake_encrypted", "SKIPPED", "No encryption keyfile found", )
def test_simple_devices(self): # test simple device list # this patches out devent2dev, os.stat, and device_part_info # so in the end, doesn't test a lot devs = ["/dev/XXda1", "/dev/YYda2"] devstat_ret = Bunch(st_mode=25008, st_ino=6078, st_dev=5L, st_nlink=1, st_uid=0, st_gid=6, st_size=0, st_atime=0, st_mtime=0, st_ctime=0) enoent = ["/dev/NOENT"] real_stat = os.stat resize_calls = [] class myresizer(object): def resize(self, diskdev, partnum, partdev): resize_calls.append((diskdev, partnum, partdev)) if partdev == "/dev/YYda2": return (1024, 2048) return (1024, 1024) # old size, new size def mystat(path): if path in devs: return devstat_ret if path in enoent: e = OSError("%s: does not exist" % path) e.errno = errno.ENOENT raise e return real_stat(path) try: opinfo = cc_growpart.device_part_info cc_growpart.device_part_info = simple_device_part_info os.stat = mystat resized = cc_growpart.resize_devices(myresizer(), devs + enoent) def find(name, res): for f in res: if f[0] == name: return f return None self.assertEqual(cc_growpart.RESIZE.NOCHANGE, find("/dev/XXda1", resized)[1]) self.assertEqual(cc_growpart.RESIZE.CHANGED, find("/dev/YYda2", resized)[1]) self.assertEqual(cc_growpart.RESIZE.SKIPPED, find(enoent[0], resized)[1]) # self.assertEqual(resize_calls, # [("/dev/XXda", "1", "/dev/XXda1"), # ("/dev/YYda", "2", "/dev/YYda2")]) finally: cc_growpart.device_part_info = opinfo os.stat = real_stat
def test_encrypted_but_cryptsetup_not_found(self, common_mocks, mocker, caplog): mocker.patch( "cloudinit.config.cc_growpart.subp.which", return_value=None, ) info = cc_growpart.resize_devices(self.resizer, ["/fake_encrypted"]) assert len(info) == 1 assert "skipped as it is not encrypted" in info[0][2] assert "cryptsetup not found" in caplog.text self.assert_no_resize_or_cleanup()
def test_resize_when_encrypted(self, common_mocks, caplog): info = cc_growpart.resize_devices(self.resizer, ["/fake_encrypted"]) assert len(info) == 2 assert info[0][0] == "/dev/vdx1" assert info[0][2].startswith("no change necessary") assert info[1][0] == "/fake_encrypted" assert (info[1][2] == "Successfully resized encrypted volume '/dev/mapper/fake'") assert ("/dev/mapper/fake is a mapped device pointing to /dev/dm-1" in caplog.text) assert "Determined that /dev/dm-1 is encrypted" in caplog.text self.assert_resize_and_cleanup()
def test_missing_keydata(self, common_mocks, mocker, caplog): # Note that this will be standard behavior after first boot # on a system with an encrypted root partition mocker.patch("pathlib.Path.open", side_effect=FileNotFoundError()) info = cc_growpart.resize_devices(self.resizer, ["/fake_encrypted"]) assert len(info) == 2 assert info[0][0] == "/dev/vdx1" assert info[0][2].startswith("no change necessary") assert info[1][0] == "/fake_encrypted" assert info[1][1] == "FAILED" assert (info[1][2] == "Resizing encrypted device (/dev/mapper/fake) failed: Could " "not load encryption key. This is expected if the volume has " "been previously resized.") self.assert_no_resize_or_cleanup()
def test_dmsetup_not_found(self, common_mocks, mocker, caplog): def _subp_side_effect(value, **kwargs): if value[0] == "dmsetup": raise subp.ProcessExecutionError() mocker.patch( "cloudinit.config.cc_growpart.subp.subp", side_effect=_subp_side_effect, ) info = cc_growpart.resize_devices(self.resizer, ["/fake_encrypted"]) assert len(info) == 1 assert info[0][0] == "/fake_encrypted" assert info[0][1] == "FAILED" assert ("Resizing encrypted device (/dev/mapper/fake) failed" in info[0][2]) self.assert_no_resize_or_cleanup()
def test_unparsable_dmsetup(self, common_mocks, mocker, caplog): def _subp_side_effect(value, **kwargs): if value[0] == "dmsetup": return ("2 dependencies", ) return mock.Mock() mocker.patch( "cloudinit.config.cc_growpart.subp.subp", side_effect=_subp_side_effect, ) info = cc_growpart.resize_devices(self.resizer, ["/fake_encrypted"]) assert len(info) == 1 assert info[0][0] == "/fake_encrypted" assert info[0][1] == "FAILED" assert ("Resizing encrypted device (/dev/mapper/fake) failed" in info[0][2]) self.assert_no_resize_or_cleanup()
def test_resize_when_unencrypted(self, common_mocks): info = cc_growpart.resize_devices(self.resizer, ["/"]) assert len(info) == 1 assert info[0][0] == "/" assert "encrypted" not in info[0][2] self.assert_no_resize_or_cleanup()