diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/resolver/FederationNamenodeServiceState.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/resolver/FederationNamenodeServiceState.java
index ed8f8c0456..71bec63445 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/resolver/FederationNamenodeServiceState.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/resolver/FederationNamenodeServiceState.java
@@ -25,6 +25,7 @@
*/
public enum FederationNamenodeServiceState {
ACTIVE, // HAServiceState.ACTIVE or operational.
+ OBSERVER, // HAServiceState.OBSERVER.
STANDBY, // HAServiceState.STANDBY.
UNAVAILABLE, // When the namenode cannot be reached.
EXPIRED, // When the last update is too old.
@@ -34,10 +35,9 @@ public static FederationNamenodeServiceState getState(HAServiceState state) {
switch(state) {
case ACTIVE:
return FederationNamenodeServiceState.ACTIVE;
- case STANDBY:
- // TODO: we should probably have a separate state OBSERVER for RBF and
- // treat it differently.
case OBSERVER:
+ return FederationNamenodeServiceState.OBSERVER;
+ case STANDBY:
return FederationNamenodeServiceState.STANDBY;
case INITIALIZING:
return FederationNamenodeServiceState.UNAVAILABLE;
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/router/federationhealth.html b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/router/federationhealth.html
index cbe983f034..9db42b2878 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/router/federationhealth.html
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/router/federationhealth.html
@@ -129,6 +129,7 @@
- Active
+ - Observer
- Standby
- Safe mode
- Disabled
@@ -196,6 +197,7 @@
- Active
+ - Observer
- Standby
- Safe mode
- Unavailable
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/router/federationhealth.js b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/router/federationhealth.js
index 2d770ad691..3e24b41573 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/router/federationhealth.js
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/router/federationhealth.js
@@ -123,6 +123,9 @@
} else if (n.state === "ACTIVE") {
n.title = capitalise(n.state);
n.iconState = "active";
+ } else if (nodes[i].state === "OBSERVER") {
+ n.title = capitalise(n.state);
+ n.iconState = "observer";
} else if (nodes[i].state === "STANDBY") {
n.title = capitalise(n.state);
n.iconState = "standby";
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/static/rbf.css b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/static/rbf.css
index d1420a2f23..33f72f54c5 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/static/rbf.css
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/webapps/static/rbf.css
@@ -38,6 +38,11 @@
content: "\e013";
}
+.federationhealth-namenode-observer:before {
+ color: #CCCC00;
+ content: "\e013";
+}
+
.federationhealth-namenode-unavailable:before {
color: #c7254e;
content: "\e101";
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/metrics/TestMetricsBase.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/metrics/TestMetricsBase.java
index 2169b21279..429695a9a0 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/metrics/TestMetricsBase.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/metrics/TestMetricsBase.java
@@ -19,20 +19,26 @@
import static org.apache.hadoop.hdfs.server.federation.FederationTestUtils.NAMENODES;
import static org.apache.hadoop.hdfs.server.federation.FederationTestUtils.NAMESERVICES;
+import static org.apache.hadoop.hdfs.server.federation.FederationTestUtils.ROUTERS;
import static org.apache.hadoop.hdfs.server.federation.store.FederationStateStoreTestUtils.clearAllRecords;
import static org.apache.hadoop.hdfs.server.federation.store.FederationStateStoreTestUtils.createMockMountTable;
import static org.apache.hadoop.hdfs.server.federation.store.FederationStateStoreTestUtils.createMockRegistrationForNamenode;
import static org.apache.hadoop.hdfs.server.federation.store.FederationStateStoreTestUtils.synchronizeRecords;
import static org.apache.hadoop.hdfs.server.federation.store.FederationStateStoreTestUtils.waitStateStore;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.server.federation.RouterConfigBuilder;
import org.apache.hadoop.hdfs.server.federation.resolver.FederationNamenodeServiceState;
+import org.apache.hadoop.hdfs.server.federation.resolver.MembershipNamenodeResolver;
import org.apache.hadoop.hdfs.server.federation.router.Router;
import org.apache.hadoop.hdfs.server.federation.router.RouterServiceState;
import org.apache.hadoop.hdfs.server.federation.store.MembershipStore;
@@ -41,14 +47,18 @@
import org.apache.hadoop.hdfs.server.federation.store.protocol.GetRouterRegistrationRequest;
import org.apache.hadoop.hdfs.server.federation.store.protocol.GetRouterRegistrationResponse;
import org.apache.hadoop.hdfs.server.federation.store.protocol.NamenodeHeartbeatRequest;
+import org.apache.hadoop.hdfs.server.federation.store.protocol.NamenodeHeartbeatResponse;
import org.apache.hadoop.hdfs.server.federation.store.protocol.RouterHeartbeatRequest;
import org.apache.hadoop.hdfs.server.federation.store.records.MembershipState;
import org.apache.hadoop.hdfs.server.federation.store.records.MountTable;
import org.apache.hadoop.hdfs.server.federation.store.records.RouterState;
import org.apache.hadoop.hdfs.server.federation.store.records.StateStoreVersion;
import org.apache.hadoop.util.Time;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
import org.junit.After;
import org.junit.Before;
+import org.junit.Test;
/**
* Test the basic metrics functionality.
@@ -193,4 +203,59 @@ protected List getMockRouters() {
protected StateStoreService getStateStore() {
return stateStore;
}
+
+ @Test
+ public void testObserverMetrics() throws Exception {
+ mockObserver();
+
+ RBFMetrics metrics = router.getMetrics();
+ String jsonString = metrics.getNameservices();
+ JSONObject jsonObject = new JSONObject(jsonString);
+ Map map = getNameserviceStateMap(jsonObject);
+ assertTrue("Cannot find ns0 in: " + jsonString, map.containsKey("ns0"));
+ assertEquals("OBSERVER", map.get("ns0"));
+ }
+
+ public static Map getNameserviceStateMap(
+ JSONObject jsonObject) throws JSONException {
+ Map map = new TreeMap<>();
+ Iterator> keys = jsonObject.keys();
+ while (keys.hasNext()) {
+ String key = (String) keys.next();
+ JSONObject json = jsonObject.getJSONObject(key);
+ String nsId = json.getString("nameserviceId");
+ String state = json.getString("state");
+ map.put(nsId, state);
+ }
+ return map;
+ }
+
+ private void mockObserver() throws IOException {
+ String ns = "ns0";
+ String nn = "nn0";
+ createRegistration(ns, nn, ROUTERS[1],
+ FederationNamenodeServiceState.OBSERVER);
+
+ // Load data into cache and calculate quorum
+ assertTrue(stateStore.loadCache(MembershipStore.class, true));
+ membershipStore.loadCache(true);
+ MembershipNamenodeResolver resolver =
+ (MembershipNamenodeResolver) router.getNamenodeResolver();
+ resolver.loadCache(true);
+ }
+
+ private MembershipState createRegistration(String ns, String nn,
+ String routerId, FederationNamenodeServiceState state)
+ throws IOException {
+ MembershipState record =
+ MembershipState.newInstance(routerId, ns, nn, "testcluster",
+ "testblock-" + ns, "testrpc-" + ns + nn, "testservice-" + ns + nn,
+ "testlifeline-" + ns + nn, "testweb-" + ns + nn, state, false);
+ NamenodeHeartbeatRequest request =
+ NamenodeHeartbeatRequest.newInstance(record);
+ NamenodeHeartbeatResponse response =
+ membershipStore.namenodeHeartbeat(request);
+ assertTrue(response.getResult());
+ return record;
+ }
}
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/store/TestStateStoreMembershipState.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/store/TestStateStoreMembershipState.java
index f1f15c67b4..9ec9e03853 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/store/TestStateStoreMembershipState.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/store/TestStateStoreMembershipState.java
@@ -104,6 +104,16 @@ public void testNamenodeStateOverride() throws Exception {
MembershipState newState = getNamenodeRegistration(ns, nn);
assertEquals(FederationNamenodeServiceState.ACTIVE, newState.getState());
+
+ // Override cache
+ UpdateNamenodeRegistrationRequest request1 =
+ UpdateNamenodeRegistrationRequest.newInstance(ns, nn,
+ FederationNamenodeServiceState.OBSERVER);
+ assertTrue(
+ membershipStore.updateNamenodeRegistration(request1).getResult());
+
+ MembershipState newState1 = getNamenodeRegistration(ns, nn);
+ assertEquals(FederationNamenodeServiceState.OBSERVER, newState1.getState());
}
@Test