1   /*
2    * Copyright 2011 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  
21  package org.apache.hadoop.hbase.coprocessor;
22  
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  
28  import java.io.IOException;
29  import java.util.Collection;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.NavigableMap;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.hadoop.conf.Configuration;
37  import org.apache.hadoop.hbase.*;
38  import org.apache.hadoop.hbase.client.HBaseAdmin;
39  import org.apache.hadoop.hbase.client.HTable;
40  import org.apache.hadoop.hbase.master.AssignmentManager;
41  import org.apache.hadoop.hbase.master.HMaster;
42  import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
43  import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
44  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
45  import org.apache.hadoop.hbase.regionserver.HRegionServer;
46  import org.apache.hadoop.hbase.util.Bytes;
47  import org.apache.hadoop.hbase.util.Threads;
48  import org.junit.AfterClass;
49  import org.junit.BeforeClass;
50  import org.junit.Test;
51  import org.junit.experimental.categories.Category;
52  
53  /**
54   * Tests invocation of the {@link org.apache.hadoop.hbase.coprocessor.MasterObserver}
55   * interface hooks at all appropriate times during normal HMaster operations.
56   */
57  @Category(MediumTests.class)
58  public class TestMasterObserver {
59    private static final Log LOG = LogFactory.getLog(TestMasterObserver.class);
60  
61    public static class CPMasterObserver implements MasterObserver {
62  
63      private boolean bypass = false;
64      private boolean preCreateTableCalled;
65      private boolean postCreateTableCalled;
66      private boolean preDeleteTableCalled;
67      private boolean postDeleteTableCalled;
68      private boolean preModifyTableCalled;
69      private boolean postModifyTableCalled;
70      private boolean preAddColumnCalled;
71      private boolean postAddColumnCalled;
72      private boolean preModifyColumnCalled;
73      private boolean postModifyColumnCalled;
74      private boolean preDeleteColumnCalled;
75      private boolean postDeleteColumnCalled;
76      private boolean preEnableTableCalled;
77      private boolean postEnableTableCalled;
78      private boolean preDisableTableCalled;
79      private boolean postDisableTableCalled;
80      private boolean preMoveCalled;
81      private boolean postMoveCalled;
82      private boolean preAssignCalled;
83      private boolean postAssignCalled;
84      private boolean preUnassignCalled;
85      private boolean postUnassignCalled;
86      private boolean preBalanceCalled;
87      private boolean postBalanceCalled;
88      private boolean preBalanceSwitchCalled;
89      private boolean postBalanceSwitchCalled;
90      private boolean preShutdownCalled;
91      private boolean preStopMasterCalled;
92      private boolean postStartMasterCalled;
93      private boolean startCalled;
94      private boolean stopCalled;
95      private boolean preSnapshotCalled;
96      private boolean postSnapshotCalled;
97      private boolean preCloneSnapshotCalled;
98      private boolean postCloneSnapshotCalled;
99      private boolean preRestoreSnapshotCalled;
100     private boolean postRestoreSnapshotCalled;
101     private boolean preDeleteSnapshotCalled;
102     private boolean postDeleteSnapshotCalled;
103     private boolean preGetTableDescriptorsCalled;
104     private boolean postGetTableDescriptorsCalled;
105 
106     public void enableBypass(boolean bypass) {
107       this.bypass = bypass;
108     }
109 
110     public void resetStates() {
111       preCreateTableCalled = false;
112       postCreateTableCalled = false;
113       preDeleteTableCalled = false;
114       postDeleteTableCalled = false;
115       preModifyTableCalled = false;
116       postModifyTableCalled = false;
117       preAddColumnCalled = false;
118       postAddColumnCalled = false;
119       preModifyColumnCalled = false;
120       postModifyColumnCalled = false;
121       preDeleteColumnCalled = false;
122       postDeleteColumnCalled = false;
123       preEnableTableCalled = false;
124       postEnableTableCalled = false;
125       preDisableTableCalled = false;
126       postDisableTableCalled = false;
127       preMoveCalled= false;
128       postMoveCalled = false;
129       preAssignCalled = false;
130       postAssignCalled = false;
131       preUnassignCalled = false;
132       postUnassignCalled = false;
133       preBalanceCalled = false;
134       postBalanceCalled = false;
135       preBalanceSwitchCalled = false;
136       postBalanceSwitchCalled = false;
137       preSnapshotCalled = false;
138       postSnapshotCalled = false;
139       preCloneSnapshotCalled = false;
140       postCloneSnapshotCalled = false;
141       preRestoreSnapshotCalled = false;
142       postRestoreSnapshotCalled = false;
143       preDeleteSnapshotCalled = false;
144       postDeleteSnapshotCalled = false;
145       preGetTableDescriptorsCalled = false;
146       postGetTableDescriptorsCalled = false;
147     }
148 
149     @Override
150     public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> env,
151         HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
152       if (bypass) {
153         env.bypass();
154       }
155       preCreateTableCalled = true;
156     }
157 
158     @Override
159     public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> env,
160         HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
161       postCreateTableCalled = true;
162     }
163 
164     public boolean wasCreateTableCalled() {
165       return preCreateTableCalled && postCreateTableCalled;
166     }
167 
168     public boolean preCreateTableCalledOnly() {
169       return preCreateTableCalled && !postCreateTableCalled;
170     }
171 
172     @Override
173     public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> env,
174         byte[] tableName) throws IOException {
175       if (bypass) {
176         env.bypass();
177       }
178       preDeleteTableCalled = true;
179     }
180 
181     @Override
182     public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> env,
183         byte[] tableName) throws IOException {
184       postDeleteTableCalled = true;
185     }
186 
187     public boolean wasDeleteTableCalled() {
188       return preDeleteTableCalled && postDeleteTableCalled;
189     }
190 
191     public boolean preDeleteTableCalledOnly() {
192       return preDeleteTableCalled && !postDeleteTableCalled;
193     }
194 
195     @Override
196     public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> env,
197         byte[] tableName, HTableDescriptor htd) throws IOException {
198       if (bypass) {
199         env.bypass();
200       }
201       preModifyTableCalled = true;
202     }
203 
204     @Override
205     public void postModifyTable(ObserverContext<MasterCoprocessorEnvironment> env,
206         byte[] tableName, HTableDescriptor htd) throws IOException {
207       postModifyTableCalled = true;
208     }
209 
210     public boolean wasModifyTableCalled() {
211       return preModifyTableCalled && postModifyTableCalled;
212     }
213 
214     public boolean preModifyTableCalledOnly() {
215       return preModifyTableCalled && !postModifyTableCalled;
216     }
217 
218     @Override
219     public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> env,
220         byte[] tableName, HColumnDescriptor column) throws IOException {
221       if (bypass) {
222         env.bypass();
223       }
224       preAddColumnCalled = true;
225     }
226 
227     @Override
228     public void postAddColumn(ObserverContext<MasterCoprocessorEnvironment> env,
229         byte[] tableName, HColumnDescriptor column) throws IOException {
230       postAddColumnCalled = true;
231     }
232 
233     public boolean wasAddColumnCalled() {
234       return preAddColumnCalled && postAddColumnCalled;
235     }
236 
237     public boolean preAddColumnCalledOnly() {
238       return preAddColumnCalled && !postAddColumnCalled;
239     }
240 
241     @Override
242     public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> env,
243         byte[] tableName, HColumnDescriptor descriptor) throws IOException {
244       if (bypass) {
245         env.bypass();
246       }
247       preModifyColumnCalled = true;
248     }
249 
250     @Override
251     public void postModifyColumn(ObserverContext<MasterCoprocessorEnvironment> env,
252         byte[] tableName, HColumnDescriptor descriptor) throws IOException {
253       postModifyColumnCalled = true;
254     }
255 
256     public boolean wasModifyColumnCalled() {
257       return preModifyColumnCalled && postModifyColumnCalled;
258     }
259 
260     public boolean preModifyColumnCalledOnly() {
261       return preModifyColumnCalled && !postModifyColumnCalled;
262     }
263 
264     @Override
265     public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> env,
266         byte[] tableName, byte[] c) throws IOException {
267       if (bypass) {
268         env.bypass();
269       }
270       preDeleteColumnCalled = true;
271     }
272 
273     @Override
274     public void postDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> env,
275         byte[] tableName, byte[] c) throws IOException {
276       postDeleteColumnCalled = true;
277     }
278 
279     public boolean wasDeleteColumnCalled() {
280       return preDeleteColumnCalled && postDeleteColumnCalled;
281     }
282 
283     public boolean preDeleteColumnCalledOnly() {
284       return preDeleteColumnCalled && !postDeleteColumnCalled;
285     }
286 
287     @Override
288     public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> env,
289         byte[] tableName) throws IOException {
290       if (bypass) {
291         env.bypass();
292       }
293       preEnableTableCalled = true;
294     }
295 
296     @Override
297     public void postEnableTable(ObserverContext<MasterCoprocessorEnvironment> env,
298         byte[] tableName) throws IOException {
299       postEnableTableCalled = true;
300     }
301 
302     public boolean wasEnableTableCalled() {
303       return preEnableTableCalled && postEnableTableCalled;
304     }
305 
306     public boolean preEnableTableCalledOnly() {
307       return preEnableTableCalled && !postEnableTableCalled;
308     }
309 
310     @Override
311     public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> env,
312         byte[] tableName) throws IOException {
313       if (bypass) {
314         env.bypass();
315       }
316       preDisableTableCalled = true;
317     }
318 
319     @Override
320     public void postDisableTable(ObserverContext<MasterCoprocessorEnvironment> env,
321         byte[] tableName) throws IOException {
322       postDisableTableCalled = true;
323     }
324 
325     public boolean wasDisableTableCalled() {
326       return preDisableTableCalled && postDisableTableCalled;
327     }
328 
329     public boolean preDisableTableCalledOnly() {
330       return preDisableTableCalled && !postDisableTableCalled;
331     }
332 
333     @Override
334     public void preMove(ObserverContext<MasterCoprocessorEnvironment> env,
335         HRegionInfo region, ServerName srcServer, ServerName destServer)
336     throws IOException {
337       if (bypass) {
338         env.bypass();
339       }
340       preMoveCalled = true;
341     }
342 
343     @Override
344     public void postMove(ObserverContext<MasterCoprocessorEnvironment> env, HRegionInfo region,
345         ServerName srcServer, ServerName destServer)
346     throws IOException {
347       postMoveCalled = true;
348     }
349 
350     public boolean wasMoveCalled() {
351       return preMoveCalled && postMoveCalled;
352     }
353 
354     public boolean preMoveCalledOnly() {
355       return preMoveCalled && !postMoveCalled;
356     }
357     
358     @Override
359     public void preAssign(ObserverContext<MasterCoprocessorEnvironment> env,
360         final HRegionInfo regionInfo) throws IOException {
361       if (bypass) {
362         env.bypass();
363       }
364       preAssignCalled = true;
365     }
366 
367     @Override
368     public void postAssign(ObserverContext<MasterCoprocessorEnvironment> env,
369         final HRegionInfo regionInfo) throws IOException {
370       postAssignCalled = true;
371     }
372     
373     public boolean wasAssignCalled() {
374       return preAssignCalled && postAssignCalled;
375     }
376 
377     public boolean preAssignCalledOnly() {
378       return preAssignCalled && !postAssignCalled;
379     }
380 
381     @Override
382     public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> env,
383         final HRegionInfo regionInfo, final boolean force) throws IOException {
384       if (bypass) {
385         env.bypass();
386       }
387       preUnassignCalled = true;
388     }
389 
390     @Override
391     public void postUnassign(ObserverContext<MasterCoprocessorEnvironment> env,
392         final HRegionInfo regionInfo, final boolean force) throws IOException {
393       postUnassignCalled = true;
394     }
395 
396     public boolean wasUnassignCalled() {
397       return preUnassignCalled && postUnassignCalled;
398     }
399 
400     public boolean preUnassignCalledOnly() {
401       return preUnassignCalled && !postUnassignCalled;
402     }
403 
404     @Override
405     public void preBalance(ObserverContext<MasterCoprocessorEnvironment> env)
406         throws IOException {
407       if (bypass) {
408         env.bypass();
409       }
410       preBalanceCalled = true;
411     }
412 
413     @Override
414     public void postBalance(ObserverContext<MasterCoprocessorEnvironment> env)
415         throws IOException {
416       postBalanceCalled = true;
417     }
418 
419     public boolean wasBalanceCalled() {
420       return preBalanceCalled && postBalanceCalled;
421     }
422 
423     public boolean preBalanceCalledOnly() {
424       return preBalanceCalled && !postBalanceCalled;
425     }
426 
427     @Override
428     public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> env, boolean b)
429         throws IOException {
430       if (bypass) {
431         env.bypass();
432       }
433       preBalanceSwitchCalled = true;
434       return b;
435     }
436 
437     @Override
438     public void postBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> env,
439         boolean oldValue, boolean newValue) throws IOException {
440       postBalanceSwitchCalled = true;
441     }
442 
443     public boolean wasBalanceSwitchCalled() {
444       return preBalanceSwitchCalled && postBalanceSwitchCalled;
445     }
446 
447     public boolean preBalanceSwitchCalledOnly() {
448       return preBalanceSwitchCalled && !postBalanceSwitchCalled;
449     }
450 
451     @Override
452     public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> env)
453         throws IOException {
454       preShutdownCalled = true;
455     }
456 
457     @Override
458     public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> env)
459         throws IOException {
460       preStopMasterCalled = true;
461     }
462 
463     @Override
464     public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx)
465         throws IOException {
466       postStartMasterCalled = true;
467     }
468 
469     public boolean wasStartMasterCalled() {
470       return postStartMasterCalled;
471     }
472 
473     @Override
474     public void start(CoprocessorEnvironment env) throws IOException {
475       startCalled = true;
476     }
477 
478     @Override
479     public void stop(CoprocessorEnvironment env) throws IOException {
480       stopCalled = true;
481     }
482 
483     public boolean wasStarted() { return startCalled; }
484 
485     public boolean wasStopped() { return stopCalled; }
486 
487     @Override
488     public void preSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
489         final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
490         throws IOException {
491       preSnapshotCalled = true;
492     }
493 
494     @Override
495     public void postSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
496         final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
497         throws IOException {
498       postSnapshotCalled = true;
499     }
500 
501     public boolean wasSnapshotCalled() {
502       return preSnapshotCalled && postSnapshotCalled;
503     }
504 
505     @Override
506     public void preCloneSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
507         final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
508         throws IOException {
509       preCloneSnapshotCalled = true;
510     }
511 
512     @Override
513     public void postCloneSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
514         final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
515         throws IOException {
516       postCloneSnapshotCalled = true;
517     }
518 
519     public boolean wasCloneSnapshotCalled() {
520       return preCloneSnapshotCalled && postCloneSnapshotCalled;
521     }
522 
523     @Override
524     public void preRestoreSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
525         final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
526         throws IOException {
527       preRestoreSnapshotCalled = true;
528     }
529 
530     @Override
531     public void postRestoreSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
532         final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
533         throws IOException {
534       postRestoreSnapshotCalled = true;
535     }
536 
537     public boolean wasRestoreSnapshotCalled() {
538       return preRestoreSnapshotCalled && postRestoreSnapshotCalled;
539     }
540 
541     @Override
542     public void preDeleteSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
543         final SnapshotDescription snapshot) throws IOException {
544       preDeleteSnapshotCalled = true;
545     }
546 
547     @Override
548     public void postDeleteSnapshot(final ObserverContext<MasterCoprocessorEnvironment> ctx,
549         final SnapshotDescription snapshot) throws IOException {
550       postDeleteSnapshotCalled = true;
551     }
552 
553     public boolean wasDeleteSnapshotCalled() {
554       return preDeleteSnapshotCalled && postDeleteSnapshotCalled;
555     }
556 
557     @Override
558     public void preGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
559         List<String> tableNamesList, List<HTableDescriptor> descriptors) throws IOException {
560       preGetTableDescriptorsCalled = true;
561     }
562 
563     @Override
564     public void postGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
565         List<HTableDescriptor> descriptors) throws IOException {
566       postGetTableDescriptorsCalled = true;
567     }
568 
569     public boolean wasGetTableDescriptorsCalled() {
570       return preGetTableDescriptorsCalled && postGetTableDescriptorsCalled;
571     }
572   }
573 
574   private static HBaseTestingUtility UTIL = new HBaseTestingUtility();
575   private static byte[] TEST_SNAPSHOT = Bytes.toBytes("observed_snapshot");
576   private static byte[] TEST_TABLE = Bytes.toBytes("observed_table");
577   private static byte[] TEST_CLONE = Bytes.toBytes("observed_clone");
578   private static byte[] TEST_FAMILY = Bytes.toBytes("fam1");
579   private static byte[] TEST_FAMILY2 = Bytes.toBytes("fam2");
580 
581   @BeforeClass
582   public static void setupBeforeClass() throws Exception {
583     Configuration conf = UTIL.getConfiguration();
584     conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
585         CPMasterObserver.class.getName());
586     // Enable snapshot
587     conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
588     // We need more than one data server on this test
589     UTIL.startMiniCluster(2);
590   }
591 
592   @AfterClass
593   public static void tearDownAfterClass() throws Exception {
594     UTIL.shutdownMiniCluster();
595   }
596 
597   @Test
598   public void testStarted() throws Exception {
599     MiniHBaseCluster cluster = UTIL.getHBaseCluster();
600 
601     HMaster master = cluster.getMaster();
602     assertTrue("Master should be active", master.isActiveMaster());
603     MasterCoprocessorHost host = master.getCoprocessorHost();
604     assertNotNull("CoprocessorHost should not be null", host);
605     CPMasterObserver cp = (CPMasterObserver)host.findCoprocessor(
606         CPMasterObserver.class.getName());
607     assertNotNull("CPMasterObserver coprocessor not found or not installed!", cp);
608 
609     // check basic lifecycle
610     assertTrue("MasterObserver should have been started", cp.wasStarted());
611     assertTrue("postStartMaster() hook should have been called",
612         cp.wasStartMasterCalled());
613   }
614 
615   @Test
616   public void testTableOperations() throws Exception {
617     MiniHBaseCluster cluster = UTIL.getHBaseCluster();
618 
619     HMaster master = cluster.getMaster();
620     MasterCoprocessorHost host = master.getCoprocessorHost();
621     CPMasterObserver cp = (CPMasterObserver)host.findCoprocessor(
622         CPMasterObserver.class.getName());
623     cp.enableBypass(true);
624     cp.resetStates();
625     assertFalse("No table created yet", cp.wasCreateTableCalled());
626 
627     // create a table
628     HTableDescriptor htd = new HTableDescriptor(TEST_TABLE);
629     htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
630     HBaseAdmin admin = UTIL.getHBaseAdmin();
631 
632     admin.createTable(htd);
633     // preCreateTable can't bypass default action.
634     assertTrue("Test table should be created", cp.wasCreateTableCalled());
635 
636     admin.disableTable(TEST_TABLE);
637     assertTrue(admin.isTableDisabled(TEST_TABLE));
638     // preDisableTable can't bypass default action.
639     assertTrue("Coprocessor should have been called on table disable",
640       cp.wasDisableTableCalled());
641 
642     // enable
643     assertFalse(cp.wasEnableTableCalled());
644     admin.enableTable(TEST_TABLE);
645     assertTrue(admin.isTableEnabled(TEST_TABLE));
646     // preEnableTable can't bypass default action.
647     assertTrue("Coprocessor should have been called on table enable",
648       cp.wasEnableTableCalled());
649 
650     admin.disableTable(TEST_TABLE);
651     assertTrue(admin.isTableDisabled(TEST_TABLE));
652 
653     // modify table
654     htd.setMaxFileSize(512 * 1024 * 1024);
655     modifyTableSync(admin, TEST_TABLE, htd);
656     // preModifyTable can't bypass default action.
657     assertTrue("Test table should have been modified",
658       cp.wasModifyTableCalled());
659 
660     // add a column family
661     admin.addColumn(TEST_TABLE, new HColumnDescriptor(TEST_FAMILY2));
662     assertTrue("New column family shouldn't have been added to test table",
663       cp.preAddColumnCalledOnly());
664 
665     // modify a column family
666     HColumnDescriptor hcd1 = new HColumnDescriptor(TEST_FAMILY2);
667     hcd1.setMaxVersions(25);
668     admin.modifyColumn(TEST_TABLE, hcd1);
669     assertTrue("Second column family should be modified",
670       cp.preModifyColumnCalledOnly());
671 
672     // delete table
673     admin.deleteTable(TEST_TABLE);
674     assertFalse("Test table should have been deleted",
675         admin.tableExists(TEST_TABLE));
676     // preDeleteTable can't bypass default action.
677     assertTrue("Coprocessor should have been called on table delete",
678       cp.wasDeleteTableCalled());
679 
680 
681     // turn off bypass, run the tests again
682     cp.enableBypass(false);
683     cp.resetStates();
684 
685     admin.createTable(htd);
686     assertTrue("Test table should be created", cp.wasCreateTableCalled());
687 
688     // disable
689     assertFalse(cp.wasDisableTableCalled());
690 
691     admin.disableTable(TEST_TABLE);
692     assertTrue(admin.isTableDisabled(TEST_TABLE));
693     assertTrue("Coprocessor should have been called on table disable",
694       cp.wasDisableTableCalled());
695 
696     // modify table
697     htd.setMaxFileSize(512 * 1024 * 1024);
698     modifyTableSync(admin, TEST_TABLE, htd);
699     assertTrue("Test table should have been modified",
700         cp.wasModifyTableCalled());
701 
702     // add a column family
703     admin.addColumn(TEST_TABLE, new HColumnDescriptor(TEST_FAMILY2));
704     assertTrue("New column family should have been added to test table",
705         cp.wasAddColumnCalled());
706 
707     // modify a column family
708     HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY2);
709     hcd.setMaxVersions(25);
710     admin.modifyColumn(TEST_TABLE, hcd);
711     assertTrue("Second column family should be modified",
712         cp.wasModifyColumnCalled());
713 
714     // enable
715     assertFalse(cp.wasEnableTableCalled());
716     admin.enableTable(TEST_TABLE);
717     assertTrue(admin.isTableEnabled(TEST_TABLE));
718     assertTrue("Coprocessor should have been called on table enable",
719         cp.wasEnableTableCalled());
720 
721     // disable again
722     admin.disableTable(TEST_TABLE);
723     assertTrue(admin.isTableDisabled(TEST_TABLE));
724 
725     // delete column
726     assertFalse("No column family deleted yet", cp.wasDeleteColumnCalled());
727     admin.deleteColumn(TEST_TABLE, TEST_FAMILY2);
728     HTableDescriptor tableDesc = admin.getTableDescriptor(TEST_TABLE);
729     assertNull("'"+Bytes.toString(TEST_FAMILY2)+"' should have been removed",
730         tableDesc.getFamily(TEST_FAMILY2));
731     assertTrue("Coprocessor should have been called on column delete",
732         cp.wasDeleteColumnCalled());
733 
734     // delete table
735     assertFalse("No table deleted yet", cp.wasDeleteTableCalled());
736     admin.deleteTable(TEST_TABLE);
737     assertFalse("Test table should have been deleted",
738         admin.tableExists(TEST_TABLE));
739     assertTrue("Coprocessor should have been called on table delete",
740         cp.wasDeleteTableCalled());
741   }
742 
743   private void modifyTableSync(HBaseAdmin admin, byte[] tableName, HTableDescriptor htd)
744       throws IOException {
745     admin.modifyTable(tableName, htd);
746     //wait until modify table finishes
747     for (int t = 0; t < 100; t++) { //10 sec timeout
748       HTableDescriptor td = admin.getTableDescriptor(htd.getName());
749       if (td.equals(htd)) {
750         break;
751       }
752       Threads.sleep(100);
753     }
754   }
755 
756   @Test
757   public void testRegionTransitionOperations() throws Exception {
758     MiniHBaseCluster cluster = UTIL.getHBaseCluster();
759 
760     HMaster master = cluster.getMaster();
761     MasterCoprocessorHost host = master.getCoprocessorHost();
762     CPMasterObserver cp = (CPMasterObserver)host.findCoprocessor(
763         CPMasterObserver.class.getName());
764     cp.enableBypass(false);
765     cp.resetStates();
766 
767     HTable table = UTIL.createTable(TEST_TABLE, TEST_FAMILY);
768     UTIL.createMultiRegions(table, TEST_FAMILY);
769     UTIL.waitUntilAllRegionsAssigned(TEST_TABLE);
770     
771     NavigableMap<HRegionInfo, ServerName> regions = table.getRegionLocations();
772     Map.Entry<HRegionInfo, ServerName> firstGoodPair = null;
773     for (Map.Entry<HRegionInfo, ServerName> e: regions.entrySet()) {
774       if (e.getValue() != null) {
775         firstGoodPair = e;
776         break;
777       }
778     }
779     assertNotNull("Found a non-null entry", firstGoodPair);
780     LOG.info("Found " + firstGoodPair.toString());
781     // Try to force a move
782     Collection<ServerName> servers = master.getClusterStatus().getServers();
783     String destName = null;
784     String firstRegionHostnamePortStr = firstGoodPair.getValue().toString();
785     LOG.info("firstRegionHostnamePortStr=" + firstRegionHostnamePortStr);
786     boolean found = false;
787     // Find server that is NOT carrying the first region
788     for (ServerName info : servers) {
789       LOG.info("ServerName=" + info);
790       if (!firstRegionHostnamePortStr.equals(info.getHostAndPort())) {
791         destName = info.toString();
792         found = true;
793         break;
794       }
795     }
796     assertTrue("Found server", found);
797     LOG.info("Found " + destName);
798     master.move(firstGoodPair.getKey().getEncodedNameAsBytes(),
799       Bytes.toBytes(destName));
800     assertTrue("Coprocessor should have been called on region move",
801       cp.wasMoveCalled());
802 
803     // make sure balancer is on
804     master.balanceSwitch(true);
805     assertTrue("Coprocessor should have been called on balance switch",
806         cp.wasBalanceSwitchCalled());
807 
808     // force region rebalancing
809     master.balanceSwitch(false);
810     // move half the open regions from RS 0 to RS 1
811     HRegionServer rs = cluster.getRegionServer(0);
812     byte[] destRS = Bytes.toBytes(cluster.getRegionServer(1).getServerName().toString());
813     //Make sure no regions are in transition now
814     waitForRITtoBeZero(master);
815     List<HRegionInfo> openRegions = rs.getOnlineRegions();
816     int moveCnt = openRegions.size()/2;
817     for (int i=0; i<moveCnt; i++) {
818       HRegionInfo info = openRegions.get(i);
819       if (!info.isMetaTable()) {
820         master.move(openRegions.get(i).getEncodedNameAsBytes(), destRS);
821       }
822     }
823     //Make sure no regions are in transition now
824     waitForRITtoBeZero(master);
825     // now trigger a balance
826     master.balanceSwitch(true);
827     boolean balanceRun = master.balance();
828     assertTrue("Coprocessor should be called on region rebalancing",
829         cp.wasBalanceCalled());
830   }
831 
832   @Test
833   public void testSnapshotOperations() throws Exception {
834     MiniHBaseCluster cluster = UTIL.getHBaseCluster();
835     HMaster master = cluster.getMaster();
836     MasterCoprocessorHost host = master.getCoprocessorHost();
837     CPMasterObserver cp = (CPMasterObserver)host.findCoprocessor(
838         CPMasterObserver.class.getName());
839     cp.resetStates();
840 
841     // create a table
842     HTableDescriptor htd = new HTableDescriptor(TEST_TABLE);
843     htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
844     HBaseAdmin admin = UTIL.getHBaseAdmin();
845 
846     // delete table if exists
847     if (admin.tableExists(TEST_TABLE)) {
848       UTIL.deleteTable(TEST_TABLE);
849     }
850 
851     admin.createTable(htd);
852     admin.disableTable(TEST_TABLE);
853     assertTrue(admin.isTableDisabled(TEST_TABLE));
854 
855     try {
856       // Test snapshot operation
857       assertFalse("Coprocessor should not have been called yet",
858         cp.wasSnapshotCalled());
859       admin.snapshot(TEST_SNAPSHOT, TEST_TABLE);
860       assertTrue("Coprocessor should have been called on snapshot",
861         cp.wasSnapshotCalled());
862 
863       // Test clone operation
864       admin.cloneSnapshot(TEST_SNAPSHOT, TEST_CLONE);
865       assertTrue("Coprocessor should have been called on snapshot clone",
866         cp.wasCloneSnapshotCalled());
867       assertFalse("Coprocessor restore should not have been called on snapshot clone",
868         cp.wasRestoreSnapshotCalled());
869       admin.disableTable(TEST_CLONE);
870       assertTrue(admin.isTableDisabled(TEST_TABLE));
871       admin.deleteTable(TEST_CLONE);
872 
873       // Test restore operation
874       cp.resetStates();
875       admin.restoreSnapshot(TEST_SNAPSHOT);
876       assertTrue("Coprocessor should have been called on snapshot restore",
877         cp.wasRestoreSnapshotCalled());
878       assertFalse("Coprocessor clone should not have been called on snapshot restore",
879         cp.wasCloneSnapshotCalled());
880 
881       admin.deleteSnapshot(TEST_SNAPSHOT);
882       assertTrue("Coprocessor should have been called on snapshot delete",
883         cp.wasDeleteSnapshotCalled());
884     } finally {
885       admin.deleteTable(TEST_TABLE);
886     }
887   }
888 
889   private void waitForRITtoBeZero(HMaster master) throws IOException {
890     // wait for assignments to finish
891     AssignmentManager mgr = master.getAssignmentManager();
892     Collection<AssignmentManager.RegionState> transRegions =
893         mgr.getRegionsInTransition().values();
894     for (AssignmentManager.RegionState state : transRegions) {
895       mgr.waitOnRegionToClearRegionsInTransition(state.getRegion());
896     }
897   }
898 
899   @Test
900   public void testTableDescriptorsEnumeration() throws Exception {
901     MiniHBaseCluster cluster = UTIL.getHBaseCluster();
902 
903     HMaster master = cluster.getMaster();
904     MasterCoprocessorHost host = master.getCoprocessorHost();
905     CPMasterObserver cp = (CPMasterObserver)host.findCoprocessor(
906         CPMasterObserver.class.getName());
907     cp.resetStates();
908 
909     master.getHTableDescriptors();
910 
911     assertTrue("Coprocessor should be called on table descriptors request",
912       cp.wasGetTableDescriptorsCalled());
913   }
914 
915   @org.junit.Rule
916   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
917     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
918 }
919