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}