/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.apache.geode.management; import org.junit.experimental.categories.Category; import org.junit.Test; import static org.junit.Assert.*; import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase; import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase; import org.apache.geode.test.junit.categories.DistributedTest; import java.io.File; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; import javax.management.ObjectName; import org.apache.geode.LogWriter; import org.apache.geode.cache.Cache; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.DiskStore; import org.apache.geode.cache.DiskStoreFactory; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionFactory; import org.apache.geode.cache.Scope; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.internal.cache.DiskRegion; import org.apache.geode.internal.cache.DiskRegionStats; import org.apache.geode.internal.cache.DistributedRegion; import org.apache.geode.internal.cache.GemFireCacheImpl; import org.apache.geode.internal.cache.LocalRegion; import org.apache.geode.internal.cache.persistence.PersistentMemberID; import org.apache.geode.internal.cache.persistence.PersistentMemberManager; import org.apache.geode.management.internal.MBeanJMXAdapter; import org.apache.geode.test.dunit.AsyncInvocation; import org.apache.geode.test.dunit.SerializableCallable; import org.apache.geode.test.dunit.SerializableRunnable; import org.apache.geode.test.dunit.VM; import org.apache.geode.test.dunit.Wait; import org.apache.geode.test.dunit.WaitCriterion; /** * Test cases to cover all test cases which pertains to disk from Management layer * * */ @Category(DistributedTest.class) public class DiskManagementDUnitTest extends ManagementTestBase { /** * */ private static final long serialVersionUID = 1L; // This must be bigger than the dunit ack-wait-threshold for the revoke // tests. The command line is setting the ack-wait-threshold to be // 60 seconds. private static final int MAX_WAIT = 70 * 1000; boolean testFailed = false; String failureCause = ""; static final String REGION_NAME = "region"; private File diskDir; protected static LogWriter logWriter; public DiskManagementDUnitTest() throws Exception { super(); diskDir = new File("diskDir-" + getName()).getAbsoluteFile(); org.apache.geode.internal.FileUtil.delete(diskDir); diskDir.mkdir(); diskDir.deleteOnExit(); } @Override protected final void postSetUpManagementTestBase() throws Exception { failureCause = ""; testFailed = false; } @Override protected final void postTearDownManagementTestBase() throws Exception { org.apache.geode.internal.FileUtil.delete(diskDir); } /** * Tests Disk Compaction from a MemberMbean which is at cache level. All the disks which belong to * the cache should be compacted. * * @throws Exception */ @Test public void testDiskCompact() throws Throwable { initManagement(false); for (VM vm : getManagedNodeList()) { createPersistentRegion(vm); makeDiskCompactable(vm); } for (VM vm : getManagedNodeList()) { compactAllDiskStores(vm); } } /** * Tests Disk Compaction from a MemberMbean which is at cache level. All the disks which belong to * the cache should be compacted. * * @throws Exception */ @Test public void testDiskCompactRemote() throws Throwable { initManagement(false); for (VM vm : getManagedNodeList()) { createPersistentRegion(vm); makeDiskCompactable(vm); } compactDiskStoresRemote(managingNode); } /** * Tests various operations defined on DiskStore Mbean * * @throws Exception */ @Test public void testDiskOps() throws Throwable { initManagement(false); for (VM vm : getManagedNodeList()) { createPersistentRegion(vm); makeDiskCompactable(vm); invokeFlush(vm); invokeForceRoll(vm); invokeForceCompaction(vm); } } @Test public void testDiskBackupAllMembers() throws Throwable { initManagement(false); for (VM vm : getManagedNodeList()) { createPersistentRegion(vm); makeDiskCompactable(vm); } backupAllMembers(managingNode); } /** * Checks the test case of missing disks and revoking them through MemberMbean interfaces * * @throws Throwable */ @SuppressWarnings("serial") @Test public void testMissingMembers() throws Throwable { initManagement(false); VM vm0 = getManagedNodeList().get(0); VM vm1 = getManagedNodeList().get(1); VM vm2 = getManagedNodeList().get(2); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter().info("Creating region in VM0"); createPersistentRegion(vm0); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter().info("Creating region in VM1"); createPersistentRegion(vm1); putAnEntry(vm0); managingNode.invoke(new SerializableRunnable("Check for waiting regions") { public void run() { Cache cache = getCache(); ManagementService service = getManagementService(); DistributedSystemMXBean bean = service.getDistributedSystemMXBean(); PersistentMemberDetails[] missingDiskStores = bean.listMissingDiskStores(); assertNull(missingDiskStores); } }); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter().info("closing region in vm0"); closeRegion(vm0); updateTheEntry(vm1); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter().info("closing region in vm1"); closeRegion(vm1); AsyncInvocation future = createPersistentRegionAsync(vm0); waitForBlockedInitialization(vm0); assertTrue(future.isAlive()); managingNode.invoke(new SerializableRunnable("Revoke the member") { public void run() { Cache cache = getCache(); GemFireCacheImpl cacheImpl = (GemFireCacheImpl) cache; ManagementService service = getManagementService(); DistributedSystemMXBean bean = service.getDistributedSystemMXBean(); PersistentMemberDetails[] missingDiskStores = bean.listMissingDiskStores(); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter() .info("waiting members=" + missingDiskStores); assertNotNull(missingDiskStores); assertEquals(1, missingDiskStores.length); for (PersistentMemberDetails id : missingDiskStores) { org.apache.geode.test.dunit.LogWriterUtils.getLogWriter() .info("Missing DiskStoreID is =" + id.getDiskStoreId()); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter() .info("Missing Host is =" + id.getHost()); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter() .info("Missing Directory is =" + id.getDirectory()); try { bean.revokeMissingDiskStores(id.getDiskStoreId()); } catch (Exception e) { fail("revokeMissingDiskStores failed with exception " + e); } } } }); future.join(MAX_WAIT); if (future.isAlive()) { fail("Region not created within" + MAX_WAIT); } if (future.exceptionOccurred()) { throw new Exception(future.getException()); } checkForRecoveryStat(vm0, true); // Check to make sure we recovered the old // value of the entry. SerializableRunnable checkForEntry = new SerializableRunnable("check for the entry") { public void run() { Cache cache = getCache(); Region region = cache.getRegion(REGION_NAME); assertEquals("B", region.get("A")); } }; vm0.invoke(checkForEntry); } protected void checkNavigation(final VM vm, final DistributedMember diskMember, final String diskStoreName) { SerializableRunnable checkNavigation = new SerializableRunnable("Check Navigation") { public void run() { final ManagementService service = getManagementService(); DistributedSystemMXBean disMBean = service.getDistributedSystemMXBean(); try { ObjectName expected = MBeanJMXAdapter.getDiskStoreMBeanName(diskMember.getId(), diskStoreName); ObjectName actual = disMBean.fetchDiskStoreObjectName(diskMember.getId(), diskStoreName); assertEquals(expected, actual); } catch (Exception e) { fail("Disk Store Navigation Failed " + e); } } }; vm.invoke(checkNavigation); } /** * get Distributed member for a given vm */ @SuppressWarnings("serial") protected static DistributedMember getMember() throws Exception { GemFireCacheImpl cache = GemFireCacheImpl.getInstance(); return cache.getDistributedSystem().getDistributedMember(); } /** * Invokes flush on the given disk store by MBean interface * * @param vm reference to VM */ @SuppressWarnings("serial") public void invokeFlush(final VM vm) { SerializableRunnable invokeFlush = new SerializableRunnable("Invoke Flush On Disk") { public void run() { Cache cache = getCache(); DiskStoreFactory dsf = cache.createDiskStoreFactory(); String name = "testFlush_" + vm.getPid(); DiskStore ds = dsf.create(name); ManagementService service = getManagementService(); DiskStoreMXBean bean = service.getLocalDiskStoreMBean(name); assertNotNull(bean); bean.flush(); } }; vm.invoke(invokeFlush); } /** * Invokes force roll on disk store by MBean interface * * @param vm reference to VM */ @SuppressWarnings("serial") public void invokeForceRoll(final VM vm) { SerializableRunnable invokeForceRoll = new SerializableRunnable("Invoke Force Roll") { public void run() { Cache cache = getCache(); DiskStoreFactory dsf = cache.createDiskStoreFactory(); String name = "testForceRoll_" + vm.getPid(); DiskStore ds = dsf.create(name); ManagementService service = getManagementService(); DiskStoreMXBean bean = service.getLocalDiskStoreMBean(name); assertNotNull(bean); bean.forceRoll(); } }; vm.invoke(invokeForceRoll); } /** * Invokes force compaction on disk store by MBean interface * * @param vm reference to VM */ @SuppressWarnings("serial") public void invokeForceCompaction(final VM vm) { SerializableRunnable invokeForceCompaction = new SerializableRunnable("Invoke Force Compaction") { public void run() { Cache cache = getCache(); DiskStoreFactory dsf = cache.createDiskStoreFactory(); dsf.setAllowForceCompaction(true); String name = "testForceCompaction_" + vm.getPid(); DiskStore ds = dsf.create(name); ManagementService service = getManagementService(); DiskStoreMXBean bean = service.getLocalDiskStoreMBean(name); assertNotNull(bean); assertEquals(false, bean.forceCompaction()); } }; vm.invoke(invokeForceCompaction); } /** * Makes the disk compactable by adding and deleting some entries * * @throws Exception */ @SuppressWarnings("serial") public void makeDiskCompactable(VM vm1) throws Exception { vm1.invoke(new SerializableRunnable("Make The Disk Compactable") { public void run() { Cache cache = getCache(); Region region = cache.getRegion(REGION_NAME); DiskRegion dr = ((LocalRegion) region).getDiskRegion(); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter().info("putting key1"); region.put("key1", "value1"); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter().info("putting key2"); region.put("key2", "value2"); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter().info("removing key2"); region.remove("key2"); // now that it is compactable the following forceCompaction should // go ahead and do a roll and compact it. } }); } /** * Compacts all DiskStores belonging to a member * * @param vm1 reference to VM * @throws Exception */ @SuppressWarnings("serial") public void compactAllDiskStores(VM vm1) throws Exception { vm1.invoke(new SerializableCallable("Compact All Disk Stores") { public Object call() throws Exception { ManagementService service = getManagementService(); MemberMXBean memberBean = service.getMemberMXBean(); String[] compactedDiskStores = memberBean.compactAllDiskStores(); assertTrue(compactedDiskStores.length > 0); for (int i = 0; i < compactedDiskStores.length; i++) { org.apache.geode.test.dunit.LogWriterUtils.getLogWriter() .info("<ExpectedString> Compacted Store " + i + " " + compactedDiskStores[i] + "</ExpectedString> "); } return null; } }); } /** * Takes a back up of all the disk store in a given directory */ @SuppressWarnings("serial") public void backupAllMembers(final VM managingVM) throws Exception { managingVM.invoke(new SerializableCallable("Backup All Disk Stores") { public Object call() throws Exception { ManagementService service = getManagementService(); DistributedSystemMXBean bean = service.getDistributedSystemMXBean(); DiskBackupStatus status = bean.backupAllMembers(getBackupDir("test_backupAllMembers").getAbsolutePath(), null); return null; } }); } /** * Compact a disk store from Managing node */ @SuppressWarnings("serial") public void compactDiskStoresRemote(VM managingVM) throws Exception { { managingVM.invoke(new SerializableCallable("Compact All Disk Stores Remote") { public Object call() throws Exception { GemFireCacheImpl cache = GemFireCacheImpl.getInstance(); Set<DistributedMember> otherMemberSet = cache.getDistributionManager().getOtherNormalDistributionManagerIds(); for (DistributedMember member : otherMemberSet) { MemberMXBean bean = MBeanUtil.getMemberMbeanProxy(member); String[] allDisks = bean.listDiskStores(true); assertNotNull(allDisks); List<String> listString = Arrays.asList(allDisks); org.apache.geode.test.dunit.LogWriterUtils.getLogWriter() .info("<ExpectedString> Remote All Disk Stores Are " + listString.toString() + "</ExpectedString> "); String[] compactedDiskStores = bean.compactAllDiskStores(); assertTrue(compactedDiskStores.length > 0); for (int i = 0; i < compactedDiskStores.length; i++) { org.apache.geode.test.dunit.LogWriterUtils.getLogWriter() .info("<ExpectedString> Remote Compacted Store " + i + " " + compactedDiskStores[i] + "</ExpectedString> "); } } return null; } }); } } /** * Checks if a file with the given extension is present * * @param fileExtension file extension * @throws Exception */ protected void checkIfContainsFileWithExt(String fileExtension) throws Exception { File[] files = diskDir.listFiles(); for (int j = 0; j < files.length; j++) { if (files[j].getAbsolutePath().endsWith(fileExtension)) { fail("file \"" + files[j].getAbsolutePath() + "\" still exists"); } } } /** * Update Entry * * @param vm1 reference to VM */ protected void updateTheEntry(VM vm1) { updateTheEntry(vm1, "C"); } /** * Update an Entry * * @param vm1 reference to VM * @param value Value which is updated */ @SuppressWarnings("serial") protected void updateTheEntry(VM vm1, final String value) { vm1.invoke(new SerializableRunnable("change the entry") { public void run() { Cache cache = getCache(); Region region = cache.getRegion(REGION_NAME); region.put("A", value); } }); } /** * Put an entry to region * * @param vm0 reference to VM */ @SuppressWarnings("serial") protected void putAnEntry(VM vm0) { vm0.invoke(new SerializableRunnable("Put an entry") { public void run() { Cache cache = getCache(); Region region = cache.getRegion(REGION_NAME); region.put("A", "B"); } }); } /** * Close the given region REGION_NAME * * @param vm reference to VM */ @SuppressWarnings("serial") protected void closeRegion(final VM vm) { SerializableRunnable closeRegion = new SerializableRunnable("Close persistent region") { public void run() { Cache cache = getCache(); Region region = cache.getRegion(REGION_NAME); region.close(); } }; vm.invoke(closeRegion); } /** * Waiting to blocked waiting for another persistent member to come online * * @param vm reference to VM */ @SuppressWarnings("serial") private void waitForBlockedInitialization(VM vm) { vm.invoke(new SerializableRunnable() { public void run() { Wait.waitForCriterion(new WaitCriterion() { public String description() { return "Waiting to blocked waiting for another persistent member to come online"; } public boolean done() { Cache cache = getCache(); GemFireCacheImpl cacheImpl = (GemFireCacheImpl) cache; PersistentMemberManager mm = cacheImpl.getPersistentMemberManager(); Map<String, Set<PersistentMemberID>> regions = mm.getWaitingRegions(); boolean done = !regions.isEmpty(); return done; } }, MAX_WAIT, 100, true); } }); } /** * Creates a persistent region * * @param vm reference to VM * @throws Throwable */ protected void createPersistentRegion(VM vm) throws Throwable { AsyncInvocation future = createPersistentRegionAsync(vm); future.join(MAX_WAIT); if (future.isAlive()) { fail("Region not created within" + MAX_WAIT); } if (future.exceptionOccurred()) { throw new RuntimeException(future.getException()); } } /** * Creates a persistent region in async manner * * @param vm reference to VM * @return reference to AsyncInvocation */ @SuppressWarnings("serial") protected AsyncInvocation createPersistentRegionAsync(final VM vm) { SerializableRunnable createRegion = new SerializableRunnable("Create persistent region") { public void run() { Cache cache = getCache(); DiskStoreFactory dsf = cache.createDiskStoreFactory(); File dir = getDiskDirForVM(vm); dir.mkdirs(); dsf.setDiskDirs(new File[] {dir}); dsf.setMaxOplogSize(1); dsf.setAllowForceCompaction(true); dsf.setAutoCompact(false); DiskStore ds = dsf.create(REGION_NAME); RegionFactory rf = cache.createRegionFactory(); rf.setDiskStoreName(ds.getName()); rf.setDiskSynchronous(true); rf.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE); rf.setScope(Scope.DISTRIBUTED_ACK); rf.create(REGION_NAME); } }; return vm.invokeAsync(createRegion); } /** * Validates a persistent region * * @param vm reference to VM */ @SuppressWarnings("serial") protected void validatePersistentRegion(final VM vm) { SerializableRunnable validateDisk = new SerializableRunnable("Validate persistent region") { public void run() { Cache cache = getCache(); ManagementService service = getManagementService(); DiskStoreMXBean bean = service.getLocalDiskStoreMBean(REGION_NAME); assertNotNull(bean); } }; vm.invoke(validateDisk); } /** * Appends vm id to disk dir * * @param vm reference to VM * @return */ protected File getDiskDirForVM(final VM vm) { File dir = new File(diskDir, String.valueOf(vm.getPid())); return dir; } /** * Checks recovery status * * @param vm reference to VM * @param localRecovery local recovery on or not */ @SuppressWarnings("serial") private void checkForRecoveryStat(VM vm, final boolean localRecovery) { vm.invoke(new SerializableRunnable("check disk region stat") { public void run() { Cache cache = getCache(); Region region = cache.getRegion(REGION_NAME); DistributedRegion distributedRegion = (DistributedRegion) region; DiskRegionStats stats = distributedRegion.getDiskRegion().getStats(); if (localRecovery) { assertEquals(1, stats.getLocalInitializations()); assertEquals(0, stats.getRemoteInitializations()); } else { assertEquals(0, stats.getLocalInitializations()); assertEquals(1, stats.getRemoteInitializations()); } } }); } /** * * @return back up directory */ protected static File getBackupDir(String name) throws Exception { File backUpDir = new File("BackupDir-" + name).getAbsoluteFile(); org.apache.geode.internal.FileUtil.delete(backUpDir); backUpDir.mkdir(); backUpDir.deleteOnExit(); return backUpDir; } }