diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckEmptyRegionInfo.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckEmptyRegionInfo.java new file mode 100644 index 000000000000..5d1ca54bf1be --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckEmptyRegionInfo.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import org.apache.yetus.audience.InterfaceAudience; + +/** + * POJO to present Empty Region Info from Catalog Janitor Inconsistencies Report via REST API. These + * inconsistencies are shown on hbck.jsp page on Active HMaster UI as part of Catalog Janitor + * inconsistencies. + */ +@InterfaceAudience.Public +public class HbckEmptyRegionInfo { + private final String regionInfo; + + public HbckEmptyRegionInfo(String emptyRegionInfo) { + this.regionInfo = emptyRegionInfo; + } + + public String getRegionInfo() { + return regionInfo; + } +} diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckInconsistentRegions.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckInconsistentRegions.java new file mode 100644 index 000000000000..f32f73a73d15 --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckInconsistentRegions.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import java.util.List; +import org.apache.yetus.audience.InterfaceAudience; + +/** + * POJO to present HBCK Inconsistent Regions from HBCK Inconsistencies Report via REST API. These + * inconsistencies are shown on hbck.jsp page on Active HMaster UI as part of HBCK inconsistencies. + */ +@InterfaceAudience.Public +public class HbckInconsistentRegions { + private final String regionId; + private final HbckServerName serverNameInMeta; + private final List listOfServers; + + public HbckInconsistentRegions(String inconsistentRegionId, HbckServerName serverNameInMeta, + List listOfServerName) { + this.regionId = inconsistentRegionId; + this.serverNameInMeta = serverNameInMeta; + this.listOfServers = listOfServerName; + } + + public String getRegionId() { + return regionId; + } + + public HbckServerName getServerNameInMeta() { + return serverNameInMeta; + } + + public List getListOfServers() { + return listOfServers; + } +} diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckOrphanRegionsOnFS.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckOrphanRegionsOnFS.java new file mode 100644 index 000000000000..43a045fb2933 --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckOrphanRegionsOnFS.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import org.apache.yetus.audience.InterfaceAudience; + +/** + * POJO to present Orphan Region on FS from HBCK Inconsistencies Report via REST API. These + * inconsistencies are shown on hbck.jsp page on Active HMaster UI as part of HBCK Inconsistencies. + */ +@InterfaceAudience.Public +public class HbckOrphanRegionsOnFS { + private final String regionId; + private final String regionHdfsPath; + + public HbckOrphanRegionsOnFS(String regionId, String orphanRegionHdfsPath) { + this.regionId = regionId; + this.regionHdfsPath = orphanRegionHdfsPath; + } + + public String getRegionId() { + return regionId; + } + + public String getRegionHdfsPath() { + return regionHdfsPath; + } +} diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckOrphanRegionsOnRS.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckOrphanRegionsOnRS.java new file mode 100644 index 000000000000..2d442b7a9e40 --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckOrphanRegionsOnRS.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import org.apache.yetus.audience.InterfaceAudience; + +/** + * POJO to present Orphan Region on RS from HBCK Inconsistencies Report via REST API. These + * inconsistencies are shown on hbck.jsp page on Active HMaster UI as part of HBCK Inconsistencies. + */ +@InterfaceAudience.Public +public class HbckOrphanRegionsOnRS { + private final String regionId; + private final HbckServerName rsName; + + public HbckOrphanRegionsOnRS(String orphanRegionId, HbckServerName orphanRegionRsName) { + this.regionId = orphanRegionId; + this.rsName = orphanRegionRsName; + } + + public String getRegionId() { + return regionId; + } + + public HbckServerName getRsName() { + return rsName; + } +} diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckOverlapRegions.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckOverlapRegions.java new file mode 100644 index 000000000000..4170932bf563 --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckOverlapRegions.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import org.apache.yetus.audience.InterfaceAudience; + +/** + * POJO to present Region Overlap from Catalog Janitor Inconsistencies Report via REST API. These + * inconsistencies are shown on hbck.jsp page on Active HMaster UI as part of Catalog Janitor + * inconsistencies. + */ +@InterfaceAudience.Public +public class HbckOverlapRegions { + private final HbckRegionDetails region1Info; + private final HbckRegionDetails region2Info; + + public HbckOverlapRegions(HbckRegionDetails region1Info, HbckRegionDetails region2Info) { + this.region1Info = region1Info; + this.region2Info = region2Info; + } + + public HbckRegionDetails getRegion1Info() { + return region1Info; + } + + public HbckRegionDetails getRegion2Info() { + return region2Info; + } +} diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckRegionDetails.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckRegionDetails.java new file mode 100644 index 000000000000..a79245636276 --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckRegionDetails.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import org.apache.yetus.audience.InterfaceAudience; + +/** + * POJO class for HBCK RegionInfo in HBCK Inconsistencies report. + */ +@InterfaceAudience.Public +public class HbckRegionDetails { + private final String regionId; + private final String tableName; + private final String startKey; + private final String endKey; + + public HbckRegionDetails(String regionId, String tableName, String startKey, String endKey) { + this.regionId = regionId; + this.tableName = tableName; + this.startKey = startKey; + this.endKey = endKey; + } + + public String getRegionId() { + return regionId; + } + + public String getTableName() { + return tableName; + } + + public String getStartKey() { + return startKey; + } + + public String getEndKey() { + return endKey; + } +} diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckRegionHoles.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckRegionHoles.java new file mode 100644 index 000000000000..643e014735a0 --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckRegionHoles.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import org.apache.yetus.audience.InterfaceAudience; + +/** + * POJO to present Region Holes from Catalog Janitor Inconsistencies Report via REST API. These + * inconsistencies are shown on hbck.jsp page on Active HMaster UI as part of Catalog Janitor + * inconsistencies. + */ +@InterfaceAudience.Public +public class HbckRegionHoles { + private final HbckRegionDetails region1Info; + private final HbckRegionDetails region2Info; + + public HbckRegionHoles(HbckRegionDetails region1Info, HbckRegionDetails region2Info) { + this.region1Info = region1Info; + this.region2Info = region2Info; + } + + public HbckRegionDetails getRegion1Info() { + return region1Info; + } + + public HbckRegionDetails getRegion2Info() { + return region2Info; + } +} diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckServerName.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckServerName.java new file mode 100644 index 000000000000..2c6b899fb15c --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckServerName.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import org.apache.yetus.audience.InterfaceAudience; + +/** + * POJO class for ServerName in HBCK Inconsistencies report. + */ +@InterfaceAudience.Public +public class HbckServerName { + private final String hostName; + private final int hostPort; + private final long startCode; + + public HbckServerName(String hostName, int hostPort, long startCode) { + this.hostName = hostName; + this.hostPort = hostPort; + this.startCode = startCode; + } + + public String getHostName() { + return hostName; + } + + public int getHostPort() { + return hostPort; + } + + public long getStartCode() { + return startCode; + } +} diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckUnknownServers.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckUnknownServers.java new file mode 100644 index 000000000000..c070f84e69fe --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HbckUnknownServers.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import org.apache.yetus.audience.InterfaceAudience; + +/** + * POJO to present Unknown Regions from Catalog Janitor Inconsistencies Report via REST API. These + * inconsistencies are shown on hbck.jsp page on Active HMaster UI as part of Catalog Janitor + * inconsistencies. + */ +@InterfaceAudience.Public +public class HbckUnknownServers { + private final HbckRegionDetails regionInfo; + private final HbckServerName serverName; + + public HbckUnknownServers(HbckRegionDetails regionInfo, HbckServerName unknownServerName) { + this.regionInfo = regionInfo; + this.serverName = unknownServerName; + } + + public HbckRegionDetails getRegionInfo() { + return regionInfo; + } + + public HbckServerName getServerName() { + return serverName; + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index 0dc5b61cba8c..0f4162cd1f74 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -146,6 +146,7 @@ import org.apache.hadoop.hbase.master.http.MasterRedirectServlet; import org.apache.hadoop.hbase.master.http.MasterStatusServlet; import org.apache.hadoop.hbase.master.http.api_v1.ResourceConfigFactory; +import org.apache.hadoop.hbase.master.http.hbck.HbckConfigFactory; import org.apache.hadoop.hbase.master.janitor.CatalogJanitor; import org.apache.hadoop.hbase.master.locking.LockManager; import org.apache.hadoop.hbase.master.migrate.RollingUpgradeChore; @@ -760,6 +761,7 @@ protected MasterRpcServices createRpcServices() throws IOException { protected void configureInfoServer(InfoServer infoServer) { infoServer.addUnprivilegedServlet("master-status", "/master-status", MasterStatusServlet.class); infoServer.addUnprivilegedServlet("api_v1", "/api/v1/*", buildApiV1Servlet()); + infoServer.addUnprivilegedServlet("hbck", "/hbck/*", buildHbckServlet()); infoServer.setAttribute(MASTER, this); } @@ -769,6 +771,11 @@ private ServletHolder buildApiV1Servlet() { return new ServletHolder(new ServletContainer(config)); } + private ServletHolder buildHbckServlet() { + final ResourceConfig config = HbckConfigFactory.createResourceConfig(conf, this); + return new ServletHolder(new ServletContainer(config)); + } + @Override protected Class getDumpServlet() { return MasterDumpServlet.class; @@ -1377,6 +1384,22 @@ private void finishActiveMasterInitialization() throws IOException, InterruptedE status.markComplete("Progress after master initialized complete"); } + /** + * Used for testing only to set Mock objects. + * @param hbckChore hbckChore + */ + public void setHbckChoreForTesting(HbckChore hbckChore) { + this.hbckChore = hbckChore; + } + + /** + * Used for testing only to set Mock objects. + * @param catalogJanitorChore catalogJanitorChore + */ + public void setCatalogJanitorChoreForTesting(CatalogJanitor catalogJanitorChore) { + this.catalogJanitorChore = catalogJanitorChore; + } + private void createMissingCFsInMetaDuringUpgrade(TableDescriptor metaDescriptor) throws IOException { TableDescriptor newMetaDesc = TableDescriptorBuilder.newBuilder(metaDescriptor) @@ -4257,6 +4280,7 @@ public SyncReplicationReplayWALManager getSyncReplicationReplayWALManager() { return this.syncReplicationReplayWALManager; } + @Override public HbckChore getHbckChore() { return this.hbckChore; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java index b5e25bb44f33..3aa5c2df751b 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java @@ -34,6 +34,7 @@ import org.apache.hadoop.hbase.executor.ExecutorService; import org.apache.hadoop.hbase.favored.FavoredNodesManager; import org.apache.hadoop.hbase.master.assignment.AssignmentManager; +import org.apache.hadoop.hbase.master.hbck.HbckChore; import org.apache.hadoop.hbase.master.janitor.CatalogJanitor; import org.apache.hadoop.hbase.master.locking.LockManager; import org.apache.hadoop.hbase.master.normalizer.RegionNormalizerManager; @@ -106,6 +107,9 @@ public interface MasterServices extends Server { /** Returns Master's instance of {@link CatalogJanitor} */ CatalogJanitor getCatalogJanitor(); + /** Returns Master's instance of {@link HbckChore} */ + HbckChore getHbckChore(); + /** Returns Master's instance of {@link ProcedureExecutor} */ ProcedureExecutor getMasterProcedureExecutor(); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/http/hbck/HbckConfigFactory.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/http/hbck/HbckConfigFactory.java new file mode 100644 index 000000000000..32dfd4a23b9c --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/http/hbck/HbckConfigFactory.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.master.http.hbck; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.http.jersey.ResponseEntityMapper; +import org.apache.hadoop.hbase.master.HMaster; +import org.apache.hadoop.hbase.master.http.gson.GsonSerializationFeature; +import org.apache.hadoop.hbase.master.http.jersey.MasterFeature; +import org.apache.yetus.audience.InterfaceAudience; + +import org.apache.hbase.thirdparty.org.glassfish.jersey.server.ResourceConfig; +import org.apache.hbase.thirdparty.org.glassfish.jersey.server.ServerProperties; +import org.apache.hbase.thirdparty.org.glassfish.jersey.server.TracingConfig; + +@InterfaceAudience.Private +public final class HbckConfigFactory { + private HbckConfigFactory() { + } + + public static ResourceConfig createResourceConfig(Configuration conf, HMaster master) { + return new ResourceConfig().setApplicationName("hbck") + .packages(HbckConfigFactory.class.getPackage().getName()) + // TODO: anything registered here that does not have necessary bindings won't inject properly + // at annotation sites and will result in a WARN logged by o.a.h.t.o.g.j.i.inject.Providers. + // These warnings should be treated by the service as fatal errors, but I have not found a + // callback API for registering a failed binding handler. + .register(ResponseEntityMapper.class).register(GsonSerializationFeature.class) + .register(new MasterFeature(master)) + + // devs: enable TRACING to see how jersey is dispatching to resources. + // in hbase-site.xml, set 'hbase.http.jersey.tracing.type=ON_DEMAND` and + // to curl, add `-H X-Jersey-Tracing-Accept:true` + .property(ServerProperties.TRACING, + conf.get("hbase.http.jersey.tracing.type", TracingConfig.OFF.name())) + .property(ServerProperties.TRACING_THRESHOLD, + conf.get("hbase.http.jersey.tracing.threshold", "TRACE")); + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/http/hbck/model/HbckMetrics.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/http/hbck/model/HbckMetrics.java new file mode 100644 index 000000000000..ba4cfecdcf4c --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/http/hbck/model/HbckMetrics.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.master.http.hbck.model; + +import java.util.List; +import org.apache.hadoop.hbase.HbckEmptyRegionInfo; +import org.apache.hadoop.hbase.HbckInconsistentRegions; +import org.apache.hadoop.hbase.HbckOrphanRegionsOnFS; +import org.apache.hadoop.hbase.HbckOrphanRegionsOnRS; +import org.apache.hadoop.hbase.HbckOverlapRegions; +import org.apache.hadoop.hbase.HbckRegionHoles; +import org.apache.hadoop.hbase.HbckUnknownServers; +import org.apache.yetus.audience.InterfaceAudience; + +/** + * This class exposes hbck.jsp report as JSON Output via /hbck/hbck-metrics API. + */ +@InterfaceAudience.Private +public class HbckMetrics { + + private final long hbckReportStartTime; + private final long hbckReportEndTime; + private final List hbckOrphanRegionsOnFs; + private final List hbckOrphanRegionsOnRs; + private final List hbckInconsistentRegions; + private final List hbckHoles; + private final List hbckOverlaps; + private final List hbckUnknownServers; + private final List hbckEmptyRegionInfo; + + public HbckMetrics(long hbckReportStartTime, long hbckReportEndTime, + List hbckOrphanRegionsOnFs, + List hbckOrphanRegionsOnRs, + List hbckInconsistentRegions, List hbckHoles, + List hbckOverlaps, List hbckUnknownServers, + List hbckEmptyRegionInfo) { + this.hbckReportStartTime = hbckReportStartTime; + this.hbckReportEndTime = hbckReportEndTime; + this.hbckOrphanRegionsOnFs = hbckOrphanRegionsOnFs; + this.hbckOrphanRegionsOnRs = hbckOrphanRegionsOnRs; + this.hbckInconsistentRegions = hbckInconsistentRegions; + this.hbckHoles = hbckHoles; + this.hbckOverlaps = hbckOverlaps; + this.hbckUnknownServers = hbckUnknownServers; + this.hbckEmptyRegionInfo = hbckEmptyRegionInfo; + } + + public long gethbckReportStartTime() { + return hbckReportStartTime; + } + + public long gethbckReportEndTime() { + return hbckReportEndTime; + } + + public List gethbckOrphanRegionsOnFs() { + return hbckOrphanRegionsOnFs; + } + + public List gethbckOrphanRegionsOnRs() { + return hbckOrphanRegionsOnRs; + } + + public List gethbckInconsistentRegions() { + return hbckInconsistentRegions; + } + + public List gethbckHoles() { + return hbckHoles; + } + + public List gethbckOverlaps() { + return hbckOverlaps; + } + + public List gethbckUnknownServers() { + return hbckUnknownServers; + } + + public List gethbckEmptyRegionInfo() { + return hbckEmptyRegionInfo; + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/http/hbck/resource/HbckMetricsResource.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/http/hbck/resource/HbckMetricsResource.java new file mode 100644 index 000000000000..96924aa126d3 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/http/hbck/resource/HbckMetricsResource.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.master.http.hbck.resource; + +import java.util.List; +import java.util.stream.Collectors; +import javax.inject.Inject; +import org.apache.hadoop.hbase.HbckEmptyRegionInfo; +import org.apache.hadoop.hbase.HbckInconsistentRegions; +import org.apache.hadoop.hbase.HbckOrphanRegionsOnFS; +import org.apache.hadoop.hbase.HbckOrphanRegionsOnRS; +import org.apache.hadoop.hbase.HbckOverlapRegions; +import org.apache.hadoop.hbase.HbckRegionDetails; +import org.apache.hadoop.hbase.HbckRegionHoles; +import org.apache.hadoop.hbase.HbckServerName; +import org.apache.hadoop.hbase.HbckUnknownServers; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.master.MasterServices; +import org.apache.hadoop.hbase.master.hbck.HbckReport; +import org.apache.hadoop.hbase.master.http.hbck.model.HbckMetrics; +import org.apache.hadoop.hbase.master.janitor.CatalogJanitorReport; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.yetus.audience.InterfaceAudience; + +import org.apache.hbase.thirdparty.javax.ws.rs.GET; +import org.apache.hbase.thirdparty.javax.ws.rs.Path; +import org.apache.hbase.thirdparty.javax.ws.rs.Produces; +import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType; + +/** + * The root object exposing hbck.jsp page as JSON Output. + */ +@Path("hbck-metrics") +@Produces({ MediaType.APPLICATION_JSON }) +@InterfaceAudience.Private +public class HbckMetricsResource { + private final HbckReport hbckReport; + private final CatalogJanitorReport catalogJanitorReport; + + @Inject + public HbckMetricsResource(MasterServices master) { + this.hbckReport = master.getHbckChore().getLastReport(); + this.catalogJanitorReport = master.getCatalogJanitor().getLastReport(); + } + + @GET + public HbckMetrics getBaseHbckMetrics() { + return new HbckMetrics(hbckReport.getCheckingStartTimestamp().toEpochMilli(), + hbckReport.getCheckingEndTimestamp().toEpochMilli(), getOrphanRegionsOnFS(), + getOrphanRegionsOnRS(), getInconsistentRegions(), getRegionChainHoles(), + getRegionChainOverlap(), getUnknownServers(), getEmptyRegionInfo()); + } + + @GET + @Path("/orphan-regions-on-fs") + public List getOrphanRegionsOnFS() { + return hbckReport.getOrphanRegionsOnFS().entrySet().stream() + .map(obj1 -> new HbckOrphanRegionsOnFS(obj1.getKey(), obj1.getValue().toString())) + .collect(Collectors.toList()); + } + + @GET + @Path("/orphan-regions-on-rs") + public List getOrphanRegionsOnRS() { + return hbckReport.getOrphanRegionsOnRS().entrySet().stream() + .map(obj1 -> new HbckOrphanRegionsOnRS(obj1.getKey(), parseServerName(obj1.getValue()))) + .collect(Collectors.toList()); + } + + @GET + @Path("/inconsistent-regions") + public List getInconsistentRegions() { + return hbckReport.getInconsistentRegions().entrySet().stream() + .map(obj1 -> new HbckInconsistentRegions(obj1.getKey(), + parseServerName(obj1.getValue().getFirst()), obj1.getValue().getSecond().stream() + .map(this::parseServerName).collect(Collectors.toList()))) + .collect(Collectors.toList()); + } + + @GET + @Path("/region-holes") + public List getRegionChainHoles() { + return catalogJanitorReport.getHoles().stream() + .map(obj1 -> new HbckRegionHoles(parseRegionInfo(obj1.getFirst()), + parseRegionInfo(obj1.getSecond()))) + .collect(Collectors.toList()); + } + + @GET + @Path("/region-overlaps") + public List getRegionChainOverlap() { + return catalogJanitorReport.getOverlaps().stream() + .map(obj1 -> new HbckOverlapRegions(parseRegionInfo(obj1.getFirst()), + parseRegionInfo(obj1.getSecond()))) + .collect(Collectors.toList()); + } + + @GET + @Path("/unknown-servers") + public List getUnknownServers() { + return catalogJanitorReport.getUnknownServers().stream() + .map(obj1 -> new HbckUnknownServers(parseRegionInfo(obj1.getFirst()), + parseServerName(obj1.getSecond()))) + .collect(Collectors.toList()); + } + + @GET + @Path("/empty-regioninfo") + public List getEmptyRegionInfo() { + return catalogJanitorReport.getEmptyRegionInfo().stream() + .map(obj1 -> new HbckEmptyRegionInfo(Bytes.toString(obj1))).collect(Collectors.toList()); + } + + public HbckRegionDetails parseRegionInfo(RegionInfo regionInfo) { + return new HbckRegionDetails(regionInfo.getEncodedName(), + regionInfo.getTable().getNameAsString(), new String(regionInfo.getStartKey()), + new String(regionInfo.getEndKey())); + } + + public HbckServerName parseServerName(ServerName serverName) { + return new HbckServerName(serverName.getHostname(), serverName.getPort(), + serverName.getStartCode()); + } +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockNoopMasterServices.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockNoopMasterServices.java index 0c4f3d7db266..3d4d63722e09 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockNoopMasterServices.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockNoopMasterServices.java @@ -39,6 +39,7 @@ import org.apache.hadoop.hbase.executor.ExecutorService; import org.apache.hadoop.hbase.favored.FavoredNodesManager; import org.apache.hadoop.hbase.master.assignment.AssignmentManager; +import org.apache.hadoop.hbase.master.hbck.HbckChore; import org.apache.hadoop.hbase.master.janitor.CatalogJanitor; import org.apache.hadoop.hbase.master.locking.LockManager; import org.apache.hadoop.hbase.master.normalizer.RegionNormalizerManager; @@ -117,6 +118,11 @@ public CatalogJanitor getCatalogJanitor() { return null; } + @Override + public HbckChore getHbckChore() { + return null; + } + @Override public MasterFileSystem getMasterFileSystem() { return null; diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/http/TestHbckMetricsResource.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/http/TestHbckMetricsResource.java new file mode 100644 index 000000000000..6ed5087ccbdb --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/http/TestHbckMetricsResource.java @@ -0,0 +1,422 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.master.http; + +import static org.apache.hadoop.hbase.client.RegionInfoBuilder.FIRST_META_REGIONINFO; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hbase.ConnectionRule; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.MiniClusterRule; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.StartTestingClusterOption; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.AsyncAdmin; +import org.apache.hadoop.hbase.client.AsyncConnection; +import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; +import org.apache.hadoop.hbase.client.Durability; +import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.client.TableDescriptor; +import org.apache.hadoop.hbase.client.TableDescriptorBuilder; +import org.apache.hadoop.hbase.master.HMaster; +import org.apache.hadoop.hbase.master.hbck.HbckChore; +import org.apache.hadoop.hbase.master.hbck.HbckReport; +import org.apache.hadoop.hbase.master.http.hbck.resource.HbckMetricsResource; +import org.apache.hadoop.hbase.master.janitor.CatalogJanitor; +import org.apache.hadoop.hbase.master.janitor.CatalogJanitorReport; +import org.apache.hadoop.hbase.testclassification.LargeTests; +import org.apache.hadoop.hbase.testclassification.MasterTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.Pair; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.ExternalResource; +import org.junit.rules.RuleChain; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.hbase.thirdparty.javax.ws.rs.NotAcceptableException; +import org.apache.hbase.thirdparty.javax.ws.rs.client.Client; +import org.apache.hbase.thirdparty.javax.ws.rs.client.ClientBuilder; +import org.apache.hbase.thirdparty.javax.ws.rs.client.WebTarget; +import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType; + +/** + * Tests for the {@link HbckMetricsResource}. + */ +@Category({ MasterTests.class, LargeTests.class }) +public class TestHbckMetricsResource { + + private static final Logger LOG = LoggerFactory.getLogger(TestHbckMetricsResource.class); + + // Test data for Mock HBCK Report + private static final long reportStartTime = 123456789000L; + private static final long reportEndTime = 234567890000L; + private static final String regionId1 = "regionId1"; + private static final String regionId2 = "regionId2"; + private static final String localhost1 = "localhost1"; + private static final String localhost2 = "localhost2"; + private static final String port = "16010"; + private static final String hostStartCode = "123456789"; + private static final String path1 = "hdfs://path1"; + private static final String path2 = "hdfs://path2"; + private static final String metaRegionID = FIRST_META_REGIONINFO.getEncodedName(); + private static final String metaTableName = FIRST_META_REGIONINFO.getTable().getNameAsString(); + + // Various Keys in HBCK JSON Response. + private static final String quoteColon = "\":"; + private static final String quote = "\""; + private static final String regionId = quote + "region_id" + quoteColon; + private static final String regionHdfsPath = quote + "region_hdfs_path" + quoteColon; + private static final String rsName = quote + "rs_name" + quoteColon; + private static final String hostName = quote + "host_name" + quoteColon; + private static final String hostPort = quote + "host_port" + quoteColon; + private static final String startCode = quote + "start_code" + quoteColon; + private static final String serverNameInMeta = quote + "server_name_in_meta" + quoteColon; + private static final String listOfServers = quote + "list_of_servers" + quoteColon; + private static final String region1Info = quote + "region1_info" + quoteColon; + private static final String region2Info = quote + "region2_info" + quoteColon; + private static final String regionInfo = quote + "region_info" + quoteColon; + private static final String serverName = quote + "server_name" + quoteColon; + private static final String tableName = quote + "table_name" + quoteColon; + + private static final String dataStartsWith = "{\"data\":["; + private static final String dataEndsWith = "]}"; + private static final String hbckReportStartTime = quote + "hbck_report_start_time" + quoteColon; + private static final String hbckReportEndTime = quote + "hbck_report_end_time" + quoteColon; + private static final String hbckOrphanRegionOnFS = + quote + "hbck_orphan_regions_on_fs" + quoteColon; + private static final String hbckOrphanRegionOnRS = + quote + "hbck_orphan_regions_on_rs" + quoteColon; + private static final String hbckInconsistentRegion = + quote + "hbck_inconsistent_regions" + quoteColon; + private static final String hbckHoles = quote + "hbck_holes" + quoteColon; + private static final String hbckOverlaps = quote + "hbck_overlaps" + quoteColon; + private static final String hbckUnknownServers = quote + "hbck_unknown_servers" + quoteColon; + private static final String hbckEmptyRegionInfo = quote + "hbck_empty_region_info" + quoteColon; + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestHbckMetricsResource.class); + + private static final MiniClusterRule miniClusterRule = MiniClusterRule.newBuilder() + .setMiniClusterOption( + StartTestingClusterOption.builder().numZkServers(3).numMasters(3).numDataNodes(3).build()) + .setConfiguration(() -> { + // enable Master InfoServer and random port selection + final Configuration conf = HBaseConfiguration.create(); + conf.setInt(HConstants.MASTER_INFO_PORT, 0); + conf.set("hbase.http.jersey.tracing.type", "ON_DEMAND"); + return conf; + }).build(); + + private static final ConnectionRule connectionRule = + ConnectionRule.createAsyncConnectionRule(miniClusterRule::createAsyncConnection); + private static final ClassSetup classRule = new ClassSetup(connectionRule::getAsyncConnection); + + private static final class ClassSetup extends ExternalResource { + + private final Supplier connectionSupplier; + private final TableName tableName; + private AsyncAdmin admin; + private WebTarget target; + + public ClassSetup(final Supplier connectionSupplier) { + this.connectionSupplier = connectionSupplier; + tableName = TableName.valueOf(TestHbckMetricsResource.class.getSimpleName()); + } + + public WebTarget getTarget() { + return target; + } + + @Override + protected void before() throws Throwable { + final AsyncConnection conn = connectionSupplier.get(); + admin = conn.getAdmin(); + final TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName) + .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("c")).build()) + .setDurability(Durability.SKIP_WAL).build(); + admin.createTable(tableDescriptor).get(); + + HMaster master = miniClusterRule.getTestingUtility().getMiniHBaseCluster().getMaster(); + + HbckChore hbckChore = mock(HbckChore.class); + HbckReport hbckReport = mock(HbckReport.class); + CatalogJanitor catalogJanitorChore = mock(CatalogJanitor.class); + CatalogJanitorReport catalogJanitorReport = mock(CatalogJanitorReport.class); + master.setHbckChoreForTesting(hbckChore); + master.setCatalogJanitorChoreForTesting(catalogJanitorChore); + + // Test data for Mock HBCK Report + ServerName server1 = + ServerName.valueOf(localhost1, Integer.parseInt(port), Integer.parseInt(hostStartCode)); + ServerName server2 = + ServerName.valueOf(localhost2, Integer.parseInt(port), Integer.parseInt(hostStartCode)); + Path hdfsPath1 = new Path(path1); + Path hdfsPath2 = new Path(path2); + + // Orphan on RS Test data + Map mapOfOrphanRegionsOnRS = new HashMap<>(); + mapOfOrphanRegionsOnRS.put(regionId1, server1); + mapOfOrphanRegionsOnRS.put(regionId2, server2); + + // Orphan Region on FS Test Data + Map mapOfOrphanRegionOnFS = new HashMap<>(); + mapOfOrphanRegionOnFS.put(regionId1, hdfsPath1); + mapOfOrphanRegionOnFS.put(regionId2, hdfsPath2); + + // Inconsistent Regions Test Data + Map>> mapOfInconsistentRegions = new HashMap<>(); + mapOfInconsistentRegions.put(regionId1, new Pair<>(server1, Arrays.asList(server1, server2))); + mapOfInconsistentRegions.put(regionId2, new Pair<>(server2, Arrays.asList(server1, server2))); + + // Region Overlap and Region Holes Test Data + List> listOfRegion = new ArrayList<>(); + listOfRegion.add(new Pair<>(FIRST_META_REGIONINFO, FIRST_META_REGIONINFO)); + listOfRegion.add(new Pair<>(FIRST_META_REGIONINFO, FIRST_META_REGIONINFO)); + + // Unknown RegionServer Test Data + List> listOfUnknownServers = new ArrayList<>(); + listOfUnknownServers.add(new Pair<>(FIRST_META_REGIONINFO, server1)); + listOfUnknownServers.add(new Pair<>(FIRST_META_REGIONINFO, server2)); + + // Empty Region Info Test Data + List listOfEmptyRegionInfo = new ArrayList<>(); + listOfEmptyRegionInfo.add(regionId1.getBytes()); + listOfEmptyRegionInfo.add(regionId2.getBytes()); + + // Mock HBCK Report and CatalogJanitor Report + when(hbckReport.getCheckingStartTimestamp()) + .thenReturn(Instant.ofEpochMilli(reportStartTime)); + when(hbckReport.getCheckingEndTimestamp()).thenReturn(Instant.ofEpochSecond(reportEndTime)); + when(hbckReport.getOrphanRegionsOnFS()).thenReturn(mapOfOrphanRegionOnFS); + when(hbckReport.getOrphanRegionsOnRS()).thenReturn(mapOfOrphanRegionsOnRS); + when(hbckReport.getInconsistentRegions()).thenReturn(mapOfInconsistentRegions); + when(catalogJanitorReport.getHoles()).thenReturn(listOfRegion); + when(catalogJanitorReport.getOverlaps()).thenReturn(listOfRegion); + when(catalogJanitorReport.getUnknownServers()).thenReturn(listOfUnknownServers); + when(catalogJanitorReport.getEmptyRegionInfo()).thenReturn(listOfEmptyRegionInfo); + + Mockito.doReturn(hbckReport).when(hbckChore).getLastReport(); + Mockito.doReturn(catalogJanitorReport).when(catalogJanitorChore).getLastReport(); + + final String baseUrl = + admin.getMaster().thenApply(ServerName::getHostname).thenCombine(admin.getMasterInfoPort(), + (hostName, infoPort) -> "http://" + hostName + ":" + infoPort).get(); + final Client client = ClientBuilder.newClient(); + target = client.target(baseUrl).path("hbck/hbck-metrics"); + } + + @Override + protected void after() { + final TableName tableName = TableName.valueOf("test"); + try { + admin.tableExists(tableName).thenCompose(val -> { + if (val) { + return admin.disableTable(tableName) + .thenCompose(ignored -> admin.deleteTable(tableName)); + } else { + return CompletableFuture.completedFuture(null); + } + }).get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @ClassRule + public static RuleChain ruleChain = + RuleChain.outerRule(miniClusterRule).around(connectionRule).around(classRule); + + @Test + public void testGetRoot() { + final String response = classRule.getTarget().request(MediaType.APPLICATION_JSON_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class); + LOG.info("HBCK JSON Response : " + response); + assertThat(response, + allOf(containsString(hbckReportStartTime), containsString(hbckReportEndTime), + containsString(hbckOrphanRegionOnFS), containsString(hbckOrphanRegionOnRS), + containsString(hbckInconsistentRegion), containsString(hbckHoles), + containsString(hbckOverlaps), containsString(hbckUnknownServers), + containsString(hbckEmptyRegionInfo), containsString(Objects.toString(reportStartTime)), + containsString(Objects.toString(reportEndTime)))); + } + + @Test + public void testGetRootHtml() { + assertThrows(NotAcceptableException.class, () -> classRule.getTarget() + .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); + } + + @Test + public void testGetOrphanRegionOnFS() { + final String response = + classRule.getTarget().path("orphan-regions-on-fs").request(MediaType.APPLICATION_JSON_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class); + LOG.info("HBCK Response for resource orphan-regions-on-fs : " + response); + assertThat(response, + allOf(startsWith(dataStartsWith), endsWith(dataEndsWith), containsString(regionId), + containsString(regionHdfsPath), containsString(regionId1), containsString(regionId2), + containsString(path1), containsString(path2))); + } + + @Test + public void testGetOrphanRegionOnFSHtml() { + assertThrows(NotAcceptableException.class, + () -> classRule.getTarget().path("orphan-regions-on-fs").request(MediaType.TEXT_HTML_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class)); + } + + @Test + public void testGetOrphanRegionOnRS() { + final String response = + classRule.getTarget().path("orphan-regions-on-rs").request(MediaType.APPLICATION_JSON_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class); + LOG.info("HBCK Response for resource orphan-regions-on-rs : " + response); + assertThat(response, + allOf(startsWith(dataStartsWith), endsWith(dataEndsWith), containsString(regionId), + containsString(rsName), containsString(hostName), containsString(hostPort), + containsString(startCode), containsString(regionId1), containsString(regionId2), + containsString(localhost1), containsString(localhost2), containsString(port), + containsString(hostStartCode))); + } + + @Test + public void testGetOrphanRegionOnRSHtml() { + assertThrows(NotAcceptableException.class, + () -> classRule.getTarget().path("orphan-regions-on-rs").request(MediaType.TEXT_HTML_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class)); + } + + @Test + public void testGetInconsistentRegions() { + final String response = + classRule.getTarget().path("inconsistent-regions").request(MediaType.APPLICATION_JSON_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class); + LOG.info("HBCK Response for resource inconsistent-regions : " + response); + assertThat(response, + allOf(startsWith(dataStartsWith), endsWith(dataEndsWith), containsString(hostName), + containsString(hostPort), containsString(startCode), containsString(listOfServers), + containsString(regionId1), containsString(regionId2), containsString(regionId), + containsString(serverNameInMeta), containsString(localhost1), containsString(localhost2), + containsString(port), containsString(hostStartCode))); + } + + @Test + public void testGetInconsistentRegionsHtml() { + assertThrows(NotAcceptableException.class, + () -> classRule.getTarget().path("inconsistent-regions").request(MediaType.TEXT_HTML_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class)); + } + + @Test + public void testGetRegionHoles() { + final String response = + classRule.getTarget().path("region-holes").request(MediaType.APPLICATION_JSON_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class); + LOG.info("HBCK Response for resource region-holes : " + response); + assertThat(response, + allOf(startsWith(dataStartsWith), endsWith(dataEndsWith), containsString(region1Info), + containsString(region2Info), containsString(regionId), containsString(tableName), + containsString(metaRegionID), containsString(metaTableName))); + } + + @Test + public void testGetRegionHolesHtml() { + assertThrows(NotAcceptableException.class, () -> classRule.getTarget().path("region-holes") + .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); + } + + @Test + public void testGetRegionOverlaps() { + final String response = + classRule.getTarget().path("region-overlaps").request(MediaType.APPLICATION_JSON_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class); + LOG.info("HBCK Response for resource region-overlaps : " + response); + assertThat(response, + allOf(startsWith(dataStartsWith), endsWith(dataEndsWith), containsString(regionId), + containsString(tableName), containsString(region2Info), containsString(region2Info), + containsString(metaRegionID), containsString(metaTableName))); + } + + @Test + public void testGetRegionOverlapsHtml() { + assertThrows(NotAcceptableException.class, () -> classRule.getTarget().path("region-overlaps") + .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); + } + + @Test + public void testGetUnkownServers() { + final String response = + classRule.getTarget().path("unknown-servers").request(MediaType.APPLICATION_JSON_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class); + LOG.info("HBCK Response for resource unknown-servers : " + response); + assertThat(response, + allOf(startsWith(dataStartsWith), endsWith(dataEndsWith), containsString(regionInfo), + containsString(regionId), containsString(tableName), containsString(serverName), + containsString(serverName), containsString(port), containsString(startCode), + containsString(metaRegionID), containsString(metaTableName), containsString(localhost1), + containsString(localhost2), containsString(port), containsString(startCode))); + } + + @Test + public void testGetUnkownServersHtml() { + assertThrows(NotAcceptableException.class, () -> classRule.getTarget().path("unknown-servers") + .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); + } + + @Test + public void testGetEmptyRegionInfo() { + final String response = + classRule.getTarget().path("empty-regioninfo").request(MediaType.APPLICATION_JSON_TYPE) + .header("X-Jersey-Tracing-Accept", true).get(String.class); + LOG.info("HBCK Response for resource empty-regioninfo : " + response); + assertThat(response, allOf(startsWith(dataStartsWith), endsWith(dataEndsWith), + containsString(regionInfo), containsString(regionId1), containsString(regionId2))); + } + + @Test + public void testGetEmptyRegionInfoHtml() { + assertThrows(NotAcceptableException.class, () -> classRule.getTarget().path("empty-regioninfo") + .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); + } +}