001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import java.io.IOException; 021import java.util.EnumSet; 022import java.util.List; 023import java.util.Optional; 024import java.util.concurrent.atomic.AtomicInteger; 025import java.util.stream.Stream; 026import org.apache.hadoop.conf.Configuration; 027import org.apache.hadoop.hbase.ClusterMetrics.Option; 028import org.apache.hadoop.hbase.Waiter.Predicate; 029import org.apache.hadoop.hbase.client.Admin; 030import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 031import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 032import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 033import org.apache.hadoop.hbase.coprocessor.MasterObserver; 034import org.apache.hadoop.hbase.coprocessor.ObserverContext; 035import org.apache.hadoop.hbase.master.HMaster; 036import org.apache.hadoop.hbase.regionserver.HRegionServer; 037import org.apache.hadoop.hbase.testclassification.MediumTests; 038import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread; 039import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 040import org.junit.AfterClass; 041import org.junit.Assert; 042import org.junit.BeforeClass; 043import org.junit.ClassRule; 044import org.junit.Test; 045import org.junit.experimental.categories.Category; 046 047/** 048 * Test the ClusterStatus. 049 */ 050@Category(MediumTests.class) 051public class TestClientClusterStatus { 052 053 @ClassRule 054 public static final HBaseClassTestRule CLASS_RULE = 055 HBaseClassTestRule.forClass(TestClientClusterStatus.class); 056 057 private static HBaseTestingUtility UTIL; 058 private static Admin ADMIN; 059 private final static int SLAVES = 5; 060 private final static int MASTERS = 3; 061 private static MiniHBaseCluster CLUSTER; 062 private static HRegionServer DEAD; 063 064 @BeforeClass 065 public static void setUpBeforeClass() throws Exception { 066 Configuration conf = HBaseConfiguration.create(); 067 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, MyObserver.class.getName()); 068 UTIL = new HBaseTestingUtility(conf); 069 StartMiniClusterOption option = StartMiniClusterOption.builder() 070 .numMasters(MASTERS).numRegionServers(SLAVES).numDataNodes(SLAVES).build(); 071 UTIL.startMiniCluster(option); 072 CLUSTER = UTIL.getHBaseCluster(); 073 CLUSTER.waitForActiveAndReadyMaster(); 074 ADMIN = UTIL.getAdmin(); 075 // Kill one region server 076 List<RegionServerThread> rsts = CLUSTER.getLiveRegionServerThreads(); 077 RegionServerThread rst = rsts.get(rsts.size() - 1); 078 DEAD = rst.getRegionServer(); 079 DEAD.stop("Test dead servers status"); 080 while (rst.isAlive()) { 081 Thread.sleep(500); 082 } 083 } 084 085 @Test 086 public void testDefaults() throws Exception { 087 ClusterStatus origin = ADMIN.getClusterStatus(); 088 ClusterStatus defaults 089 = new ClusterStatus(ADMIN.getClusterMetrics(EnumSet.allOf(Option.class))); 090 checkPbObjectNotNull(origin); 091 checkPbObjectNotNull(defaults); 092 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 093 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 094 Assert.assertTrue(origin.getAverageLoad() == defaults.getAverageLoad()); 095 Assert.assertTrue(origin.getBackupMastersSize() == defaults.getBackupMastersSize()); 096 Assert.assertTrue(origin.getDeadServersSize() == defaults.getDeadServersSize()); 097 Assert.assertTrue(origin.getRegionsCount() == defaults.getRegionsCount()); 098 Assert.assertTrue(origin.getServersSize() == defaults.getServersSize()); 099 Assert.assertTrue(origin.getMasterInfoPort() == defaults.getMasterInfoPort()); 100 Assert.assertTrue(origin.equals(defaults)); 101 Assert.assertTrue(origin.getServersName().size() == defaults.getServersName().size()); 102 } 103 104 @Test 105 public void testNone() throws Exception { 106 ClusterMetrics status0 = ADMIN.getClusterMetrics(EnumSet.allOf(Option.class)); 107 ClusterMetrics status1 = ADMIN.getClusterMetrics(EnumSet.noneOf(Option.class)); 108 // Do a rough compare. More specific compares can fail because all regions not deployed yet 109 // or more requests than expected. 110 Assert.assertEquals(status0.getLiveServerMetrics().size(), 111 status1.getLiveServerMetrics().size()); 112 checkPbObjectNotNull(new ClusterStatus(status0)); 113 checkPbObjectNotNull(new ClusterStatus(status1)); 114 } 115 116 @Test 117 public void testLiveAndDeadServersStatus() throws Exception { 118 // Count the number of live regionservers 119 List<RegionServerThread> regionserverThreads = CLUSTER.getLiveRegionServerThreads(); 120 int numRs = 0; 121 int len = regionserverThreads.size(); 122 for (int i = 0; i < len; i++) { 123 if (regionserverThreads.get(i).isAlive()) { 124 numRs++; 125 } 126 } 127 // Depending on the (random) order of unit execution we may run this unit before the 128 // minicluster is fully up and recovered from the RS shutdown done during test init. 129 Waiter.waitFor(CLUSTER.getConfiguration(), 10 * 1000, 100, new Predicate<Exception>() { 130 @Override 131 public boolean evaluate() throws Exception { 132 ClusterStatus status 133 = new ClusterStatus(ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))); 134 Assert.assertNotNull(status); 135 return status.getRegionsCount() > 0; 136 } 137 }); 138 // Retrieve live servers and dead servers info. 139 EnumSet<Option> options = 140 EnumSet.of(Option.LIVE_SERVERS, Option.DEAD_SERVERS, Option.SERVERS_NAME); 141 ClusterStatus status = new ClusterStatus(ADMIN.getClusterMetrics(options)); 142 checkPbObjectNotNull(status); 143 Assert.assertNotNull(status); 144 Assert.assertNotNull(status.getServers()); 145 // exclude a dead region server 146 Assert.assertEquals(SLAVES -1, numRs); 147 // live servers = nums of regionservers 148 // By default, HMaster don't carry any regions so it won't report its load. 149 // Hence, it won't be in the server list. 150 Assert.assertEquals(status.getServers().size(), numRs); 151 Assert.assertTrue(status.getRegionsCount() > 0); 152 Assert.assertNotNull(status.getDeadServerNames()); 153 Assert.assertEquals(1, status.getDeadServersSize()); 154 ServerName deadServerName = status.getDeadServerNames().iterator().next(); 155 Assert.assertEquals(DEAD.getServerName(), deadServerName); 156 Assert.assertNotNull(status.getServersName()); 157 Assert.assertEquals(numRs, status.getServersName().size()); 158 } 159 160 @Test 161 public void testMasterAndBackupMastersStatus() throws Exception { 162 // get all the master threads 163 List<MasterThread> masterThreads = CLUSTER.getMasterThreads(); 164 int numActive = 0; 165 int activeIndex = 0; 166 ServerName activeName = null; 167 HMaster active = null; 168 for (int i = 0; i < masterThreads.size(); i++) { 169 if (masterThreads.get(i).getMaster().isActiveMaster()) { 170 numActive++; 171 activeIndex = i; 172 active = masterThreads.get(activeIndex).getMaster(); 173 activeName = active.getServerName(); 174 } 175 } 176 Assert.assertNotNull(active); 177 Assert.assertEquals(1, numActive); 178 Assert.assertEquals(MASTERS, masterThreads.size()); 179 // Retrieve master and backup masters infos only. 180 EnumSet<Option> options = EnumSet.of(Option.MASTER, Option.BACKUP_MASTERS); 181 ClusterStatus status = new ClusterStatus(ADMIN.getClusterMetrics(options)); 182 Assert.assertTrue(status.getMaster().equals(activeName)); 183 Assert.assertEquals(MASTERS - 1, status.getBackupMastersSize()); 184 } 185 186 @Test 187 public void testOtherStatusInfos() throws Exception { 188 EnumSet<Option> options = 189 EnumSet.of(Option.MASTER_COPROCESSORS, Option.HBASE_VERSION, 190 Option.CLUSTER_ID, Option.BALANCER_ON); 191 ClusterStatus status = new ClusterStatus(ADMIN.getClusterMetrics(options)); 192 Assert.assertTrue(status.getMasterCoprocessors().length == 1); 193 Assert.assertNotNull(status.getHBaseVersion()); 194 Assert.assertNotNull(status.getClusterId()); 195 Assert.assertTrue(status.getAverageLoad() == 0.0); 196 Assert.assertNotNull(status.getBalancerOn()); 197 } 198 199 @AfterClass 200 public static void tearDownAfterClass() throws Exception { 201 if (ADMIN != null) ADMIN.close(); 202 UTIL.shutdownMiniCluster(); 203 } 204 205 @Test 206 public void testObserver() throws IOException { 207 int preCount = MyObserver.PRE_COUNT.get(); 208 int postCount = MyObserver.POST_COUNT.get(); 209 Assert.assertTrue(Stream.of(ADMIN.getClusterStatus().getMasterCoprocessors()) 210 .anyMatch(s -> s.equals(MyObserver.class.getSimpleName()))); 211 Assert.assertEquals(preCount + 1, MyObserver.PRE_COUNT.get()); 212 Assert.assertEquals(postCount + 1, MyObserver.POST_COUNT.get()); 213 } 214 215 /** 216 * HBASE-19496 do the refactor for ServerLoad and RegionLoad so the inner pb object is useless 217 * now. However, they are Public classes, and consequently we must make sure the all pb objects 218 * have initialized. 219 */ 220 private static void checkPbObjectNotNull(ClusterStatus status) { 221 for (ServerName name : status.getLiveServerMetrics().keySet()) { 222 ServerLoad load = status.getLoad(name); 223 Assert.assertNotNull(load.obtainServerLoadPB()); 224 for (RegionLoad rl : load.getRegionsLoad().values()) { 225 Assert.assertNotNull(rl.regionLoadPB); 226 } 227 } 228 } 229 230 public static class MyObserver implements MasterCoprocessor, MasterObserver { 231 private static final AtomicInteger PRE_COUNT = new AtomicInteger(0); 232 private static final AtomicInteger POST_COUNT = new AtomicInteger(0); 233 234 @Override public Optional<MasterObserver> getMasterObserver() { 235 return Optional.of(this); 236 } 237 238 @Override public void preGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx) 239 throws IOException { 240 PRE_COUNT.incrementAndGet(); 241 } 242 243 @Override public void postGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx, 244 ClusterMetrics status) throws IOException { 245 POST_COUNT.incrementAndGet(); 246 } 247 } 248}