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