def test_lv_reload_for_stale_vg(fake_devices, no_delay): fake_runner = FakeRunner() lc = lvm.LVMCache(fake_runner, cache_lvs=True) assert lc._lvs_needs_reload("vg") # getLv call should call reload lvs. lc.getLv("vg") assert not lc._lvs_needs_reload("vg")
def test_suppress_warnings(fake_devices, fake_runner): fake_runner.err = [ b" before", b" WARNING: This metadata update is NOT backed up.", b" after", ] lc = lvm.LVMCache() rc, out, err = lc.cmd(["fake"]) assert rc == 0 assert err == [b" before", b" after"]
def test_build_command_read_only(fake_devices): # When cache in read-write mode, use locking_type=1 lc = lvm.LVMCache() cmd = lc._addExtraCfg(["lvs", "-o", "+tags"]) assert " locking_type=1 " in cmd[3] # When cache in read-only mode, use locking_type=4 lc.set_read_only(True) cmd = lc._addExtraCfg(["lvs", "-o", "+tags"]) assert " locking_type=4 " in cmd[3]
def test_cmd_read_only_max_retries(fake_devices, fake_runner): lc = lvm.LVMCache() lc.set_read_only(True) # Require max retries to succeed. fake_runner.retries = lc.READ_ONLY_RETRIES rc, out, err = lc.cmd(["fake"]) # Call should succeed (1 call + max retries). assert rc == 0 assert len(fake_runner.calls) == lc.READ_ONLY_RETRIES + 1 assert len(set(repr(c) for c in fake_runner.calls)) == 1
def test_suppress_warnings(fake_devices, fake_runner): fake_runner.err = [ b" before", b" WARNING: This metadata update is NOT backed up.", b" WARNING: Combining activation change with other commands is " b"not advised.", b" after", ] lc = lvm.LVMCache() rc, out, err = lc.cmd(["fake"]) assert rc == 0 assert err == [b" before", b" after"]
def test_suppress_warnings(fake_devices, no_delay): fake_runner = FakeRunner() fake_runner.err = b"""\ before WARNING: This metadata update is NOT backed up. WARNING: Combining activation change with other commands is not advised. after""" lc = lvm.LVMCache(fake_runner) rc, out, err = lc.cmd(["fake"]) assert rc == 0 assert err == [u" before", u" after"]
def test_lv_reload_error_one(fake_devices, no_delay): fake_runner = FakeRunner(rc=5, err=b"Fake lvm error") lc = lvm.LVMCache(fake_runner) other_lv = make_lv("other-lv", "vg-name") lc._lvs = {("vg-name", "other-lv"): other_lv} # Should raise, but currently return None. assert lc.getLv("vg-name", "lv-name") is None # Other lv is not affected since it was not a stale. assert lc._lvs == {("vg-name", "other-lv"): other_lv}
def test_lv_reload_error_all_stale_other_vgs(fake_devices, no_delay): fake_runner = FakeRunner(rc=5, err=b"Fake lvm error") lc = lvm.LVMCache(fake_runner) lc._lvs = { ("vg-name", "lv-name"): lvm.Stale("lv-name"), ("other-vg", "other-lv"): lvm.Stale("other-lv"), } lc.getLv("vg-name") # Should not affect other vg lvs. other_lv = lc._lvs[("other-vg", "other-lv")] assert not isinstance(other_lv, lvm.Unreadable)
def test_rebuild_filter_after_invaliation(fake_devices): # Check that adding a device and invalidating the filter rebuilds the # config with the correct filter. lc = lvm.LVMCache() lc._addExtraCfg(["lvs"]) fake_devices.append("/dev/mapper/c") lc.invalidateFilter() cmd = lc._addExtraCfg(["lvs"]) assert cmd[3] == lvm._buildConfig( dev_filter=lvm._buildFilter(fake_devices), locking_type="1")
def test_lv_reload_error_all_other_vg(fake_devices, no_delay): fake_runner = FakeRunner(rc=5, err=b"Fake lvm error") lc = lvm.LVMCache(fake_runner) lc._lvs = {("vg-name", "lv-name"): lvm.Stale("lv-name")} lvs = lc.getLv("vg-name") # Mark lv as unreadable. assert lc._lvs == {("vg-name", "lv-name"): lvm.Unreadable("lv-name")} # Currnetly we don't report stales or unreadables lvs. This is not # consistent with getLv(vg_name, lv_name). assert lvs == []
def test_cmd_read_only_max_retries_fail(fake_devices, fake_runner): lc = lvm.LVMCache() lc.set_read_only(True) # Require max retries + 1 to succeed. fake_runner.retries = lc.READ_ONLY_RETRIES + 1 rc, out, err = lc.cmd(["fake"]) # Call should fail (1 call + max retries). assert rc == 1 assert len(fake_runner.calls) == lc.READ_ONLY_RETRIES + 1
def test_suppress_multiple_lvm_warnings(fake_devices, no_delay): fake_runner = FakeRunner() fake_runner.err = b"""\ before Configuration setting "global/event_activation" unknown. Configuration setting "global/event_activation" unknown. Configuration setting "global/event_activation" unknown. after""" lc = lvm.LVMCache(fake_runner) rc, out, err = lc.cmd(["fake"]) assert rc == 0 assert err == [u" before", u" after"]
def test_cmd_error(fake_devices, fake_runner): lc = lvm.LVMCache() # Require 2 calls to succeed. assert lc.READ_ONLY_RETRIES > 1 fake_runner.retries = 1 # Since the filter is correct, the error should be propagated to the caller # after the first call. rc, out, err = lc.cmd(["lvs", "-o", "+tags"]) assert rc == 1 assert len(fake_runner.calls) == 1
def test_suppress_multiple_lvm_warnings(fake_devices, no_delay): fake_runner = FakeRunner() fake_runner.err = b"""\ before WARNING: This metadata update is NOT backed up. WARNING: This metadata update is NOT backed up. WARNING: This metadata update is NOT backed up. after""" lc = lvm.LVMCache(fake_runner) rc, out, err = lc.cmd(["fake"]) assert rc == 0 assert err == [u" before", u" after"]
def test_cmd_read_only(fake_devices, fake_runner): lc = lvm.LVMCache() lc.set_read_only(True) # Require 3 calls to succeed. assert lc.READ_ONLY_RETRIES > 2 fake_runner.retries = 2 rc, out, err = lc.cmd(["fake"]) # Call should succeed after 3 identical calls. assert rc == 0 assert len(fake_runner.calls) == 3 assert len(set(repr(c) for c in fake_runner.calls)) == 1
def test_build_command_long_filter(fake_devices): # If the devices are not specified, include all devices reported by # multipath. lc = lvm.LVMCache() cmd = lc._addExtraCfg(["lvs", "-o", "+tags"]) assert cmd == [ constants.EXT_LVM, "lvs", "--config", lvm._buildConfig( dev_filter=lvm._buildFilter(fake_devices), locking_type="1"), "-o", "+tags", ]
def test_cmd_read_only_filter_stale(fake_devices, fake_runner): # Make a call to load the cache. initial_devices = fake_devices[:] lc = lvm.LVMCache() lc.cmd(["fake"]) del fake_runner.calls[:] # Add a new device to the system. This will makes the cached filter stale, # so the command will be retried with a new filter. fake_devices.append("/dev/mapper/c") # Require max retries + 1 calls to succeed. fake_runner.retries = lc.READ_ONLY_RETRIES + 1 lc.set_read_only(True) rc, out, err = lc.cmd(["fake"]) # Call should succeed after one call with stale filter, one call with wider # filter and max retries identical calls. assert rc == 0 assert len(fake_runner.calls) == lc.READ_ONLY_RETRIES + 2 # The first call used the stale cache filter. cmd, kwargs = fake_runner.calls[0] assert cmd == [ constants.EXT_LVM, "fake", "--config", lvm._buildConfig( dev_filter=lvm._buildFilter(initial_devices), locking_type="4"), ] assert kwargs == {"sudo": True} # The seocnd call used a wider filter. cmd, kwargs = fake_runner.calls[1] assert cmd == [ constants.EXT_LVM, "fake", "--config", lvm._buildConfig( dev_filter=lvm._buildFilter(fake_devices), locking_type="4"), ] assert kwargs == {"sudo": True} # And then indentical retries with the wider filter. assert len(set(repr(c) for c in fake_runner.calls[1:])) == 1
def test_cmd_success(fake_devices, no_delay): fake_runner = FakeRunner() lc = lvm.LVMCache(fake_runner) rc, out, err = lc.cmd(["lvs", "-o", "+tags"]) assert rc == 0 assert len(fake_runner.calls) == 1 cmd = fake_runner.calls[0] assert cmd == [ constants.EXT_LVM, "lvs", "--config", lvm._buildConfig( dev_filter=lvm._buildFilter(fake_devices), locking_type="1"), "-o", "+tags", ]
def test_lv_reload_error_one_stale(fake_devices, no_delay): fake_runner = FakeRunner(rc=5, err=b"Fake lvm error") lc = lvm.LVMCache(fake_runner) lc._lvs = { ("vg-name", "lv-name"): lvm.Stale("lv-name"), ("vg-name", "other-lv"): lvm.Stale("other-lv"), } lv = lc.getLv("vg-name", "lv-name") # Mark lv as unreadable. Because we always reload all lvs, the other lvs is # also marked as unreadable. assert lc._lvs == { ("vg-name", "lv-name"): lvm.Unreadable("lv-name"), ("vg-name", "other-lv"): lvm.Unreadable("other-lv"), } # Report the unreadbale lv. assert lv.name == "lv-name" assert isinstance(lv, lvm.Unreadable)
def test_cmd_retry_filter_stale(fake_devices, fake_runner): # Make a call to load the cache. initial_devices = fake_devices[:] lc = lvm.LVMCache() lc.cmd(["fake"]) del fake_runner.calls[:] # Add a new device to the system. This will makes the cached filter stale, # so the command will be retried with a new filter. fake_devices.append("/dev/mapper/c") # Require 2 calls to succeed. assert lc.READ_ONLY_RETRIES > 1 fake_runner.retries = 1 rc, out, err = lc.cmd(["fake"]) assert rc == 0 assert len(fake_runner.calls) == 2 # The first call used the stale cache filter. cmd, kwargs = fake_runner.calls[0] assert cmd == [ constants.EXT_LVM, "fake", "--config", lvm._buildConfig( dev_filter=lvm._buildFilter(initial_devices), locking_type="1"), ] assert kwargs == {"sudo": True} # The seocnd call used a wider filter. cmd, kwargs = fake_runner.calls[1] assert cmd == [ constants.EXT_LVM, "fake", "--config", lvm._buildConfig( dev_filter=lvm._buildFilter(fake_devices), locking_type="1"), ] assert kwargs == {"sudo": True}
def test_cmd_success(fake_devices, fake_runner): lc = lvm.LVMCache() rc, out, err = lc.cmd(["lvs", "-o", "+tags"]) assert rc == 0 assert len(fake_runner.calls) == 1 cmd, kwargs = fake_runner.calls[0] assert cmd == [ constants.EXT_LVM, "lvs", "--config", lvm._buildConfig( dev_filter=lvm._buildFilter(fake_devices), locking_type="1"), "-o", "+tags", ] assert kwargs == {"sudo": True}
def test_cmd_read_only_filter_stale_fail(fake_devices, fake_runner): # Make a call to load the cache. lc = lvm.LVMCache() lc.cmd(["fake"]) del fake_runner.calls[:] # Add a new device to the system. This will makes the cached filter stale, # so the command will be retried with a new filter. fake_devices.append("/dev/mapper/c") # Require max retries + 2 calls to succeed. fake_runner.retries = lc.READ_ONLY_RETRIES + 2 lc.set_read_only(True) rc, out, err = lc.cmd(["fake"]) # Call should fail after max retries + 2 calls. assert rc == 1 assert len(fake_runner.calls) == lc.READ_ONLY_RETRIES + 2
def test_change_read_only_mode(fake_devices, no_delay, workers): # Test that changing read only wait for running commands, and new commands # wait for the read only change. fake_runner = FakeRunner() lc = lvm.LVMCache(fake_runner) def run_after(delay, func, *args): time.sleep(delay) func(*args) fake_runner.delay = 0.3 start = time.time() try: # Start few commands in read-write mode. for i in range(2): workers.start_thread(run_after, 0.0, lc.cmd, ["read-write"]) # After 0.1 seconds change read only mode to True. Should wait for the # running commands before changing the mode. workers.start_thread(run_after, 0.1, lc.set_read_only, True) # After 0.2 seconds, start new commands. Should wait until the mode is # changed and run in read-only mode. for i in range(2): workers.start_thread(run_after, 0.2, lc.cmd, ["read-only"]) finally: workers.join() elapsed = time.time() - start assert len(fake_runner.calls) == 4 # The first 2 commands should run in read-write mode. for cmd in fake_runner.calls[:2]: assert " locking_type=1 " in cmd[3] # The last 2 command should run in not read-only mode. for cmd in fake_runner.calls[2:]: assert " locking_type=4 " in cmd[3] # The last 2 command can start only after the first 2 command finished. assert elapsed > fake_runner.delay * 2
def test_command_concurrency(fake_devices, fake_runner, workers, read_only): # Test concurrent commands to reveal locking issues. lc = lvm.LVMCache() lc.set_read_only(read_only) fake_runner.delay = 0.2 count = 50 start = time.time() try: for i in range(count): workers.start_thread(lc.cmd, ["fake", i]) finally: workers.join() elapsed = time.time() - start assert len(fake_runner.calls) == count # This takes about 1 second on my idle laptop. Add more time to avoid # failures on overloaded slave. assert elapsed < fake_runner.delay * count / lc.MAX_COMMANDS + 1.0
def test_suppress_warnings(fake_devices, no_delay): fake_runner = FakeRunner() fake_runner.err = b"""\ before WARNING: This metadata update is NOT backed up. WARNING: Combining activation change with other commands is not advised. Configuration setting "global/event_activation" unknown. WARNING: ignoring metadata seqno 1566 on /dev/mapper/3600a098038304437415d4b6a59684474 for seqno 1567 on /dev/mapper/3600a098038304437415d4b6a59684474 for VG Bug." WARNING: Inconsistent metadata found for VG Bug." after""" # NOQA: E501 (potentially long line) lc = lvm.LVMCache(fake_runner) rc, out, err = lc.cmd(["fake"]) assert rc == 0 assert err == [ u" before", (u" WARNING: Combining activation change with other commands is " "not advised."), u" Configuration setting \"global/event_activation\" unknown.", u" after" ]
def test_lv_reload_fresh_vg(fake_devices, no_delay): fake_runner = FakeRunner() lc = lvm.LVMCache(fake_runner, cache_lvs=True) lv1 = make_lv("lv1", "vg1") # vg1's lvs are fresh, vg2's lvs were invalidated. lc._freshlv = {"vg1", "vg2"} lc._lvs = { ("vg1", "lv1"): lv1, ("vg2", "lv2"): lvm.Stale("lv2"), } assert not lc._lvs_needs_reload("vg1") assert lc._lvs_needs_reload("vg2") # getLv for vg1 should use cache without reload lvs. assert lc.getLv("vg1") == [lv1] assert not lc._lvs_needs_reload("vg1") assert lc._lvs_needs_reload("vg2") # getLv for vg2 should reload lvs. assert lc.getLv("vg2") == [] assert not lc._lvs_needs_reload("vg1") assert not lc._lvs_needs_reload("vg2")
def test_lv_reload_error_all(fake_devices, no_delay): fake_runner = FakeRunner(rc=5, err=b"Fake lvm error") lc = lvm.LVMCache(fake_runner) assert lc.getLv("vg-name") == []