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.apache.hadoop.hbase.util.Threads; 041import org.junit.AfterClass; 042import org.junit.Assert; 043import org.junit.BeforeClass; 044import org.junit.ClassRule; 045import org.junit.Test; 046import org.junit.experimental.categories.Category; 047 048/** 049 * Test the ClusterStatus. 050 */ 051@Category(MediumTests.class) 052public class TestClientClusterStatus { 053 054 @ClassRule 055 public static final HBaseClassTestRule CLASS_RULE = 056 HBaseClassTestRule.forClass(TestClientClusterStatus.class); 057 058 private static HBaseTestingUtility UTIL; 059 private static Admin ADMIN; 060 private final static int SLAVES = 5; 061 private final static int MASTERS = 3; 062 private static MiniHBaseCluster CLUSTER; 063 private static HRegionServer DEAD; 064 065 @BeforeClass 066 public static void setUpBeforeClass() throws Exception { 067 Configuration conf = HBaseConfiguration.create(); 068 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, MyObserver.class.getName()); 069 UTIL = new HBaseTestingUtility(conf); 070 UTIL.startMiniCluster(MASTERS, SLAVES); 071 CLUSTER = UTIL.getHBaseCluster(); 072 CLUSTER.waitForActiveAndReadyMaster(); 073 ADMIN = UTIL.getAdmin(); 074 // Kill one region server 075 List<RegionServerThread> rsts = CLUSTER.getLiveRegionServerThreads(); 076 RegionServerThread rst = rsts.get(rsts.size() - 1); 077 DEAD = rst.getRegionServer(); 078 DEAD.stop("Test dead servers status"); 079 while (rst.isAlive()) { 080 Thread.sleep(500); 081 } 082 } 083 084 @Test 085 public void testDefaults() throws Exception { 086 ClusterStatus origin = ADMIN.getClusterStatus(); 087 ClusterStatus defaults 088 = new ClusterStatus(ADMIN.getClusterMetrics(EnumSet.allOf(Option.class))); 089 checkPbObjectNotNull(origin); 090 checkPbObjectNotNull(defaults); 091 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 092 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 093 Assert.assertTrue(origin.getAverageLoad() == defaults.getAverageLoad()); 094 Assert.assertTrue(origin.getBackupMastersSize() == defaults.getBackupMastersSize()); 095 Assert.assertTrue(origin.getDeadServersSize() == defaults.getDeadServersSize()); 096 Assert.assertTrue(origin.getRegionsCount() == defaults.getRegionsCount()); 097 Assert.assertTrue(origin.getServersSize() == defaults.getServersSize()); 098 Assert.assertTrue(origin.getMasterInfoPort() == defaults.getMasterInfoPort()); 099 Assert.assertTrue(origin.equals(defaults)); 100 } 101 102 @Test 103 public void testNone() throws Exception { 104 ClusterMetrics status0 = ADMIN.getClusterMetrics(EnumSet.allOf(Option.class)); 105 ClusterMetrics status1 = ADMIN.getClusterMetrics(EnumSet.noneOf(Option.class)); 106 // Do a rough compare. More specific compares can fail because all regions not deployed yet 107 // or more requests than expected. 108 Assert.assertEquals(status0.getLiveServerMetrics().size(), 109 status1.getLiveServerMetrics().size()); 110 checkPbObjectNotNull(new ClusterStatus(status0)); 111 checkPbObjectNotNull(new ClusterStatus(status1)); 112 } 113 114 @Test 115 public void testLiveAndDeadServersStatus() throws Exception { 116 // Count the number of live regionservers 117 List<RegionServerThread> regionserverThreads = CLUSTER.getLiveRegionServerThreads(); 118 int numRs = 0; 119 int len = regionserverThreads.size(); 120 for (int i = 0; i < len; i++) { 121 if (regionserverThreads.get(i).isAlive()) { 122 numRs++; 123 } 124 } 125 // Depending on the (random) order of unit execution we may run this unit before the 126 // minicluster is fully up and recovered from the RS shutdown done during test init. 127 Waiter.waitFor(CLUSTER.getConfiguration(), 10 * 1000, 100, new Predicate<Exception>() { 128 @Override 129 public boolean evaluate() throws Exception { 130 ClusterStatus status 131 = new ClusterStatus(ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))); 132 Assert.assertNotNull(status); 133 return status.getRegionsCount() > 0; 134 } 135 }); 136 // Retrieve live servers and dead servers info. 137 EnumSet<Option> options = EnumSet.of(Option.LIVE_SERVERS, Option.DEAD_SERVERS); 138 ClusterStatus status = new ClusterStatus(ADMIN.getClusterMetrics(options)); 139 checkPbObjectNotNull(status); 140 Assert.assertNotNull(status); 141 Assert.assertNotNull(status.getServers()); 142 // exclude a dead region server 143 Assert.assertEquals(SLAVES -1, numRs); 144 // live servers = nums of regionservers 145 // By default, HMaster don't carry any regions so it won't report its load. 146 // Hence, it won't be in the server list. 147 Assert.assertEquals(status.getServers().size(), numRs); 148 Assert.assertTrue(status.getRegionsCount() > 0); 149 Assert.assertNotNull(status.getDeadServerNames()); 150 Assert.assertEquals(1, status.getDeadServersSize()); 151 ServerName deadServerName = status.getDeadServerNames().iterator().next(); 152 Assert.assertEquals(DEAD.getServerName(), deadServerName); 153 } 154 155 @Test 156 public void testMasterAndBackupMastersStatus() throws Exception { 157 // get all the master threads 158 List<MasterThread> masterThreads = CLUSTER.getMasterThreads(); 159 int numActive = 0; 160 int activeIndex = 0; 161 ServerName activeName = null; 162 HMaster active = null; 163 for (int i = 0; i < masterThreads.size(); i++) { 164 if (masterThreads.get(i).getMaster().isActiveMaster()) { 165 numActive++; 166 activeIndex = i; 167 active = masterThreads.get(activeIndex).getMaster(); 168 activeName = active.getServerName(); 169 } 170 } 171 Assert.assertNotNull(active); 172 Assert.assertEquals(1, numActive); 173 Assert.assertEquals(MASTERS, masterThreads.size()); 174 // Retrieve master and backup masters infos only. 175 EnumSet<Option> options = EnumSet.of(Option.MASTER, Option.BACKUP_MASTERS); 176 ClusterStatus status = new ClusterStatus(ADMIN.getClusterMetrics(options)); 177 Assert.assertTrue(status.getMaster().equals(activeName)); 178 Assert.assertEquals(MASTERS - 1, status.getBackupMastersSize()); 179 } 180 181 @Test 182 public void testOtherStatusInfos() throws Exception { 183 EnumSet<Option> options = 184 EnumSet.of(Option.MASTER_COPROCESSORS, Option.HBASE_VERSION, 185 Option.CLUSTER_ID, Option.BALANCER_ON); 186 ClusterStatus status = new ClusterStatus(ADMIN.getClusterMetrics(options)); 187 Assert.assertTrue(status.getMasterCoprocessors().length == 1); 188 Assert.assertNotNull(status.getHBaseVersion()); 189 Assert.assertNotNull(status.getClusterId()); 190 Assert.assertTrue(status.getAverageLoad() == 0.0); 191 Assert.assertNotNull(status.getBalancerOn()); 192 } 193 194 @AfterClass 195 public static void tearDownAfterClass() throws Exception { 196 if (ADMIN != null) ADMIN.close(); 197 UTIL.shutdownMiniCluster(); 198 } 199 200 @Test 201 public void testObserver() throws IOException { 202 int preCount = MyObserver.PRE_COUNT.get(); 203 int postCount = MyObserver.POST_COUNT.get(); 204 Assert.assertTrue(Stream.of(ADMIN.getClusterStatus().getMasterCoprocessors()) 205 .anyMatch(s -> s.equals(MyObserver.class.getSimpleName()))); 206 Assert.assertEquals(preCount + 1, MyObserver.PRE_COUNT.get()); 207 Assert.assertEquals(postCount + 1, MyObserver.POST_COUNT.get()); 208 } 209 210 /** 211 * HBASE-19496 do the refactor for ServerLoad and RegionLoad so the inner pb object is useless 212 * now. However, they are Public classes, and consequently we must make sure the all pb objects 213 * have initialized. 214 */ 215 private static void checkPbObjectNotNull(ClusterStatus status) { 216 for (ServerName name : status.getLiveServerMetrics().keySet()) { 217 ServerLoad load = status.getLoad(name); 218 Assert.assertNotNull(load.obtainServerLoadPB()); 219 for (RegionLoad rl : load.getRegionsLoad().values()) { 220 Assert.assertNotNull(rl.regionLoadPB); 221 } 222 } 223 } 224 225 public static class MyObserver implements MasterCoprocessor, MasterObserver { 226 private static final AtomicInteger PRE_COUNT = new AtomicInteger(0); 227 private static final AtomicInteger POST_COUNT = new AtomicInteger(0); 228 229 @Override public Optional<MasterObserver> getMasterObserver() { 230 return Optional.of(this); 231 } 232 233 @Override public void preGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx) 234 throws IOException { 235 PRE_COUNT.incrementAndGet(); 236 } 237 238 @Override public void postGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx, 239 ClusterMetrics status) throws IOException { 240 POST_COUNT.incrementAndGet(); 241 } 242 } 243}