View Javadoc

1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.rsgroup;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertFalse;
24  import static org.junit.Assert.assertNotNull;
25  import static org.junit.Assert.assertNull;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import com.google.common.collect.Sets;
30  
31  import java.io.IOException;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.apache.hadoop.hbase.HRegionInfo;
39  import org.apache.hadoop.hbase.ServerName;
40  import org.apache.hadoop.hbase.TableName;
41  import org.apache.hadoop.hbase.TableNotFoundException;
42  import org.apache.hadoop.hbase.Waiter;
43  import org.apache.hadoop.hbase.client.ClusterConnection;
44  import org.apache.hadoop.hbase.master.HMaster;
45  import org.apache.hadoop.hbase.master.RegionState;
46  import org.apache.hadoop.hbase.net.Address;
47  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
48  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
49  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetServerInfoRequest;
50  import org.apache.hadoop.hbase.testclassification.MediumTests;
51  import org.apache.hadoop.hbase.util.Bytes;
52  import org.junit.After;
53  import org.junit.AfterClass;
54  import org.junit.Assert;
55  import org.junit.Before;
56  import org.junit.BeforeClass;
57  import org.junit.Test;
58  import org.junit.experimental.categories.Category;
59  
60  @Category({MediumTests.class})
61  public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
62    protected static final Log LOG = LogFactory.getLog(TestRSGroupsAdmin2.class);
63  
64    @BeforeClass
65    public static void setUp() throws Exception {
66      setUpTestBeforeClass();
67    }
68  
69    @AfterClass
70    public static void tearDown() throws Exception {
71      tearDownAfterClass();
72    }
73  
74    @Before
75    public void beforeMethod() throws Exception {
76      setUpBeforeMethod();
77    }
78  
79    @After
80    public void afterMethod() throws Exception {
81      tearDownAfterMethod();
82    }
83  
84  
85    @Test
86    public void testRegionMove() throws Exception {
87      LOG.info("testRegionMove");
88  
89      final RSGroupInfo newGroup = addGroup(rsGroupAdmin, getGroupName("testRegionMove"), 1);
90      final TableName tableName = TableName.valueOf(tablePrefix + rand.nextInt());
91      final byte[] familyNameBytes = Bytes.toBytes("f");
92      // All the regions created below will be assigned to the default group.
93      TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 6);
94      TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
95        @Override
96        public boolean evaluate() throws Exception {
97          List<String> regions = getTableRegionMap().get(tableName);
98          if (regions == null)
99            return false;
100         return getTableRegionMap().get(tableName).size() >= 6;
101       }
102     });
103 
104     //get target region to move
105     Map<ServerName,List<String>> assignMap =
106         getTableServerRegionMap().get(tableName);
107     String targetRegion = null;
108     for(ServerName server : assignMap.keySet()) {
109       targetRegion = assignMap.get(server).size() > 0 ? assignMap.get(server).get(0) : null;
110       if(targetRegion != null) {
111         break;
112       }
113     }
114     //get server which is not a member of new group
115     ServerName targetServer = null;
116     for(ServerName server : admin.getClusterStatus().getServers()) {
117       if(!newGroup.containsServer(server.getAddress())) {
118         targetServer = server;
119         break;
120       }
121     }
122     assertNotNull(targetServer);
123 
124     final AdminProtos.AdminService.BlockingInterface targetRS =
125         admin.getConnection().getAdmin(targetServer);
126 
127     //move target server to group
128     rsGroupAdmin.moveServers(Sets.newHashSet(targetServer.getAddress()),
129         newGroup.getName());
130     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
131       @Override
132       public boolean evaluate() throws Exception {
133         return ProtobufUtil.getOnlineRegions(targetRS).size() <= 0;
134       }
135     });
136 
137     // Lets move this region to the new group.
138     TEST_UTIL.getHBaseAdmin().move(Bytes.toBytes(HRegionInfo.encodeRegionName(Bytes.toBytes(targetRegion))),
139         Bytes.toBytes(targetServer.getServerName()));
140     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
141       @Override
142       public boolean evaluate() throws Exception {
143         List<String> regions = getTableRegionMap().get(tableName);
144         Set<RegionState> regionsInTransition = admin.getClusterStatus().getRegionsInTransition();
145         return (regions != null && getTableRegionMap().get(tableName).size() == 6) &&
146            ( regionsInTransition == null || regionsInTransition.size() < 1);
147       }
148     });
149 
150     //verify that targetServer didn't open it
151     for (HRegionInfo region: ProtobufUtil.getOnlineRegions(targetRS)) {
152       if (targetRegion.equals(region.getRegionNameAsString())) {
153         fail("Target server opened region");
154       }
155     }
156   }
157 
158   @Test
159   public void testRegionServerMove() throws IOException,
160       InterruptedException {
161     LOG.info("testRegionServerMove");
162 
163     int initNumGroups = rsGroupAdmin.listRSGroups().size();
164     RSGroupInfo appInfo = addGroup(rsGroupAdmin, getGroupName("testRegionServerMove"), 1);
165     RSGroupInfo adminInfo = addGroup(rsGroupAdmin, getGroupName("testRegionServerMove"), 1);
166     RSGroupInfo dInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
167     Assert.assertEquals(initNumGroups + 2, rsGroupAdmin.listRSGroups().size());
168     assertEquals(1, adminInfo.getServers().size());
169     assertEquals(1, appInfo.getServers().size());
170     assertEquals(getNumServers() - 2, dInfo.getServers().size());
171     rsGroupAdmin.moveServers(appInfo.getServers(),
172         RSGroupInfo.DEFAULT_GROUP);
173     rsGroupAdmin.removeRSGroup(appInfo.getName());
174     rsGroupAdmin.moveServers(adminInfo.getServers(),
175         RSGroupInfo.DEFAULT_GROUP);
176     rsGroupAdmin.removeRSGroup(adminInfo.getName());
177     Assert.assertEquals(rsGroupAdmin.listRSGroups().size(), initNumGroups);
178   }
179 
180   @Test
181   public void testMoveServers() throws Exception {
182     LOG.info("testMoveServers");
183 
184     //create groups and assign servers
185     addGroup(rsGroupAdmin, "bar", 3);
186     rsGroupAdmin.addRSGroup("foo");
187 
188     RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar");
189     RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
190     assertEquals(3, barGroup.getServers().size());
191     assertEquals(0, fooGroup.getServers().size());
192 
193     //test fail bogus server move
194     try {
195       rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromString("foo:9999")),"foo");
196       fail("Bogus servers shouldn't have been successfully moved.");
197     } catch(IOException ex) {
198       String exp = "Server foo:9999 does not have a group.";
199       String msg = "Expected '"+exp+"' in exception message: ";
200       assertTrue(msg+" "+ex.getMessage(), ex.getMessage().contains(exp));
201     }
202 
203     //test success case
204     LOG.info("moving servers "+barGroup.getServers()+" to group foo");
205     rsGroupAdmin.moveServers(barGroup.getServers(), fooGroup.getName());
206 
207     barGroup = rsGroupAdmin.getRSGroupInfo("bar");
208     fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
209     assertEquals(0,barGroup.getServers().size());
210     assertEquals(3,fooGroup.getServers().size());
211 
212     LOG.info("moving servers "+fooGroup.getServers()+" to group default");
213     rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
214 
215     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
216       @Override
217       public boolean evaluate() throws Exception {
218         return getNumServers() ==
219         rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size();
220       }
221     });
222 
223     fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
224     assertEquals(0,fooGroup.getServers().size());
225 
226     //test group removal
227     LOG.info("Remove group "+barGroup.getName());
228     rsGroupAdmin.removeRSGroup(barGroup.getName());
229     Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(barGroup.getName()));
230     LOG.info("Remove group "+fooGroup.getName());
231     rsGroupAdmin.removeRSGroup(fooGroup.getName());
232     Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName()));
233   }
234 
235   @Test
236   public void testRemoveServers() throws Exception {
237     final RSGroupInfo newGroup = addGroup(rsGroupAdmin, "testRemoveServers", 3);
238     ServerName targetServer = ServerName.parseServerName(
239         newGroup.getServers().iterator().next().toString());
240     try {
241       rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress()));
242       fail("Online servers shouldn't have been successfully removed.");
243     } catch(IOException ex) {
244       String exp = "Server " + targetServer.getAddress()
245           + " is an online server, not allowed to remove.";
246       String msg = "Expected '" + exp + "' in exception message: ";
247       assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
248     }
249     assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
250 
251     AdminProtos.AdminService.BlockingInterface targetRS =
252         ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
253     try {
254       targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null,
255           GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName());
256       //stopping may cause an exception
257       //due to the connection loss
258       targetRS.stopServer(null,
259           AdminProtos.StopServerRequest.newBuilder().setReason("Die").build());
260     } catch(Exception e) {
261     }
262 
263     final HMaster master = TEST_UTIL.getHBaseCluster().getMaster();
264     //wait for stopped regionserver to dead server list
265     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
266       @Override
267       public boolean evaluate() throws Exception {
268         return !master.getServerManager().areDeadServersInProgress()
269             && cluster.getClusterStatus().getDeadServerNames().size() > 0;
270       }
271     });
272 
273     try {
274       rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress()));
275       fail("Dead servers shouldn't have been successfully removed.");
276     } catch(IOException ex) {
277       String exp = "Server " + targetServer.getAddress() + " is on the dead servers list,"
278           + " Maybe it will come back again, not allowed to remove.";
279       String msg = "Expected '" + exp + "' in exception message: ";
280       assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
281     }
282     assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
283 
284     ServerName sn = TEST_UTIL.getHBaseClusterInterface().getClusterStatus().getMaster();
285     TEST_UTIL.getHBaseClusterInterface().stopMaster(sn);
286     TEST_UTIL.getHBaseClusterInterface().waitForMasterToStop(sn, 60000);
287     TEST_UTIL.getHBaseClusterInterface().startMaster(sn.getHostname(), 0);
288     TEST_UTIL.getHBaseClusterInterface().waitForActiveAndReadyMaster(60000);
289 
290     assertEquals(3, cluster.getClusterStatus().getServersSize());
291     assertFalse(cluster.getClusterStatus().getServers().contains(targetServer));
292     assertFalse(cluster.getClusterStatus().getDeadServerNames().contains(targetServer));
293     assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
294 
295     rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress()));
296     Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers();
297     assertFalse(newGroupServers.contains(targetServer.getAddress()));
298     assertEquals(2, newGroupServers.size());
299 
300     assertTrue(observer.preRemoveServersCalled);
301   }
302 
303   @Test
304   public void testNonExistentTableMove() throws Exception {
305     TableName tableName = TableName.valueOf(tablePrefix + rand.nextInt());
306 
307     RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName);
308     assertNull(tableGrp);
309 
310     //test if table exists already.
311     boolean exist = admin.tableExists(tableName);
312     assertFalse(exist);
313 
314     LOG.info("Moving table "+ tableName + " to " + RSGroupInfo.DEFAULT_GROUP);
315     try {
316       rsGroupAdmin.moveTables(Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
317       fail("Table " + tableName + " shouldn't have been successfully moved.");
318     } catch(IOException ex) {
319       assertTrue(ex instanceof TableNotFoundException);
320     }
321 
322     try {
323       rsGroupAdmin.moveServersAndTables(
324         Sets.newHashSet(Address.fromParts("bogus",123)),
325         Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
326       fail("Table " + tableName + " shouldn't have been successfully moved.");
327     } catch(IOException ex) {
328       assertTrue(ex instanceof TableNotFoundException);
329     }
330     //verify group change
331     assertNull(rsGroupAdmin.getRSGroupInfoOfTable(tableName));
332   }
333 
334   @Test
335   public void testMoveServersAndTables() throws Exception {
336     final TableName tableName = TableName.valueOf(tablePrefix + "_testMoveServersAndTables");
337     final RSGroupInfo newGroup = addGroup(rsGroupAdmin, getGroupName("testMoveServersAndTables"), 1);
338 
339     //create table
340     final byte[] familyNameBytes = Bytes.toBytes("f");
341     TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
342     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
343       @Override
344       public boolean evaluate() throws Exception {
345         List<String> regions = getTableRegionMap().get(tableName);
346         if (regions == null)
347           return false;
348         return getTableRegionMap().get(tableName).size() >= 5;
349       }
350     });
351 
352     //get server which is not a member of new group
353     ServerName targetServer = null;
354     for(ServerName server : admin.getClusterStatus().getServers()) {
355       if(!newGroup.containsServer(server.getAddress()) &&
356            !rsGroupAdmin.getRSGroupInfo("master").containsServer(server.getAddress())) {
357         targetServer = server;
358         break;
359       }
360     }
361 
362     LOG.debug("Print group info : " + rsGroupAdmin.listRSGroups());
363     int oldDefaultGroupServerSize =
364             rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size();
365     int oldDefaultGroupTableSize =
366             rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size();
367 
368     //test fail bogus server move
369     try {
370       rsGroupAdmin.moveServersAndTables(Sets.newHashSet(Address.fromString("foo:9999")),
371               Sets.newHashSet(tableName), newGroup.getName());
372       fail("Bogus servers shouldn't have been successfully moved.");
373     } catch(IOException ex) {
374     }
375 
376     //test fail server move
377     try {
378       rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()),
379               Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
380       fail("servers shouldn't have been successfully moved.");
381     } catch(IOException ex) {
382     }
383 
384     //verify default group info
385     Assert.assertEquals(oldDefaultGroupServerSize,
386             rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size());
387     Assert.assertEquals(oldDefaultGroupTableSize,
388             rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size());
389 
390     //verify new group info
391     Assert.assertEquals(1,
392             rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers().size());
393     Assert.assertEquals(0,
394             rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
395 
396     //get all region to move targetServer
397     List<String> regionList = getTableRegionMap().get(tableName);
398     for(String region : regionList) {
399       // Lets move this region to the targetServer
400       admin.move(Bytes.toBytes(HRegionInfo.encodeRegionName(Bytes.toBytes(region))),
401               Bytes.toBytes(targetServer.getServerName()));
402     }
403 
404     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
405       @Override
406       public boolean evaluate() throws Exception {
407         List<String> regions = getTableRegionMap().get(tableName);
408         Map<ServerName, List<String>> serverMap = getTableServerRegionMap().get(tableName);
409         Set<RegionState> regionsInTransition = admin.getClusterStatus().getRegionsInTransition();
410         return (regions != null && regions.size() == 5) &&
411           (serverMap != null && serverMap.size() == 1) &&
412           (regionsInTransition == null || regionsInTransition.size() < 1);
413       }
414     });
415 
416     //verify that all region move to targetServer
417     Assert.assertNotNull(getTableServerRegionMap().get(tableName));
418     Assert.assertNotNull(getTableServerRegionMap().get(tableName).get(targetServer));
419     Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size());
420 
421     //move targetServer and table to newGroup
422     LOG.info("moving server and table to newGroup");
423     rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()),
424             Sets.newHashSet(tableName), newGroup.getName());
425 
426     //verify group change
427     Assert.assertEquals(newGroup.getName(),
428             rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
429 
430     //verify servers' not exist in old group
431     Set<Address> defaultServers = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers();
432     assertFalse(defaultServers.contains(targetServer.getAddress()));
433 
434     //verify servers' exist in new group
435     Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers();
436     assertTrue(newGroupServers.contains(targetServer.getAddress()));
437 
438     //verify tables' not exist in old group
439     Set<TableName> defaultTables = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables();
440     assertFalse(defaultTables.contains(tableName));
441 
442     //verify tables' exist in new group
443     Set<TableName> newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables();
444     assertTrue(newGroupTables.contains(tableName));
445 
446     assertTrue(observer.preMoveServersAndTables);
447     assertTrue(observer.postMoveServersAndTables);
448   }
449 
450 }