You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ao...@apache.org on 2016/06/08 10:40:48 UTC

[1/2] ambari git commit: AMBARI-17063. Retrieve specific metrics when Ambari queries NameNode HA states (aonishuk)

Repository: ambari
Updated Branches:
  refs/heads/branch-2.4 f516c79e1 -> 7d9818ba8
  refs/heads/trunk 32890b554 -> 35ff7d79a


AMBARI-17063. Retrieve specific metrics when Ambari queries NameNode HA states (aonishuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/35ff7d79
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/35ff7d79
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/35ff7d79

Branch: refs/heads/trunk
Commit: 35ff7d79a8b8c2c74bf772efff295af1dc4091f8
Parents: 32890b5
Author: Andrew Onishuk <ao...@hortonworks.com>
Authored: Wed Jun 8 13:40:40 2016 +0300
Committer: Andrew Onishuk <ao...@hortonworks.com>
Committed: Wed Jun 8 13:40:40 2016 +0300

----------------------------------------------------------------------
 .../controller/jmx/JMXPropertyProvider.java     |  48 ++++++++-
 .../controller/jmx/TestStreamProvider.java      |  19 +++-
 .../metrics/JMXPropertyProviderTest.java        | 102 ++++++++++++++++++-
 .../resources/hdfs_namenode_jmx_ha_only.json    |   7 ++
 4 files changed, 167 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/35ff7d79/ambari-server/src/main/java/org/apache/ambari/server/controller/jmx/JMXPropertyProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/jmx/JMXPropertyProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/jmx/JMXPropertyProvider.java
index a315e5c..7665d7f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/jmx/JMXPropertyProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/jmx/JMXPropertyProvider.java
@@ -86,6 +86,19 @@ public class JMXPropertyProvider extends ThreadPoolEnabledPropertyProvider {
 
   private static final Map<String, String> DEFAULT_JMX_PORTS = new HashMap<String, String>();
 
+  /**
+   * When Ambari queries NameNode's HA state (among other metrics), it retrieves all metrics from "NN_URL:port/jmx".
+   * But some metrics may compete for the NameNode lock and a request to /jmx may take much time.
+   * <p>
+   * The properties from this map will be retrieved using a provided URL query.
+   * Even if JMX is locked and a request for all metrics is waiting (/jmx is unavailable),
+   * HAState will be updated via a separate JMX call.
+   * <p>
+   * Currently org.apache.hadoop.jmx.JMXJsonServlet can provide only one property per a request,
+   * each property from this list adds a request to JMX.
+   */
+  private static final Map<String, Map<String, String>> AD_HOC_PROPERTIES = new HashMap<>();
+
   static {
     DEFAULT_JMX_PORTS.put("NAMENODE",           "50070");
     DEFAULT_JMX_PORTS.put("DATANODE",           "50075");
@@ -96,6 +109,10 @@ public class JMXPropertyProvider extends ThreadPoolEnabledPropertyProvider {
     DEFAULT_JMX_PORTS.put("NODEMANAGER",         "8042");
     DEFAULT_JMX_PORTS.put("JOURNALNODE",         "8480");
     DEFAULT_JMX_PORTS.put("STORM_REST_API",      "8745");
+
+    AD_HOC_PROPERTIES.put("NAMENODE",
+        Collections.singletonMap("metrics/dfs/FSNamesystem/HAState",
+                                 "/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState"));
   }
 
   protected final static Logger LOG =
@@ -249,17 +266,40 @@ public class JMXPropertyProvider extends ThreadPoolEnabledPropertyProvider {
 
         // check to see if there is a cached value and use it if there is
         JMXMetricHolder jmxMetricHolder = metricsRetrievalService.getCachedJMXMetric(jmxUrl);
-        if (null == jmxMetricHolder) {
-          return resource;
-        }
 
         // if the ticket becomes invalid (timeout) then bail out
         if (!ticket.isValid()) {
           return resource;
         }
 
-        getHadoopMetricValue(jmxMetricHolder, ids, resource, request, ticket);
+        if (null != jmxMetricHolder) {
+          getHadoopMetricValue(jmxMetricHolder, ids, resource, request, ticket);
+        }
 
+        if (AD_HOC_PROPERTIES.containsKey(componentName)) {
+          for (String propertyId : ids) {
+            for (String adHocId : AD_HOC_PROPERTIES.get(componentName).keySet()) {
+              String queryURL = null;
+              // if all metrics from "metrics/dfs/FSNamesystem" were requested, retrieves HAState.
+              if (adHocId.equals(propertyId) || adHocId.startsWith(propertyId + '/')) {
+                queryURL = AD_HOC_PROPERTIES.get(componentName).get(adHocId);
+              }
+              if (queryURL != null) {
+                String adHocUrl = getSpec(protocol, hostName, port, queryURL);
+                metricsRetrievalService.submitJMXRequest(streamProvider, adHocUrl);
+                JMXMetricHolder adHocJMXMetricHolder = metricsRetrievalService.getCachedJMXMetric(adHocUrl);
+
+                // if the ticket becomes invalid (timeout) then bail out
+                if (!ticket.isValid()) {
+                  return resource;
+                }
+                if (null != adHocJMXMetricHolder) {
+                  getHadoopMetricValue(adHocJMXMetricHolder, Collections.singleton(propertyId), resource, request, ticket);
+                }
+              }
+            }
+          }
+        }
       } catch (IOException e) {
         AmbariException detailedException = new AmbariException(String.format(
             "Unable to get JMX metrics from the host %s for the component %s. Spec: %s", hostName,

http://git-wip-us.apache.org/repos/asf/ambari/blob/35ff7d79/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java
index 8819991..f29dc6f 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java
@@ -24,7 +24,9 @@ import org.apache.ambari.server.controller.utilities.StreamProvider;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 public class TestStreamProvider extends URLStreamProvider {
@@ -43,12 +45,15 @@ public class TestStreamProvider extends URLStreamProvider {
     FILE_MAPPING.put("8745",  "storm_rest_api_jmx.json");
   }
 
+  private static String NN_HASTATE_ONLY_JMX = "hdfs_namenode_jmx_ha_only.json";
+
   /**
    * Delay to simulate response time.
    */
   protected final long delay;
 
   private String lastSpec;
+  private List<String> specs = new ArrayList<>();
 
   private boolean isLastSpecUpdated;
 
@@ -64,11 +69,19 @@ public class TestStreamProvider extends URLStreamProvider {
 
   @Override
   public InputStream readFrom(String spec) throws IOException {
+    specs.add(spec);
     if (!isLastSpecUpdated)
       lastSpec = spec;
     
     isLastSpecUpdated = false;
-    String filename = FILE_MAPPING.get(getPort(spec));
+
+    String filename = null;
+    if (spec.endsWith(":50070/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState")) {
+      filename = NN_HASTATE_ONLY_JMX;
+    } else {
+      filename = FILE_MAPPING.get(getPort(spec));
+    }
+
     if (filename == null) {
       throw new IOException("Can't find JMX source for " + spec);
     }
@@ -87,6 +100,10 @@ public class TestStreamProvider extends URLStreamProvider {
     return lastSpec;
   }
 
+  public List<String> getSpecs() {
+    return specs;
+  }
+
   private String getPort(String spec) {
     int colonIndex = spec.indexOf(":", 5);
     int slashIndex = spec.indexOf("/", colonIndex);

http://git-wip-us.apache.org/repos/asf/ambari/blob/35ff7d79/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/JMXPropertyProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/JMXPropertyProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/JMXPropertyProviderTest.java
index 4adea20..80d7438 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/JMXPropertyProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/JMXPropertyProviderTest.java
@@ -24,9 +24,11 @@ import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 
 import java.lang.reflect.Field;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -34,6 +36,7 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.AmbariServer;
+import org.apache.ambari.server.controller.internal.PropertyInfo;
 import org.apache.ambari.server.controller.internal.ResourceImpl;
 import org.apache.ambari.server.controller.jmx.JMXHostProvider;
 import org.apache.ambari.server.controller.jmx.JMXPropertyProvider;
@@ -72,6 +75,15 @@ public class JMXPropertyProviderTest {
   protected static final String HOST_COMPONENT_STATE_PROPERTY_ID = PropertyHelper.getPropertyId("HostRoles", "state");
 
   public static final int NUMBER_OF_RESOURCES = 400;
+
+  public static final Map<String, Map<String, PropertyInfo>> jmxPropertyIds = PropertyHelper.getJMXPropertyIds(Resource.Type.HostComponent);
+  public static final Map<String, Map<String, PropertyInfo>> jmxPropertyIdsWithHAState;
+
+  static {
+    jmxPropertyIdsWithHAState = new HashMap<>(jmxPropertyIds);
+    jmxPropertyIdsWithHAState.get("NAMENODE").put("metrics/dfs/FSNamesystem/HAState", new PropertyInfo("Hadoop:service=NameNode,name=FSNamesystem.tag#HAState", false, true));
+  }
+
   private static MetricPropertyProviderFactory metricPropertyProviderFactory;
 
   @BeforeClass
@@ -126,6 +138,7 @@ public class JMXPropertyProviderTest {
     testPopulateResourcesUnhealthyResource();
     testPopulateResourcesMany();
     testPopulateResourcesTimeout();
+    testPopulateResources_HAState_request();
   }
 
   @Test
@@ -139,6 +152,7 @@ public class JMXPropertyProviderTest {
     testPopulateResourcesUnhealthyResource();
     testPopulateResourcesMany();
     testPopulateResourcesTimeout();
+    testPopulateResources_HAState_request();
   }
 
   @Test
@@ -152,6 +166,7 @@ public class JMXPropertyProviderTest {
     testPopulateResourcesUnhealthyResource();
     testPopulateResourcesMany();
     testPopulateResourcesTimeout();
+    testPopulateResources_HAState_request();
   }
 
   @Test(expected = AuthorizationException.class)
@@ -167,6 +182,7 @@ public class JMXPropertyProviderTest {
     testPopulateResourcesUnhealthyResource();
     testPopulateResourcesMany();
     testPopulateResourcesTimeout();
+    testPopulateResources_HAState_request();
   }
 
   public void testPopulateResources() throws Exception {
@@ -174,7 +190,7 @@ public class JMXPropertyProviderTest {
     TestJMXHostProvider hostProvider = new TestJMXHostProvider(false);
     TestMetricHostProvider metricsHostProvider = new TestMetricHostProvider();
     JMXPropertyProvider propertyProvider = metricPropertyProviderFactory.createJMXPropertyProvider(
-      PropertyHelper.getJMXPropertyIds(Resource.Type.HostComponent),
+      jmxPropertyIdsWithHAState,
       streamProvider,
       hostProvider,
       metricsHostProvider,
@@ -194,7 +210,10 @@ public class JMXPropertyProviderTest {
     Request request = PropertyHelper.getReadRequest(Collections.<String>emptySet());
     Assert.assertEquals(1, propertyProvider.populateResources(Collections.singleton(resource), request, null).size());
 
-    Assert.assertEquals(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"), streamProvider.getLastSpec());
+    List<String> expectedSpecs = new ArrayList<String>();
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"));
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState"));
+    Assert.assertEquals(expectedSpecs, streamProvider.getSpecs());
 
     // see test/resources/hdfs_namenode_jmx.json for values
     Assert.assertEquals(13670605, resource.getPropertyValue(PropertyHelper.getPropertyId("metrics/rpc", "ReceivedBytes")));
@@ -326,7 +345,10 @@ public class JMXPropertyProviderTest {
 
     Assert.assertEquals(1, propertyProvider.populateResources(Collections.singleton(resource), request, null).size());
 
-    Assert.assertEquals(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"), streamProvider.getLastSpec());
+    List<String> expectedSpecs = new ArrayList<String>();
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"));
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState"));
+    Assert.assertEquals(expectedSpecs, streamProvider.getSpecs());
 
     // see test/resources/hdfs_namenode_jmx.json for values
     Assert.assertEquals(184320, resource.getPropertyValue("metrics/dfs/FSNamesystem/CapacityUsed"));
@@ -334,6 +356,75 @@ public class JMXPropertyProviderTest {
     Assert.assertNull(resource.getPropertyValue("metrics/rpc/ReceivedBytes"));
   }
 
+  public void testPopulateResources_HAState_request() throws Exception {
+    TestStreamProvider streamProvider = new TestStreamProvider();
+    TestJMXHostProvider hostProvider = new TestJMXHostProvider(false);
+    TestMetricHostProvider metricsHostProvider = new TestMetricHostProvider();
+
+    JMXPropertyProvider propertyProvider = metricPropertyProviderFactory.createJMXPropertyProvider(
+      jmxPropertyIdsWithHAState,
+      streamProvider,
+      hostProvider,
+      metricsHostProvider,
+      PropertyHelper.getPropertyId("HostRoles", "cluster_name"),
+      PropertyHelper.getPropertyId("HostRoles", "host_name"),
+      PropertyHelper.getPropertyId("HostRoles", "component_name"),
+      PropertyHelper.getPropertyId("HostRoles", "state"));
+
+    // namenode
+    Resource resource = new ResourceImpl(Resource.Type.HostComponent);
+    resource.setProperty(CLUSTER_NAME_PROPERTY_ID, "c1");
+    resource.setProperty(HOST_COMPONENT_HOST_NAME_PROPERTY_ID, "domu-12-31-39-0e-34-e1.compute-1.internal");
+    resource.setProperty(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID, "NAMENODE");
+    resource.setProperty(HOST_COMPONENT_STATE_PROPERTY_ID, "STARTED");
+
+    // request with an empty set should get all supported properties
+    // only ask for one property
+    Map<String, TemporalInfo> temporalInfoMap = new HashMap<String, TemporalInfo>();
+    Request request = PropertyHelper.getReadRequest(Collections.singleton("metrics/dfs/FSNamesystem"), temporalInfoMap);
+
+    Assert.assertEquals(1, propertyProvider.populateResources(Collections.singleton(resource), request, null).size());
+
+    List<String> expectedSpecs = new ArrayList<String>();
+    expectedSpecs.add(propertyProvider.getSpec("http","domu-12-31-39-0e-34-e1.compute-1.internal","50070","/jmx"));
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState"));
+
+    Assert.assertEquals(expectedSpecs, streamProvider.getSpecs());
+
+    // see test/resources/hdfs_namenode_jmx.json for values
+    Assert.assertEquals(184320, resource.getPropertyValue("metrics/dfs/FSNamesystem/CapacityUsed"));
+    Assert.assertEquals(21, resource.getPropertyValue("metrics/dfs/FSNamesystem/UnderReplicatedBlocks"));
+    Assert.assertEquals("customState", resource.getPropertyValue("metrics/dfs/FSNamesystem/HAState"));
+    Assert.assertNull(resource.getPropertyValue("metrics/rpc/ReceivedBytes"));
+
+    // namenode
+    resource = new ResourceImpl(Resource.Type.HostComponent);
+    resource.setProperty(CLUSTER_NAME_PROPERTY_ID, "c1");
+    resource.setProperty(HOST_COMPONENT_HOST_NAME_PROPERTY_ID, "domu-12-31-39-0e-34-e1.compute-1.internal");
+    resource.setProperty(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID, "NAMENODE");
+    resource.setProperty(HOST_COMPONENT_STATE_PROPERTY_ID, "STARTED");
+
+    streamProvider.getSpecs().clear();
+
+    // request with an empty set should get all supported properties
+    // only ask for one property
+    temporalInfoMap = new HashMap<String, TemporalInfo>();
+    request = PropertyHelper.getReadRequest(Collections.singleton("metrics/dfs/FSNamesystem/CapacityUsed"), temporalInfoMap);
+
+    Assert.assertEquals(1, propertyProvider.populateResources(Collections.singleton(resource), request, null).size());
+
+    // HAState isn't requested. It shouldn't be retrieved.
+    expectedSpecs.clear();
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"));
+    Assert.assertEquals(expectedSpecs, streamProvider.getSpecs());
+
+    // see test/resources/hdfs_namenode_jmx.json for values
+    Assert.assertEquals(184320, resource.getPropertyValue("metrics/dfs/FSNamesystem/CapacityUsed"));
+    Assert.assertNull(resource.getPropertyValue("metrics/dfs/FSNamesystem/HAState"));
+    Assert.assertNull(resource.getPropertyValue("metrics/rpc/ReceivedBytes"));
+
+  }
+
   public void testPopulateResourcesWithUnknownPort() throws Exception {
     TestStreamProvider streamProvider = new TestStreamProvider();
     TestJMXHostProvider hostProvider = new TestJMXHostProvider(true);
@@ -360,7 +451,10 @@ public class JMXPropertyProviderTest {
 
     Assert.assertEquals(1, propertyProvider.populateResources(Collections.singleton(resource), request, null).size());
 
-    Assert.assertEquals(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"), streamProvider.getLastSpec());
+    List<String> expectedSpecs = new ArrayList<String>();
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"));
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState"));
+    Assert.assertEquals(expectedSpecs, streamProvider.getSpecs());
 
     // see test/resources/hdfs_namenode_jmx.json for values
     Assert.assertEquals(13670605, resource.getPropertyValue(PropertyHelper.getPropertyId("metrics/rpc", "ReceivedBytes")));

http://git-wip-us.apache.org/repos/asf/ambari/blob/35ff7d79/ambari-server/src/test/resources/hdfs_namenode_jmx_ha_only.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/hdfs_namenode_jmx_ha_only.json b/ambari-server/src/test/resources/hdfs_namenode_jmx_ha_only.json
new file mode 100644
index 0000000..45b4f4b
--- /dev/null
+++ b/ambari-server/src/test/resources/hdfs_namenode_jmx_ha_only.json
@@ -0,0 +1,7 @@
+{
+  "beans" : [ {
+    "name" : "Hadoop:service=NameNode,name=FSNamesystem",
+    "modelerType" : "FSNamesystem",
+    "tag.HAState" : "customState"
+  } ]
+}
\ No newline at end of file


[2/2] ambari git commit: AMBARI-17063. Retrieve specific metrics when Ambari queries NameNode HA states (aonishuk)

Posted by ao...@apache.org.
AMBARI-17063. Retrieve specific metrics when Ambari queries NameNode HA states (aonishuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/7d9818ba
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/7d9818ba
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/7d9818ba

Branch: refs/heads/branch-2.4
Commit: 7d9818ba8e55f1165ce31d58b1bd4ea1100592cb
Parents: f516c79
Author: Andrew Onishuk <ao...@hortonworks.com>
Authored: Wed Jun 8 13:40:43 2016 +0300
Committer: Andrew Onishuk <ao...@hortonworks.com>
Committed: Wed Jun 8 13:40:43 2016 +0300

----------------------------------------------------------------------
 .../controller/jmx/JMXPropertyProvider.java     |  48 ++++++++-
 .../controller/jmx/TestStreamProvider.java      |  19 +++-
 .../metrics/JMXPropertyProviderTest.java        | 102 ++++++++++++++++++-
 .../resources/hdfs_namenode_jmx_ha_only.json    |   7 ++
 4 files changed, 167 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/7d9818ba/ambari-server/src/main/java/org/apache/ambari/server/controller/jmx/JMXPropertyProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/jmx/JMXPropertyProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/jmx/JMXPropertyProvider.java
index a315e5c..7665d7f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/jmx/JMXPropertyProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/jmx/JMXPropertyProvider.java
@@ -86,6 +86,19 @@ public class JMXPropertyProvider extends ThreadPoolEnabledPropertyProvider {
 
   private static final Map<String, String> DEFAULT_JMX_PORTS = new HashMap<String, String>();
 
+  /**
+   * When Ambari queries NameNode's HA state (among other metrics), it retrieves all metrics from "NN_URL:port/jmx".
+   * But some metrics may compete for the NameNode lock and a request to /jmx may take much time.
+   * <p>
+   * The properties from this map will be retrieved using a provided URL query.
+   * Even if JMX is locked and a request for all metrics is waiting (/jmx is unavailable),
+   * HAState will be updated via a separate JMX call.
+   * <p>
+   * Currently org.apache.hadoop.jmx.JMXJsonServlet can provide only one property per a request,
+   * each property from this list adds a request to JMX.
+   */
+  private static final Map<String, Map<String, String>> AD_HOC_PROPERTIES = new HashMap<>();
+
   static {
     DEFAULT_JMX_PORTS.put("NAMENODE",           "50070");
     DEFAULT_JMX_PORTS.put("DATANODE",           "50075");
@@ -96,6 +109,10 @@ public class JMXPropertyProvider extends ThreadPoolEnabledPropertyProvider {
     DEFAULT_JMX_PORTS.put("NODEMANAGER",         "8042");
     DEFAULT_JMX_PORTS.put("JOURNALNODE",         "8480");
     DEFAULT_JMX_PORTS.put("STORM_REST_API",      "8745");
+
+    AD_HOC_PROPERTIES.put("NAMENODE",
+        Collections.singletonMap("metrics/dfs/FSNamesystem/HAState",
+                                 "/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState"));
   }
 
   protected final static Logger LOG =
@@ -249,17 +266,40 @@ public class JMXPropertyProvider extends ThreadPoolEnabledPropertyProvider {
 
         // check to see if there is a cached value and use it if there is
         JMXMetricHolder jmxMetricHolder = metricsRetrievalService.getCachedJMXMetric(jmxUrl);
-        if (null == jmxMetricHolder) {
-          return resource;
-        }
 
         // if the ticket becomes invalid (timeout) then bail out
         if (!ticket.isValid()) {
           return resource;
         }
 
-        getHadoopMetricValue(jmxMetricHolder, ids, resource, request, ticket);
+        if (null != jmxMetricHolder) {
+          getHadoopMetricValue(jmxMetricHolder, ids, resource, request, ticket);
+        }
 
+        if (AD_HOC_PROPERTIES.containsKey(componentName)) {
+          for (String propertyId : ids) {
+            for (String adHocId : AD_HOC_PROPERTIES.get(componentName).keySet()) {
+              String queryURL = null;
+              // if all metrics from "metrics/dfs/FSNamesystem" were requested, retrieves HAState.
+              if (adHocId.equals(propertyId) || adHocId.startsWith(propertyId + '/')) {
+                queryURL = AD_HOC_PROPERTIES.get(componentName).get(adHocId);
+              }
+              if (queryURL != null) {
+                String adHocUrl = getSpec(protocol, hostName, port, queryURL);
+                metricsRetrievalService.submitJMXRequest(streamProvider, adHocUrl);
+                JMXMetricHolder adHocJMXMetricHolder = metricsRetrievalService.getCachedJMXMetric(adHocUrl);
+
+                // if the ticket becomes invalid (timeout) then bail out
+                if (!ticket.isValid()) {
+                  return resource;
+                }
+                if (null != adHocJMXMetricHolder) {
+                  getHadoopMetricValue(adHocJMXMetricHolder, Collections.singleton(propertyId), resource, request, ticket);
+                }
+              }
+            }
+          }
+        }
       } catch (IOException e) {
         AmbariException detailedException = new AmbariException(String.format(
             "Unable to get JMX metrics from the host %s for the component %s. Spec: %s", hostName,

http://git-wip-us.apache.org/repos/asf/ambari/blob/7d9818ba/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java
index 8819991..f29dc6f 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java
@@ -24,7 +24,9 @@ import org.apache.ambari.server.controller.utilities.StreamProvider;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 public class TestStreamProvider extends URLStreamProvider {
@@ -43,12 +45,15 @@ public class TestStreamProvider extends URLStreamProvider {
     FILE_MAPPING.put("8745",  "storm_rest_api_jmx.json");
   }
 
+  private static String NN_HASTATE_ONLY_JMX = "hdfs_namenode_jmx_ha_only.json";
+
   /**
    * Delay to simulate response time.
    */
   protected final long delay;
 
   private String lastSpec;
+  private List<String> specs = new ArrayList<>();
 
   private boolean isLastSpecUpdated;
 
@@ -64,11 +69,19 @@ public class TestStreamProvider extends URLStreamProvider {
 
   @Override
   public InputStream readFrom(String spec) throws IOException {
+    specs.add(spec);
     if (!isLastSpecUpdated)
       lastSpec = spec;
     
     isLastSpecUpdated = false;
-    String filename = FILE_MAPPING.get(getPort(spec));
+
+    String filename = null;
+    if (spec.endsWith(":50070/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState")) {
+      filename = NN_HASTATE_ONLY_JMX;
+    } else {
+      filename = FILE_MAPPING.get(getPort(spec));
+    }
+
     if (filename == null) {
       throw new IOException("Can't find JMX source for " + spec);
     }
@@ -87,6 +100,10 @@ public class TestStreamProvider extends URLStreamProvider {
     return lastSpec;
   }
 
+  public List<String> getSpecs() {
+    return specs;
+  }
+
   private String getPort(String spec) {
     int colonIndex = spec.indexOf(":", 5);
     int slashIndex = spec.indexOf("/", colonIndex);

http://git-wip-us.apache.org/repos/asf/ambari/blob/7d9818ba/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/JMXPropertyProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/JMXPropertyProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/JMXPropertyProviderTest.java
index 4adea20..80d7438 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/JMXPropertyProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/JMXPropertyProviderTest.java
@@ -24,9 +24,11 @@ import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 
 import java.lang.reflect.Field;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -34,6 +36,7 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.AmbariServer;
+import org.apache.ambari.server.controller.internal.PropertyInfo;
 import org.apache.ambari.server.controller.internal.ResourceImpl;
 import org.apache.ambari.server.controller.jmx.JMXHostProvider;
 import org.apache.ambari.server.controller.jmx.JMXPropertyProvider;
@@ -72,6 +75,15 @@ public class JMXPropertyProviderTest {
   protected static final String HOST_COMPONENT_STATE_PROPERTY_ID = PropertyHelper.getPropertyId("HostRoles", "state");
 
   public static final int NUMBER_OF_RESOURCES = 400;
+
+  public static final Map<String, Map<String, PropertyInfo>> jmxPropertyIds = PropertyHelper.getJMXPropertyIds(Resource.Type.HostComponent);
+  public static final Map<String, Map<String, PropertyInfo>> jmxPropertyIdsWithHAState;
+
+  static {
+    jmxPropertyIdsWithHAState = new HashMap<>(jmxPropertyIds);
+    jmxPropertyIdsWithHAState.get("NAMENODE").put("metrics/dfs/FSNamesystem/HAState", new PropertyInfo("Hadoop:service=NameNode,name=FSNamesystem.tag#HAState", false, true));
+  }
+
   private static MetricPropertyProviderFactory metricPropertyProviderFactory;
 
   @BeforeClass
@@ -126,6 +138,7 @@ public class JMXPropertyProviderTest {
     testPopulateResourcesUnhealthyResource();
     testPopulateResourcesMany();
     testPopulateResourcesTimeout();
+    testPopulateResources_HAState_request();
   }
 
   @Test
@@ -139,6 +152,7 @@ public class JMXPropertyProviderTest {
     testPopulateResourcesUnhealthyResource();
     testPopulateResourcesMany();
     testPopulateResourcesTimeout();
+    testPopulateResources_HAState_request();
   }
 
   @Test
@@ -152,6 +166,7 @@ public class JMXPropertyProviderTest {
     testPopulateResourcesUnhealthyResource();
     testPopulateResourcesMany();
     testPopulateResourcesTimeout();
+    testPopulateResources_HAState_request();
   }
 
   @Test(expected = AuthorizationException.class)
@@ -167,6 +182,7 @@ public class JMXPropertyProviderTest {
     testPopulateResourcesUnhealthyResource();
     testPopulateResourcesMany();
     testPopulateResourcesTimeout();
+    testPopulateResources_HAState_request();
   }
 
   public void testPopulateResources() throws Exception {
@@ -174,7 +190,7 @@ public class JMXPropertyProviderTest {
     TestJMXHostProvider hostProvider = new TestJMXHostProvider(false);
     TestMetricHostProvider metricsHostProvider = new TestMetricHostProvider();
     JMXPropertyProvider propertyProvider = metricPropertyProviderFactory.createJMXPropertyProvider(
-      PropertyHelper.getJMXPropertyIds(Resource.Type.HostComponent),
+      jmxPropertyIdsWithHAState,
       streamProvider,
       hostProvider,
       metricsHostProvider,
@@ -194,7 +210,10 @@ public class JMXPropertyProviderTest {
     Request request = PropertyHelper.getReadRequest(Collections.<String>emptySet());
     Assert.assertEquals(1, propertyProvider.populateResources(Collections.singleton(resource), request, null).size());
 
-    Assert.assertEquals(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"), streamProvider.getLastSpec());
+    List<String> expectedSpecs = new ArrayList<String>();
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"));
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState"));
+    Assert.assertEquals(expectedSpecs, streamProvider.getSpecs());
 
     // see test/resources/hdfs_namenode_jmx.json for values
     Assert.assertEquals(13670605, resource.getPropertyValue(PropertyHelper.getPropertyId("metrics/rpc", "ReceivedBytes")));
@@ -326,7 +345,10 @@ public class JMXPropertyProviderTest {
 
     Assert.assertEquals(1, propertyProvider.populateResources(Collections.singleton(resource), request, null).size());
 
-    Assert.assertEquals(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"), streamProvider.getLastSpec());
+    List<String> expectedSpecs = new ArrayList<String>();
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"));
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState"));
+    Assert.assertEquals(expectedSpecs, streamProvider.getSpecs());
 
     // see test/resources/hdfs_namenode_jmx.json for values
     Assert.assertEquals(184320, resource.getPropertyValue("metrics/dfs/FSNamesystem/CapacityUsed"));
@@ -334,6 +356,75 @@ public class JMXPropertyProviderTest {
     Assert.assertNull(resource.getPropertyValue("metrics/rpc/ReceivedBytes"));
   }
 
+  public void testPopulateResources_HAState_request() throws Exception {
+    TestStreamProvider streamProvider = new TestStreamProvider();
+    TestJMXHostProvider hostProvider = new TestJMXHostProvider(false);
+    TestMetricHostProvider metricsHostProvider = new TestMetricHostProvider();
+
+    JMXPropertyProvider propertyProvider = metricPropertyProviderFactory.createJMXPropertyProvider(
+      jmxPropertyIdsWithHAState,
+      streamProvider,
+      hostProvider,
+      metricsHostProvider,
+      PropertyHelper.getPropertyId("HostRoles", "cluster_name"),
+      PropertyHelper.getPropertyId("HostRoles", "host_name"),
+      PropertyHelper.getPropertyId("HostRoles", "component_name"),
+      PropertyHelper.getPropertyId("HostRoles", "state"));
+
+    // namenode
+    Resource resource = new ResourceImpl(Resource.Type.HostComponent);
+    resource.setProperty(CLUSTER_NAME_PROPERTY_ID, "c1");
+    resource.setProperty(HOST_COMPONENT_HOST_NAME_PROPERTY_ID, "domu-12-31-39-0e-34-e1.compute-1.internal");
+    resource.setProperty(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID, "NAMENODE");
+    resource.setProperty(HOST_COMPONENT_STATE_PROPERTY_ID, "STARTED");
+
+    // request with an empty set should get all supported properties
+    // only ask for one property
+    Map<String, TemporalInfo> temporalInfoMap = new HashMap<String, TemporalInfo>();
+    Request request = PropertyHelper.getReadRequest(Collections.singleton("metrics/dfs/FSNamesystem"), temporalInfoMap);
+
+    Assert.assertEquals(1, propertyProvider.populateResources(Collections.singleton(resource), request, null).size());
+
+    List<String> expectedSpecs = new ArrayList<String>();
+    expectedSpecs.add(propertyProvider.getSpec("http","domu-12-31-39-0e-34-e1.compute-1.internal","50070","/jmx"));
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState"));
+
+    Assert.assertEquals(expectedSpecs, streamProvider.getSpecs());
+
+    // see test/resources/hdfs_namenode_jmx.json for values
+    Assert.assertEquals(184320, resource.getPropertyValue("metrics/dfs/FSNamesystem/CapacityUsed"));
+    Assert.assertEquals(21, resource.getPropertyValue("metrics/dfs/FSNamesystem/UnderReplicatedBlocks"));
+    Assert.assertEquals("customState", resource.getPropertyValue("metrics/dfs/FSNamesystem/HAState"));
+    Assert.assertNull(resource.getPropertyValue("metrics/rpc/ReceivedBytes"));
+
+    // namenode
+    resource = new ResourceImpl(Resource.Type.HostComponent);
+    resource.setProperty(CLUSTER_NAME_PROPERTY_ID, "c1");
+    resource.setProperty(HOST_COMPONENT_HOST_NAME_PROPERTY_ID, "domu-12-31-39-0e-34-e1.compute-1.internal");
+    resource.setProperty(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID, "NAMENODE");
+    resource.setProperty(HOST_COMPONENT_STATE_PROPERTY_ID, "STARTED");
+
+    streamProvider.getSpecs().clear();
+
+    // request with an empty set should get all supported properties
+    // only ask for one property
+    temporalInfoMap = new HashMap<String, TemporalInfo>();
+    request = PropertyHelper.getReadRequest(Collections.singleton("metrics/dfs/FSNamesystem/CapacityUsed"), temporalInfoMap);
+
+    Assert.assertEquals(1, propertyProvider.populateResources(Collections.singleton(resource), request, null).size());
+
+    // HAState isn't requested. It shouldn't be retrieved.
+    expectedSpecs.clear();
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"));
+    Assert.assertEquals(expectedSpecs, streamProvider.getSpecs());
+
+    // see test/resources/hdfs_namenode_jmx.json for values
+    Assert.assertEquals(184320, resource.getPropertyValue("metrics/dfs/FSNamesystem/CapacityUsed"));
+    Assert.assertNull(resource.getPropertyValue("metrics/dfs/FSNamesystem/HAState"));
+    Assert.assertNull(resource.getPropertyValue("metrics/rpc/ReceivedBytes"));
+
+  }
+
   public void testPopulateResourcesWithUnknownPort() throws Exception {
     TestStreamProvider streamProvider = new TestStreamProvider();
     TestJMXHostProvider hostProvider = new TestJMXHostProvider(true);
@@ -360,7 +451,10 @@ public class JMXPropertyProviderTest {
 
     Assert.assertEquals(1, propertyProvider.populateResources(Collections.singleton(resource), request, null).size());
 
-    Assert.assertEquals(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"), streamProvider.getLastSpec());
+    List<String> expectedSpecs = new ArrayList<String>();
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx"));
+    expectedSpecs.add(propertyProvider.getSpec("http", "domu-12-31-39-0e-34-e1.compute-1.internal", "50070", "/jmx?get=Hadoop:service=NameNode,name=FSNamesystem::tag.HAState"));
+    Assert.assertEquals(expectedSpecs, streamProvider.getSpecs());
 
     // see test/resources/hdfs_namenode_jmx.json for values
     Assert.assertEquals(13670605, resource.getPropertyValue(PropertyHelper.getPropertyId("metrics/rpc", "ReceivedBytes")));

http://git-wip-us.apache.org/repos/asf/ambari/blob/7d9818ba/ambari-server/src/test/resources/hdfs_namenode_jmx_ha_only.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/hdfs_namenode_jmx_ha_only.json b/ambari-server/src/test/resources/hdfs_namenode_jmx_ha_only.json
new file mode 100644
index 0000000..45b4f4b
--- /dev/null
+++ b/ambari-server/src/test/resources/hdfs_namenode_jmx_ha_only.json
@@ -0,0 +1,7 @@
+{
+  "beans" : [ {
+    "name" : "Hadoop:service=NameNode,name=FSNamesystem",
+    "modelerType" : "FSNamesystem",
+    "tag.HAState" : "customState"
+  } ]
+}
\ No newline at end of file