You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by db...@apache.org on 2016/05/11 09:15:43 UTC

[1/4] ambari git commit: AMBARI-16274. Views: Simplify View Configuration for Remote Ambari Managed Cluster. (Gaurav Nagar via dipayanb)

Repository: ambari
Updated Branches:
  refs/heads/trunk bc82a6eca -> c150f0ded


http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/hdfs/AuthConfigurationBuilder.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/hdfs/AuthConfigurationBuilder.java b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/hdfs/AuthConfigurationBuilder.java
index c8ca6cd..4911455 100644
--- a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/hdfs/AuthConfigurationBuilder.java
+++ b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/hdfs/AuthConfigurationBuilder.java
@@ -54,13 +54,11 @@ public class AuthConfigurationBuilder {
     String auth;
     auth = context.getProperties().get("webhdfs.auth");
 
-    if (auth == null || auth.isEmpty()) {
-      try {
-        auth = getConfigurationFromAmbari();
-      } catch (NoClusterAssociatedException e) {
-        auth = "auth=SIMPLE";
-        LOG.warn(String.format("HDFS090 Authentication parameters could not be determined. %s assumed.", auth));
-      }
+    if ((auth == null || auth.isEmpty()) && context.getCluster() != null) {
+      auth = getConfigurationFromAmbari();
+    } else  {
+      auth = "auth=SIMPLE";
+      LOG.warn(String.format("HDFS090 Authentication parameters could not be determined. %s assumed.", auth));
     }
 
     parseAuthString(auth);
@@ -81,7 +79,7 @@ public class AuthConfigurationBuilder {
    * Determine configuration from Ambari.
    */
   private String getConfigurationFromAmbari() throws NoClusterAssociatedException {
-    String authMethod = ambariApi.getCluster().getConfigurationValue(
+    String authMethod = context.getCluster().getConfigurationValue(
         "core-site", "hadoop.security.authentication");
     return String.format("auth=%s", authMethod);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/hdfs/ConfigurationBuilder.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/hdfs/ConfigurationBuilder.java b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/hdfs/ConfigurationBuilder.java
index 4518ac6..8b45cd6 100644
--- a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/hdfs/ConfigurationBuilder.java
+++ b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/hdfs/ConfigurationBuilder.java
@@ -121,9 +121,10 @@ public class ConfigurationBuilder {
 
   private String getProperty(String type, String key, String instanceProperty) {
     String value;
-    try {
-      value = ambariApi.getCluster().getConfigurationValue(type, key);
-    } catch (NoClusterAssociatedException e) {
+
+    if(context.getCluster() != null) {
+      value = context.getCluster().getConfigurationValue(type, key);
+    } else {
       value = context.getProperties().get(instanceProperty);
     }
     return value;
@@ -131,15 +132,16 @@ public class ConfigurationBuilder {
 
   private void copyPropertyIfExists(String type, String key) {
     String value;
-    try {
-      value = ambariApi.getCluster().getConfigurationValue(type, key);
+
+    if(context.getCluster() != null) {
+      value = context.getCluster().getConfigurationValue(type, key);
       if (value != null) {
         conf.set(key, value);
         LOG.debug("set " + key + " = " + value);
       } else {
         LOG.debug("No such property " + type + "/" + key);
       }
-    } catch (NoClusterAssociatedException e) {
+    } else {
       LOG.debug("No such property " + type + "/" + key);
     }
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/RemoteClusterTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/RemoteClusterTest.java b/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/RemoteClusterTest.java
deleted file mode 100644
index ec4df7d..0000000
--- a/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/RemoteClusterTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/**
- * 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.ambari.view.utils.ambari;
-
-import org.apache.ambari.view.URLStreamProvider;
-import org.apache.ambari.view.ViewContext;
-import org.apache.ambari.view.cluster.Cluster;
-import org.apache.commons.collections4.map.PassiveExpiringMap;
-import org.easymock.IAnswer;
-import org.json.simple.JSONObject;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.easymock.EasyMock.*;
-import static org.junit.Assert.*;
-
-public class RemoteClusterTest {
-  public static final String AMBARI_CLUSTER_REST_URL = "http://example.com:8080/api/v1/clusters/c1";
-
-  @Rule
-  public ExpectedException thrown= ExpectedException.none();
-
-  @Test
-  public void testGetRemoteClusterThatIsNotPresent() throws Exception {
-    ViewContext viewContext = createNiceMock(ViewContext.class);
-    Map<String, String> instanceProperties = new HashMap<String, String>();
-    expect(viewContext.getProperties()).andReturn(instanceProperties).anyTimes();
-    replay(viewContext);
-
-    AmbariApi ambariApi = new AmbariApi(viewContext);
-    Cluster cluster = ambariApi.getRemoteCluster();
-    assertNull(cluster);
-  }
-
-  @Test
-  public void testGetRemoteClusterNoCredentials() throws Exception {
-    ViewContext viewContext = createNiceMock(ViewContext.class);
-    Map<String, String> instanceProperties = new HashMap<String, String>();
-    instanceProperties.put(AmbariApi.AMBARI_SERVER_URL_INSTANCE_PROPERTY,
-        AMBARI_CLUSTER_REST_URL);
-    expect(viewContext.getProperties()).andReturn(instanceProperties).anyTimes();
-    replay(viewContext);
-
-    thrown.expect(AmbariApiException.class);
-    AmbariApi ambariApi = new AmbariApi(viewContext);
-    Cluster cluster = ambariApi.getRemoteCluster();
-  }
-
-  @Test
-  public void testGetRemoteClusterThatIsPresent() throws Exception {
-    ViewContext viewContext = createNiceMock(ViewContext.class);
-    Map<String, String> instanceProperties = new HashMap<String, String>();
-    instanceProperties.put(AmbariApi.AMBARI_SERVER_URL_INSTANCE_PROPERTY,
-        AMBARI_CLUSTER_REST_URL);
-    instanceProperties.put(AmbariApi.AMBARI_SERVER_USERNAME_INSTANCE_PROPERTY, "admin");
-    instanceProperties.put(AmbariApi.AMBARI_SERVER_PASSWORD_INSTANCE_PROPERTY, "admin");
-    expect(viewContext.getProperties()).andReturn(instanceProperties).anyTimes();
-    replay(viewContext);
-
-    AmbariApi ambariApi = new AmbariApi(viewContext);
-    Cluster cluster = ambariApi.getRemoteCluster();
-    assertNotNull(cluster);
-    assertEquals(cluster.getName(), "c1");
-  }
-
-  @Test
-  public void testGetConfigurationValue() throws Exception {
-    URLStreamProvider urlStreamProvider = createNiceMock(URLStreamProvider.class);
-
-    final String desiredConfigsString = "{\"Clusters\": {\"desired_configs\": {\"test-site\": {\"tag\": \"TAG\"}}}}";
-    final String configurationString = "{\"items\": [{\"properties\": {\"test.property.name\": \"test property value\"}}]}";
-    final int[] desiredConfigPolls = {0};
-    final int[] testConfigPolls = {0};
-
-    expect(urlStreamProvider.readFrom(eq(AMBARI_CLUSTER_REST_URL + "?fields=services/ServiceInfo,hosts,Clusters"),
-        eq("GET"), (String) isNull(), (Map<String, String>) anyObject())).andAnswer(new IAnswer<InputStream>() {
-      @Override
-      public InputStream answer() throws Throwable {
-        desiredConfigPolls[0] += 1;
-        return new ByteArrayInputStream(desiredConfigsString.getBytes());
-      }
-    }).anyTimes();
-
-    expect(urlStreamProvider.readFrom(eq(AMBARI_CLUSTER_REST_URL + "/configurations?(type=test-site&tag=TAG)"),
-        eq("GET"), (String)isNull(), (Map<String, String>) anyObject())).andAnswer(new IAnswer<InputStream>() {
-      @Override
-      public InputStream answer() throws Throwable {
-        testConfigPolls[0] += 1;
-        return new ByteArrayInputStream(configurationString.getBytes());
-      }
-    }).anyTimes();
-
-    replay(urlStreamProvider);
-
-    RemoteCluster cluster = new RemoteCluster(AMBARI_CLUSTER_REST_URL, urlStreamProvider);
-    PassiveExpiringMap<String, JSONObject> cache = new PassiveExpiringMap<String, JSONObject>(10000L);
-    cluster.configurationCache = cache;
-
-    String value = cluster.getConfigurationValue("test-site", "test.property.name");
-    assertEquals(value, "test property value");
-    assertEquals(desiredConfigPolls[0], 1);
-    assertEquals(testConfigPolls[0], 1);
-
-    value = cluster.getConfigurationValue("test-site", "test.property.name");
-    assertEquals(value, "test property value");
-    assertEquals(desiredConfigPolls[0], 1);  // cache hit
-    assertEquals(testConfigPolls[0], 1);
-
-    cache.clear();
-    value = cluster.getConfigurationValue("test-site", "test.property.name");
-    assertEquals(value, "test property value");
-    assertEquals(desiredConfigPolls[0], 2);
-    assertEquals(testConfigPolls[0], 2);
-  }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/ServicesTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/ServicesTest.java b/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/ServicesTest.java
index 455ca20..c7c0990 100644
--- a/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/ServicesTest.java
+++ b/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/ServicesTest.java
@@ -100,7 +100,7 @@ public class ServicesTest extends EasyMockSupport {
 
     expect(ambariApi.isClusterAssociated()).andReturn(true).anyTimes();
     setClusterExpectation(cluster, "HTTP_ONLY");
-    expect(ambariApi.getCluster()).andReturn(cluster).anyTimes();
+    expect(viewContext.getCluster()).andReturn(cluster).anyTimes();
     replayAll();
 
     assertEquals(HTTP_RM_URL1, services.getTimelineServerUrl());
@@ -255,7 +255,7 @@ public class ServicesTest extends EasyMockSupport {
 
     expect(ambariApi.isClusterAssociated()).andReturn(true).anyTimes();
     setClusterExpectation(cluster, "HTTP_ONLY");
-    expect(ambariApi.getCluster()).andReturn(cluster).anyTimes();
+    expect(viewContext.getCluster()).andReturn(cluster).anyTimes();
 
     replayAll();
 
@@ -283,7 +283,7 @@ public class ServicesTest extends EasyMockSupport {
 
     expect(ambariApi.isClusterAssociated()).andReturn(true).anyTimes();
     setClusterExpectationWithEmptyWebappConfig(cluster, "HTTP_ONLY");
-    expect(ambariApi.getCluster()).andReturn(cluster).anyTimes();
+    expect(viewContext.getCluster()).andReturn(cluster).anyTimes();
 
     Services services = new Services(ambariApi, viewContext);
 
@@ -307,7 +307,7 @@ public class ServicesTest extends EasyMockSupport {
 
     expect(ambariApi.isClusterAssociated()).andReturn(true).anyTimes();
     setClusterExpectationInHAWithEmptyWebappConfig(cluster, "HTTP_ONLY");
-    expect(ambariApi.getCluster()).andReturn(cluster).anyTimes();
+    expect(viewContext.getCluster()).andReturn(cluster).anyTimes();
 
     Services services = new Services(ambariApi, viewContext);
 
@@ -342,7 +342,7 @@ public class ServicesTest extends EasyMockSupport {
 
     expect(ambariApi.isClusterAssociated()).andReturn(true).anyTimes();
     setClusterExpectationInHA(cluster, "HTTP_ONLY");
-    expect(ambariApi.getCluster()).andReturn(cluster).anyTimes();
+    expect(viewContext.getCluster()).andReturn(cluster).anyTimes();
     expect(viewContext.getURLStreamProvider()).andReturn(urlStreamProvider).anyTimes();
     expect(urlStreamProvider.readFrom(eq(HTTP_RM_URL1 + RM_INFO_API_ENDPOINT), eq("GET"), anyString(), EasyMock.<Map<String, String>>anyObject())).andReturn(inputStream);
 
@@ -385,7 +385,7 @@ public class ServicesTest extends EasyMockSupport {
 
     expect(ambariApi.isClusterAssociated()).andReturn(true).anyTimes();
     setClusterExpectationWithEmptyWebappConfig(cluster, "HTTP_ONLY");
-    expect(ambariApi.getCluster()).andReturn(cluster).anyTimes();
+    expect(viewContext.getCluster()).andReturn(cluster).anyTimes();
 
     Services services = new Services(ambariApi, viewContext);
 
@@ -432,4 +432,4 @@ public class ServicesTest extends EasyMockSupport {
     expect(viewContextMock.getProperties()).andReturn(map);
     return viewContextMock;
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuthTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuthTest.java b/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuthTest.java
deleted file mode 100644
index 27e98f2..0000000
--- a/contrib/views/utils/src/test/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuthTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * 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.ambari.view.utils.ambari;
-
-import org.apache.ambari.view.URLStreamProvider;
-import org.easymock.EasyMock;
-import org.easymock.IArgumentMatcher;
-
-import org.junit.Test;
-
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.easymock.EasyMock.*;
-
-public class URLStreamProviderBasicAuthTest {
-
-  @Test
-  public void testReadFrom() throws Exception {
-    URLStreamProvider urlStreamProvider = createNiceMock(URLStreamProvider.class);
-    expect(urlStreamProvider.readFrom(anyString(), anyString(), anyString(), HeadersMatcher.mapContainsAuthHeader())).andReturn(null);
-    URLStreamProviderBasicAuth urlStreamProviderBasicAuth =
-        new URLStreamProviderBasicAuth(urlStreamProvider, "user", "pass");
-
-    replay(urlStreamProvider);
-
-    urlStreamProviderBasicAuth.readFrom("http://example.com", "GET",
-        (String) null, null);
-    urlStreamProviderBasicAuth.readFrom("http://example.com", "GET",
-        (String)null, new HashMap<String, String>());
-  }
-
-  @Test
-  public void testReadFrom1() throws Exception {
-    URLStreamProvider urlStreamProvider = createNiceMock(URLStreamProvider.class);
-    expect(urlStreamProvider.readFrom(anyString(), anyString(), (InputStream)anyObject(),
-        HeadersMatcher.mapContainsAuthHeader())).andReturn(null);
-    URLStreamProviderBasicAuth urlStreamProviderBasicAuth =
-        new URLStreamProviderBasicAuth(urlStreamProvider, "user", "pass");
-
-    replay(urlStreamProvider);
-
-    urlStreamProviderBasicAuth.readFrom("http://example.com", "GET",
-        (InputStream) null, null);
-    urlStreamProviderBasicAuth.readFrom("http://example.com", "GET",
-        (InputStream)null, new HashMap<String, String>());
-  }
-
-  @Test
-  public void testReadAs() throws Exception {
-    URLStreamProvider urlStreamProvider = createNiceMock(URLStreamProvider.class);
-    expect(urlStreamProvider.readAs(anyString(), anyString(), anyString(),
-        HeadersMatcher.mapContainsAuthHeader(), anyString())).andReturn(null);
-    URLStreamProviderBasicAuth urlStreamProviderBasicAuth =
-        new URLStreamProviderBasicAuth(urlStreamProvider, "user", "pass");
-
-    replay(urlStreamProvider);
-
-    urlStreamProviderBasicAuth.readAs("http://example.com", "GET",
-        (String) null, null, "admin");
-    urlStreamProviderBasicAuth.readAs("http://example.com", "GET",
-        (String) null, new HashMap<String, String>(), "admin");
-  }
-
-  @Test
-  public void testReadAs1() throws Exception {
-    URLStreamProvider urlStreamProvider = createNiceMock(URLStreamProvider.class);
-    expect(urlStreamProvider.readAs(anyString(), anyString(), (InputStream) anyObject(),
-        HeadersMatcher.mapContainsAuthHeader(), anyString())).andReturn(null);
-    URLStreamProviderBasicAuth urlStreamProviderBasicAuth =
-        new URLStreamProviderBasicAuth(urlStreamProvider, "user", "pass");
-
-    replay(urlStreamProvider);
-
-    urlStreamProviderBasicAuth.readAs("http://example.com", "GET",
-        (InputStream) null, null, "admin");
-    urlStreamProviderBasicAuth.readAs("http://example.com", "GET",
-        (InputStream) null, new HashMap<String, String>(), "admin");
-  }
-
-  @Test
-  public void testReadAsCurrent() throws Exception {
-    URLStreamProvider urlStreamProvider = createNiceMock(URLStreamProvider.class);
-    expect(urlStreamProvider.readAsCurrent(anyString(), anyString(), anyString(),
-        HeadersMatcher.mapContainsAuthHeader())).andReturn(null);
-    URLStreamProviderBasicAuth urlStreamProviderBasicAuth =
-        new URLStreamProviderBasicAuth(urlStreamProvider, "user", "pass");
-
-    replay(urlStreamProvider);
-
-    urlStreamProviderBasicAuth.readAsCurrent("http://example.com", "GET",
-        (String) null, null);
-    urlStreamProviderBasicAuth.readAsCurrent("http://example.com", "GET",
-        (String) null, new HashMap<String, String>());
-  }
-
-  @Test
-  public void testReadAsCurrent1() throws Exception {
-    URLStreamProvider urlStreamProvider = createNiceMock(URLStreamProvider.class);
-    expect(urlStreamProvider.readAsCurrent(anyString(), anyString(), (InputStream) anyObject(),
-        HeadersMatcher.mapContainsAuthHeader())).andReturn(null);
-    URLStreamProviderBasicAuth urlStreamProviderBasicAuth =
-        new URLStreamProviderBasicAuth(urlStreamProvider, "user", "pass");
-
-    replay(urlStreamProvider);
-
-    urlStreamProviderBasicAuth.readAsCurrent("http://example.com", "GET",
-        (InputStream) null, null);
-    urlStreamProviderBasicAuth.readAsCurrent("http://example.com", "GET",
-        (InputStream)null, new HashMap<String, String>());
-  }
-
-
-  public static class HeadersMatcher implements IArgumentMatcher {
-
-    public static Map<String, String> mapContainsAuthHeader() {
-      EasyMock.reportMatcher(new HeadersMatcher());
-      return null;
-    }
-
-    public void appendTo(StringBuffer buffer) {
-      buffer.append("Authentication header matcher");
-    }
-
-    public boolean matches(Object headers) {
-      if (!(headers instanceof Map)) {
-        return false;
-      }
-
-      Map<String, String> headersMap = (Map<String, String>) headers;
-
-      if (!headersMap.containsKey("Authorization"))
-        return false;
-      String authHeader = headersMap.get("Authorization");
-
-      if (!authHeader.startsWith("Basic "))
-        return false;
-
-      return true;
-    }
-  }
-}
\ No newline at end of file


[2/4] ambari git commit: AMBARI-16274. Views: Simplify View Configuration for Remote Ambari Managed Cluster. (Gaurav Nagar via dipayanb)

Posted by db...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java
index bb6a93c..f63c962 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java
@@ -123,6 +123,12 @@ public class ViewConfig {
   private String dataVersion;
 
   /**
+   * Enable views to display cluster options in case of no cluster config properties.
+   */
+  @XmlElement(name="cluster-config-options")
+  private String clusterConfigOptions;
+
+  /**
    * The view class.
    */
   private Class<? extends ViewDataMigrator> dataMigratorClass = null;
@@ -450,4 +456,13 @@ public class ViewConfig {
   public List<PermissionConfig> getPermissions() {
     return permissions == null ? Collections.<PermissionConfig>emptyList() : permissions;
   }
+
+  /**
+   * Enables views to display cluster options in case of no cluster config properties
+   *
+   * @return clusterConfigOptions
+   */
+  public String getClusterConfigOptions() {
+    return clusterConfigOptions;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
index 415e06b..d77a0b3 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
@@ -567,6 +567,7 @@ CREATE TABLE viewinstance (
   xml_driven CHAR(1),
   alter_names SMALLINT NOT NULL DEFAULT 1,
   cluster_handle VARCHAR(255),
+  cluster_type VARCHAR(100) NOT NULL DEFAULT 'LOCAL_AMBARI',
   short_url BIGINT,
   CONSTRAINT PK_viewinstance PRIMARY KEY (view_instance_id),
   CONSTRAINT FK_instance_url_id FOREIGN KEY (short_url) REFERENCES viewurl(url_id),
@@ -601,7 +602,7 @@ CREATE TABLE viewparameter (
   label VARCHAR(255),
   placeholder VARCHAR(255),
   default_value VARCHAR(2000),
-  cluster_config VARCHAR(255),
+  cluster_config VARCHAR(255) ,
   required CHAR(1),
   masked CHAR(1),
   CONSTRAINT PK_viewparameter PRIMARY KEY (view_name, name),
@@ -778,6 +779,26 @@ CREATE TABLE setting (
   CONSTRAINT PK_setting PRIMARY KEY (id)
 );
 
+-- Remote Cluster table
+
+CREATE TABLE remoteambaricluster(
+  cluster_id BIGINT NOT NULL,
+  name VARCHAR(255) NOT NULL,
+  username VARCHAR(255) NOT NULL,
+  url VARCHAR(255) NOT NULL,
+  password VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_cluster PRIMARY KEY (cluster_id),
+  CONSTRAINT unq_remote_ambari_cluster UNIQUE (name));
+
+CREATE TABLE remoteambariclusterservice(
+  id BIGINT NOT NULL,
+  cluster_id BIGINT NOT NULL,
+  service_name VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_service PRIMARY KEY (id),
+  CONSTRAINT FK_remote_ambari_cluster_id FOREIGN KEY (cluster_id) REFERENCES remoteambaricluster(cluster_id)
+);
+
+-- Remote Cluster table ends
 
 -- upgrade tables
 CREATE TABLE upgrade (
@@ -1094,7 +1115,11 @@ INSERT INTO ambari_sequences (sequence_name, sequence_value)
   union all
   select 'blueprint_setting_id_seq', 0 FROM SYSIBM.SYSDUMMY1
   union all
-  select 'ambari_operation_history_id_seq', 0 FROM SYSIBM.SYSDUMMY1;
+  select 'ambari_operation_history_id_seq', 0 FROM SYSIBM.SYSDUMMY1
+  union all
+  select 'remote_cluster_id_seq', 0 FROM SYSIBM.SYSDUMMY1
+  union all
+  select 'remote_cluster_service_id_seq', 0 FROM SYSIBM.SYSDUMMY1;
 
 
 INSERT INTO adminresourcetype (resource_type_id, resource_type_name)

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index e3ce816..22413e0 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -575,6 +575,7 @@ CREATE TABLE viewinstance (
   xml_driven CHAR(1),
   alter_names TINYINT(1) NOT NULL DEFAULT 1,
   cluster_handle VARCHAR(255),
+  cluster_type VARCHAR(100) NOT NULL DEFAULT 'LOCAL_AMBARI',
   short_url BIGINT,
   CONSTRAINT PK_viewinstance PRIMARY KEY (view_instance_id),
   CONSTRAINT FK_instance_url_id FOREIGN KEY (short_url) REFERENCES viewurl(url_id),
@@ -785,6 +786,26 @@ CREATE TABLE setting (
   CONSTRAINT PK_setting PRIMARY KEY (id)
 );
 
+-- Remote Cluster table
+
+CREATE TABLE remoteambaricluster(
+  cluster_id BIGINT NOT NULL,
+  name VARCHAR(255) NOT NULL,
+  username VARCHAR(255) NOT NULL,
+  url VARCHAR(255) NOT NULL,
+  password VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_cluster PRIMARY KEY (cluster_id),
+  CONSTRAINT unq_remote_ambari_cluster UNIQUE (name));
+
+CREATE TABLE remoteambariclusterservice(
+  id BIGINT NOT NULL,
+  cluster_id BIGINT NOT NULL,
+  service_name VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_service PRIMARY KEY (id),
+  CONSTRAINT FK_remote_ambari_cluster_id FOREIGN KEY (cluster_id) REFERENCES remoteambaricluster(cluster_id)
+);
+
+-- Remote Cluster table ends
 
 -- upgrade tables
 CREATE TABLE upgrade (
@@ -1053,7 +1074,9 @@ INSERT INTO ambari_sequences(sequence_name, sequence_value) VALUES
   ('servicecomponentdesiredstate_id_seq', 0),
   ('servicecomponent_history_id_seq', 0),
   ('blueprint_setting_id_seq', 0),
-  ('ambari_operation_history_id_seq', 0);
+  ('ambari_operation_history_id_seq', 0),
+  ('remote_cluster_id_seq', 0),
+  ('remote_cluster_service_id_seq', 0),;
 
 INSERT INTO adminresourcetype (resource_type_id, resource_type_name) VALUES
   (1, 'AMBARI'),

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index cd88e8b..6000d24 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -566,6 +566,7 @@ CREATE TABLE viewinstance (
   xml_driven CHAR(1),
   alter_names NUMBER(1) DEFAULT 1 NOT NULL,
   cluster_handle VARCHAR(255),
+  cluster_type VARCHAR(100) DEFAULT 'LOCAL_AMBARI' NOT NULL,
   short_url NUMBER,
   CONSTRAINT PK_viewinstance PRIMARY KEY (view_instance_id),
   CONSTRAINT FK_instance_url_id FOREIGN KEY (short_url) REFERENCES viewurl(url_id),
@@ -775,6 +776,26 @@ CREATE TABLE setting (
   CONSTRAINT PK_setting PRIMARY KEY (id)
 );
 
+-- Remote Cluster table
+
+CREATE TABLE remoteambaricluster(
+  cluster_id BIGINT NOT NULL,
+  name VARCHAR(255) NOT NULL,
+  username VARCHAR(255) NOT NULL,
+  url VARCHAR(255) NOT NULL,
+  password VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_cluster PRIMARY KEY (cluster_id),
+  CONSTRAINT unq_remote_ambari_cluster UNIQUE (name));
+
+CREATE TABLE remoteambariclusterservice(
+  id BIGINT NOT NULL,
+  cluster_id BIGINT NOT NULL,
+  service_name VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_service PRIMARY KEY (id),
+  CONSTRAINT FK_remote_ambari_cluster_id FOREIGN KEY (cluster_id) REFERENCES remoteambaricluster(cluster_id)
+);
+
+-- Remote Cluster table ends
 
 -- upgrade tables
 CREATE TABLE upgrade (
@@ -1044,6 +1065,8 @@ INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('servicecomp
 INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('servicecomponent_history_id_seq', 0);
 INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('blueprint_setting_id_seq', 0);
 INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('ambari_operation_history_id_seq', 0);
+INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('remote_cluster_id_seq', 0);
+INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('remote_cluster_service_id_seq', 0);
 
 INSERT INTO metainfo("metainfo_key", "metainfo_value") values ('version', '${ambariSchemaVersion}');
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
index 108e33e..67bd38a 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -567,6 +567,7 @@ CREATE TABLE viewinstance (
   xml_driven CHAR(1),
   alter_names SMALLINT NOT NULL DEFAULT 1,
   cluster_handle VARCHAR(255),
+  cluster_type VARCHAR(100) NOT NULL DEFAULT 'LOCAL_AMBARI',
   short_url BIGINT,
   CONSTRAINT PK_viewinstance PRIMARY KEY (view_instance_id),
   CONSTRAINT FK_instance_url_id FOREIGN KEY (short_url) REFERENCES viewurl(url_id),
@@ -777,6 +778,26 @@ CREATE TABLE setting (
   update_timestamp BIGINT NOT NULL,
   CONSTRAINT PK_setting PRIMARY KEY (id));
 
+-- Remote Cluster table
+
+CREATE TABLE remoteambaricluster(
+  cluster_id BIGINT NOT NULL,
+  name VARCHAR(255) NOT NULL,
+  username VARCHAR(255) NOT NULL,
+  url VARCHAR(255) NOT NULL,
+  password VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_cluster PRIMARY KEY (cluster_id),
+  CONSTRAINT unq_remote_ambari_cluster UNIQUE (name));
+
+CREATE TABLE remoteambariclusterservice(
+  id BIGINT NOT NULL,
+  cluster_id BIGINT NOT NULL,
+  service_name VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_service PRIMARY KEY (id),
+  CONSTRAINT FK_remote_ambari_cluster_id FOREIGN KEY (cluster_id) REFERENCES remoteambaricluster(cluster_id)
+);
+
+-- Remote Cluster table ends
 
 -- upgrade tables
 CREATE TABLE upgrade (
@@ -1044,7 +1065,9 @@ INSERT INTO ambari_sequences (sequence_name, sequence_value) VALUES
   ('servicecomponentdesiredstate_id_seq', 0),
   ('servicecomponent_history_id_seq', 0),
   ('blueprint_setting_id_seq', 0),
-  ('ambari_operation_history_id_seq', 0);
+  ('ambari_operation_history_id_seq', 0),
+  ('remote_cluster_id_seq', 0),
+  ('remote_cluster_service_id_seq', 0);
 
 INSERT INTO adminresourcetype (resource_type_id, resource_type_name) VALUES
   (1, 'AMBARI'),

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
index 566da84..ea7ab7a 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
@@ -668,6 +668,7 @@ CREATE TABLE ambari.viewinstance (
   xml_driven CHAR(1),
   alter_names SMALLINT NOT NULL DEFAULT 1,
   cluster_handle VARCHAR(255),
+  cluster_type VARCHAR(100) NOT NULL DEFAULT 'LOCAL_AMBARI',
   short_url BIGINT,
   CONSTRAINT PK_viewinstance PRIMARY KEY (view_instance_id),
   CONSTRAINT FK_instance_url_id FOREIGN KEY (short_url) REFERENCES ambari.viewurl(url_id),
@@ -913,6 +914,30 @@ CREATE TABLE ambari.setting (
 );
 GRANT ALL PRIVILEGES ON TABLE ambari.setting TO :username;
 
+-- Remote Cluster table
+
+CREATE TABLE ambari.remoteambaricluster(
+  cluster_id BIGINT NOT NULL,
+  name VARCHAR(255) NOT NULL,
+  username VARCHAR(255) NOT NULL,
+  url VARCHAR(255) NOT NULL,
+  password VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_cluster PRIMARY KEY (cluster_id),
+  CONSTRAINT unq_remote_ambari_cluster UNIQUE (name)
+);
+GRANT ALL PRIVILEGES ON TABLE ambari.remoteambaricluster TO :username;
+
+CREATE TABLE ambari.remoteambariclusterservice(
+  id BIGINT NOT NULL,
+  cluster_id BIGINT NOT NULL,
+  service_name VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_service PRIMARY KEY (id),
+  CONSTRAINT FK_remote_ambari_cluster_id FOREIGN KEY (cluster_id) REFERENCES ambari.remoteambaricluster(cluster_id)
+);
+GRANT ALL PRIVILEGES ON TABLE ambari.remoteambariclusterservice TO :username;
+
+-- Remote Cluster table ends
+
 -- upgrade tables
 CREATE TABLE ambari.upgrade (
   upgrade_id BIGINT NOT NULL,
@@ -1202,7 +1227,9 @@ INSERT INTO ambari.ambari_sequences (sequence_name, sequence_value) VALUES
   ('servicecomponentdesiredstate_id_seq', 0),
   ('servicecomponent_history_id_seq', 0),
   ('blueprint_setting_id_seq', 0),
-  ('ambari_operation_history_id_seq', 0);
+  ('ambari_operation_history_id_seq', 0),
+  ('remote_cluster_id_seq', 0),
+  ('remote_cluster_service_id_seq', 0);
 
 INSERT INTO ambari.adminresourcetype (resource_type_id, resource_type_name) VALUES
   (1, 'AMBARI'),

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
index bd5d1ae..1c3393e 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
@@ -564,6 +564,7 @@ CREATE TABLE viewinstance (
   xml_driven CHAR(1),
   alter_names BIT NOT NULL DEFAULT 1,
   cluster_handle VARCHAR(255),
+  cluster_type VARCHAR(100) NOT NULL DEFAULT 'LOCAL_AMBARI',
   short_url NUMERIC,
   CONSTRAINT PK_viewinstance PRIMARY KEY (view_instance_id),
   CONSTRAINT FK_instance_url_id FOREIGN KEY (short_url) REFERENCES viewurl(url_id),
@@ -774,6 +775,26 @@ CREATE TABLE setting (
   CONSTRAINT PK_setting PRIMARY KEY (id)
 );
 
+-- Remote Cluster table
+
+CREATE TABLE remoteambaricluster(
+  cluster_id BIGINT NOT NULL,
+  name VARCHAR(255) NOT NULL,
+  username VARCHAR(255) NOT NULL,
+  url VARCHAR(255) NOT NULL,
+  password VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_cluster PRIMARY KEY (cluster_id),
+  CONSTRAINT unq_remote_ambari_cluster UNIQUE (name));
+
+CREATE TABLE remoteambariclusterservice(
+  id BIGINT NOT NULL,
+  cluster_id BIGINT NOT NULL,
+  service_name VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_service PRIMARY KEY (id),
+  CONSTRAINT FK_remote_ambari_cluster_id FOREIGN KEY (cluster_id) REFERENCES remoteambaricluster(cluster_id)
+);
+
+-- Remote Cluster table ends
 
 -- upgrade tables
 CREATE TABLE upgrade (
@@ -1043,6 +1064,8 @@ INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('servicecomp
 INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('servicecomponent_history_id_seq', 0);
 INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('blueprint_setting_id_seq', 0);
 INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('ambari_operation_history_id_seq', 0);
+INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('remote_cluster_id_seq', 0);
+INSERT INTO ambari_sequences(sequence_name, sequence_value) values ('remote_cluster_service_id_seq', 0);
 
 insert into adminresourcetype (resource_type_id, resource_type_name)
   select 1, 'AMBARI'

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
index 20c706a..2245f77 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
@@ -574,6 +574,7 @@ CREATE TABLE viewinstance (
   xml_driven CHAR(1),
   alter_names BIT NOT NULL DEFAULT 1,
   cluster_handle VARCHAR(255),
+  cluster_type VARCHAR(100) NOT NULL DEFAULT 'LOCAL_AMBARI',
   short_url BIGINT,
   CONSTRAINT PK_viewinstance PRIMARY KEY CLUSTERED (view_instance_id),
   CONSTRAINT FK_instance_url_id FOREIGN KEY (short_url) REFERENCES viewurl(url_id),
@@ -795,6 +796,26 @@ CREATE TABLE setting (
   CONSTRAINT PK_setting PRIMARY KEY (id)
 );
 
+-- Remote Cluster table
+
+CREATE TABLE remoteambaricluster(
+  cluster_id BIGINT NOT NULL,
+  name VARCHAR(255) NOT NULL,
+  username VARCHAR(255) NOT NULL,
+  url VARCHAR(255) NOT NULL,
+  password VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_cluster PRIMARY KEY (cluster_id),
+  CONSTRAINT unq_remote_ambari_cluster UNIQUE (name));
+
+CREATE TABLE remoteambariclusterservice(
+  id BIGINT NOT NULL,
+  cluster_id BIGINT NOT NULL,
+  service_name VARCHAR(255) NOT NULL,
+  CONSTRAINT PK_remote_ambari_service PRIMARY KEY (id),
+  CONSTRAINT FK_remote_ambari_cluster_id FOREIGN KEY (cluster_id) REFERENCES remoteambaricluster(cluster_id)
+);
+
+-- Remote Cluster table ends
 
 -- upgrade tables
 CREATE TABLE upgrade (
@@ -1068,7 +1089,9 @@ BEGIN TRANSACTION
     ('servicecomponentdesiredstate_id_seq', 0),
     ('servicecomponent_history_id_seq', 0),
     ('blueprint_setting_id_seq', 0),
-    ('ambari_operation_history_id_seq', 0);
+    ('ambari_operation_history_id_seq', 0),
+    ('remote_cluster_id_seq', 0),
+    ('remote_cluster_service_id_seq', 0);
 
   insert into adminresourcetype (resource_type_id, resource_type_name)
   values

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/resources/META-INF/persistence.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/META-INF/persistence.xml b/ambari-server/src/main/resources/META-INF/persistence.xml
index ce563cb..5671dcf 100644
--- a/ambari-server/src/main/resources/META-INF/persistence.xml
+++ b/ambari-server/src/main/resources/META-INF/persistence.xml
@@ -93,6 +93,8 @@
     <class>org.apache.ambari.server.orm.entities.TopologyLogicalTaskEntity</class>
     <class>org.apache.ambari.server.orm.entities.SettingEntity</class>
     <class>org.apache.ambari.server.orm.entities.KerberosDescriptorEntity</class>
+    <class>org.apache.ambari.server.orm.entities.RemoteAmbariClusterEntity</class>
+    <class>org.apache.ambari.server.orm.entities.RemoteAmbariClusterServiceEntity</class>
 
     <properties>
       <property name="eclipselink.cache.size.default" value="10000" />

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RemoteClusterResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RemoteClusterResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RemoteClusterResourceProviderTest.java
new file mode 100644
index 0000000..546f155
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RemoteClusterResourceProviderTest.java
@@ -0,0 +1,193 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ambari.server.controller.internal;
+
+import org.apache.ambari.server.controller.predicate.EqualsPredicate;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.orm.dao.RemoteAmbariClusterDAO;
+import org.apache.ambari.server.orm.entities.RemoteAmbariClusterEntity;
+import org.apache.ambari.server.orm.entities.RemoteAmbariClusterServiceEntity;
+import org.apache.ambari.server.security.TestAuthenticationFactory;
+import org.apache.ambari.server.view.RemoteAmbariClusterRegistry;
+import org.easymock.Capture;
+import org.junit.After;
+import org.junit.Test;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.newCapture;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+
+public class RemoteClusterResourceProviderTest {
+
+  @After
+  public void clearAuthentication() {
+    SecurityContextHolder.getContext().setAuthentication(null);
+  }
+
+  @Test
+  public void testToResource() throws Exception {
+    RemoteClusterResourceProvider provider = new RemoteClusterResourceProvider();
+    Set<String> propertyIds = new HashSet<String>();
+    propertyIds.add(RemoteClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID);
+    propertyIds.add(RemoteClusterResourceProvider.CLUSTER_URL_PROPERTY_ID);
+    propertyIds.add(RemoteClusterResourceProvider.USERNAME_PROPERTY_ID);
+    propertyIds.add(RemoteClusterResourceProvider.PASSWORD_PROPERTY_ID);
+    propertyIds.add(RemoteClusterResourceProvider.SERVICES_PROPERTY_ID);
+
+    RemoteAmbariClusterServiceEntity service1 = createNiceMock(RemoteAmbariClusterServiceEntity.class);
+    expect(service1.getServiceName()).andReturn("service1").once();
+
+    RemoteAmbariClusterServiceEntity service2 = createNiceMock(RemoteAmbariClusterServiceEntity.class);
+    expect(service2.getServiceName()).andReturn("service2").once();
+
+    List<RemoteAmbariClusterServiceEntity> serviceList = new ArrayList<RemoteAmbariClusterServiceEntity>();
+    serviceList.add(service1);
+    serviceList.add(service2);
+
+    RemoteAmbariClusterEntity entity = createNiceMock(RemoteAmbariClusterEntity.class);
+    expect(entity.getName()).andReturn("test").once();
+    expect(entity.getUrl()).andReturn("url").once();
+    expect(entity.getUsername()).andReturn("user").once();
+    expect(entity.getServices()).andReturn(serviceList).once();
+
+    replay(service1, service2, entity);
+
+    List<String> services = new ArrayList<String>();
+    services.add("service1");
+    services.add("service2");
+
+    Resource resource = provider.toResource(propertyIds, entity);
+    assertEquals(resource.getPropertyValue(RemoteClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID), "test");
+    assertEquals(resource.getPropertyValue(RemoteClusterResourceProvider.CLUSTER_URL_PROPERTY_ID), "url");
+    assertEquals(resource.getPropertyValue(RemoteClusterResourceProvider.USERNAME_PROPERTY_ID), "user");
+    assertEquals(resource.getPropertyValue(RemoteClusterResourceProvider.SERVICES_PROPERTY_ID), services);
+    verify(service1, service2, entity);
+  }
+
+  @Test
+  public void testCreateResourcesAsAdministrator() throws Exception {
+    testCreateResources(TestAuthenticationFactory.createAdministrator());
+  }
+
+  static void setField(Field field, Object newValue) throws Exception {
+    field.setAccessible(true);
+
+    Field modifiersField = Field.class.getDeclaredField("modifiers");
+    modifiersField.setAccessible(true);
+    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
+    field.set(null, newValue);
+  }
+
+  private void testCreateResources(Authentication authentication) throws Exception {
+
+    RemoteClusterResourceProvider provider = new RemoteClusterResourceProvider();
+
+    RemoteAmbariClusterDAO clusterDAO = createMock(RemoteAmbariClusterDAO.class);
+    setField(RemoteClusterResourceProvider.class.getDeclaredField("remoteAmbariClusterDAO"), clusterDAO);
+
+    RemoteAmbariClusterRegistry clusterRegistry = createMock(RemoteAmbariClusterRegistry.class);
+    setField(RemoteClusterResourceProvider.class.getDeclaredField("remoteAmbariClusterRegistry"), clusterRegistry);
+
+    Set<Map<String, Object>> properties = new HashSet<>();
+    Map<String, Object> propertyMap = new HashMap<>();
+    propertyMap.put(RemoteClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID, "test");
+    propertyMap.put(RemoteClusterResourceProvider.CLUSTER_URL_PROPERTY_ID, "url");
+    propertyMap.put(RemoteClusterResourceProvider.USERNAME_PROPERTY_ID, "username");
+    propertyMap.put(RemoteClusterResourceProvider.PASSWORD_PROPERTY_ID, "password");
+
+    RemoteAmbariClusterServiceEntity service1 = createNiceMock(RemoteAmbariClusterServiceEntity.class);
+    expect(service1.getServiceName()).andReturn("service1").once();
+
+    RemoteAmbariClusterServiceEntity service2 = createNiceMock(RemoteAmbariClusterServiceEntity.class);
+    expect(service2.getServiceName()).andReturn("service2").once();
+
+    List<RemoteAmbariClusterServiceEntity> serviceList = new ArrayList<RemoteAmbariClusterServiceEntity>();
+    serviceList.add(service1);
+    serviceList.add(service2);
+
+    expect(clusterDAO.findByName("test")).andReturn(null).anyTimes();
+    Capture<RemoteAmbariClusterEntity> clusterEntityCapture = newCapture();
+
+    clusterRegistry.saveOrUpdate(capture(clusterEntityCapture),eq(false));
+
+    replay(clusterRegistry, clusterDAO, service1, service2);
+
+    properties.add(propertyMap);
+
+    SecurityContextHolder.getContext().setAuthentication(authentication);
+
+    provider.createResources(PropertyHelper.getCreateRequest(properties, null));
+
+    RemoteAmbariClusterEntity clusterEntity = clusterEntityCapture.getValue();
+    assertEquals(clusterEntity.getName(), "test");
+    assertEquals(clusterEntity.getPassword(), "password");
+    assertEquals(clusterEntity.getUrl(), "url");
+    assertEquals(clusterEntity.getUsername(), "username");
+
+  }
+
+  @Test
+  public void testDeleteResources() throws Exception {
+    RemoteClusterResourceProvider provider = new RemoteClusterResourceProvider();
+    RemoteAmbariClusterDAO clusterDAO = createNiceMock(RemoteAmbariClusterDAO.class);
+    RemoteAmbariClusterEntity clusterEntity = new RemoteAmbariClusterEntity();
+
+    setField(RemoteClusterResourceProvider.class.getDeclaredField("remoteAmbariClusterDAO"), clusterDAO);
+
+    EqualsPredicate equalsPredicate = new EqualsPredicate(RemoteClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID,"test");
+
+    RemoteAmbariClusterRegistry clusterRegistry = createMock(RemoteAmbariClusterRegistry.class);
+    setField(RemoteClusterResourceProvider.class.getDeclaredField("remoteAmbariClusterRegistry"), clusterRegistry);
+
+    Set<Map<String, Object>> properties = new HashSet<>();
+    Map<String, Object> propertyMap = new HashMap<>();
+    propertyMap.put(RemoteClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID,"test");
+
+    expect(clusterDAO.findByName("test")).andReturn(clusterEntity);
+    clusterRegistry.delete(clusterEntity);
+
+    replay(clusterDAO);
+
+    properties.add(propertyMap);
+
+    SecurityContextHolder.getContext().setAuthentication(TestAuthenticationFactory.createAdministrator());
+
+    provider.deleteResources(PropertyHelper.getCreateRequest(properties, null),equalsPredicate);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog240Test.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog240Test.java b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog240Test.java
index 78a910a..f36e640 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog240Test.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog240Test.java
@@ -243,6 +243,22 @@ public class UpgradeCatalog240Test {
     dbAccessor.addFKConstraint(UpgradeCatalog240.VIEWINSTANCE_TABLE, "FK_instance_url_id",
             UpgradeCatalog240.SHORT_URL_COLUMN, UpgradeCatalog240.VIEWURL_TABLE, "url_id", false);
 
+    Capture<DBAccessor.DBColumnInfo> viewInstanceClusterType = newCapture();
+    dbAccessor.addColumn(eq(UpgradeCatalog240.VIEWINSTANCE_TABLE), capture(viewInstanceClusterType));
+
+    // Test remote Cluster Tables
+    Capture<List<DBAccessor.DBColumnInfo>> capturedRemoteAmbariClusterColumns = EasyMock.newCapture();
+    dbAccessor.createTable(eq(UpgradeCatalog240.REMOTE_AMBARI_CLUSTER_TABLE), capture(capturedRemoteAmbariClusterColumns),anyString());
+    dbAccessor.addUniqueConstraint(UpgradeCatalog240.REMOTE_AMBARI_CLUSTER_TABLE , "unq_remote_ambari_cluster" , UpgradeCatalog240.CLUSTER_NAME);
+    expect(dbAccessor.getConnection()).andReturn(connection);
+    expect(connection.createStatement()).andReturn(statement);
+
+    Capture<List<DBAccessor.DBColumnInfo>> capturedRemoteClusterServiceColumns = EasyMock.newCapture();
+    dbAccessor.createTable(eq(UpgradeCatalog240.REMOTE_AMBARI_CLUSTER_SERVICE_TABLE), capture(capturedRemoteClusterServiceColumns),anyString());
+    dbAccessor.addFKConstraint(UpgradeCatalog240.REMOTE_AMBARI_CLUSTER_SERVICE_TABLE, "FK_remote_ambari_cluster_id",
+      UpgradeCatalog240.CLUSTER_ID, UpgradeCatalog240.REMOTE_AMBARI_CLUSTER_TABLE, UpgradeCatalog240.CLUSTER_ID, false);
+    expect(dbAccessor.getConnection()).andReturn(connection);
+    expect(connection.createStatement()).andReturn(statement);
 
     replay(dbAccessor, configuration, connection, statement, resultSet);
 
@@ -403,6 +419,20 @@ public class UpgradeCatalog240Test {
     Assert.assertNotNull(capturedViewUrlColumsValue);
     Assert.assertEquals(capturedViewUrlColumsValue.size(),3);
 
+    // Verify cluster_type column
+    DBAccessor.DBColumnInfo viewInstanceEntityClusterTypeValue = viewInstanceClusterType.getValue();
+    Assert.assertNotNull(viewInstanceClusterType);
+    Assert.assertEquals("cluster_type", viewInstanceEntityClusterTypeValue.getName());
+    Assert.assertEquals(String.class, viewInstanceEntityClusterTypeValue.getType());
+
+    List<DBAccessor.DBColumnInfo> capturedRemoteAmbariClusterColumnsValue = capturedRemoteAmbariClusterColumns.getValue();
+    Assert.assertNotNull(capturedRemoteAmbariClusterColumnsValue);
+    Assert.assertEquals(capturedRemoteAmbariClusterColumnsValue.size(),5);
+
+    List<DBAccessor.DBColumnInfo> capturedRemoteClusterServiceColumnsValue = capturedRemoteClusterServiceColumns.getValue();
+    Assert.assertNotNull(capturedRemoteClusterServiceColumnsValue);
+    Assert.assertEquals(capturedRemoteClusterServiceColumnsValue.size(),3);
+
     verify(dbAccessor);
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/test/java/org/apache/ambari/server/view/RemoteAmbariClusterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/RemoteAmbariClusterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/RemoteAmbariClusterTest.java
new file mode 100644
index 0000000..ce8fe7d
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/view/RemoteAmbariClusterTest.java
@@ -0,0 +1,90 @@
+/**
+ * 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.ambari.server.view;
+
+import org.apache.ambari.server.orm.entities.RemoteAmbariClusterEntity;
+import org.apache.ambari.view.AmbariStreamProvider;
+import org.easymock.IAnswer;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Map;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isNull;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+
+public class RemoteAmbariClusterTest {
+
+  @Rule
+  public ExpectedException thrown= ExpectedException.none();
+
+
+  @Test
+  public void testGetConfigurationValue() throws Exception {
+
+    AmbariStreamProvider clusterStreamProvider = createNiceMock(AmbariStreamProvider.class);
+
+    final String desiredConfigsString = "{\"Clusters\": {\"desired_configs\": {\"test-site\": {\"tag\": \"TAG\"}}}}";
+    final String configurationString = "{\"items\": [{\"properties\": {\"test.property.name\": \"test property value\"}}]}";
+    final int[] desiredConfigPolls = {0};
+    final int[] testConfigPolls = {0};
+
+    expect(clusterStreamProvider.readFrom(eq(  "?fields=services/ServiceInfo,hosts,Clusters"),
+      eq("GET"), (String) isNull(), (Map<String, String>) anyObject())).andAnswer(new IAnswer<InputStream>() {
+      @Override
+      public InputStream answer() throws Throwable {
+        desiredConfigPolls[0] += 1;
+        return new ByteArrayInputStream(desiredConfigsString.getBytes());
+      }
+    }).anyTimes();
+
+    expect(clusterStreamProvider.readFrom(eq( "/configurations?(type=test-site&tag=TAG)"),
+      eq("GET"), (String)isNull(), (Map<String, String>) anyObject())).andAnswer(new IAnswer<InputStream>() {
+      @Override
+      public InputStream answer() throws Throwable {
+        testConfigPolls[0] += 1;
+        return new ByteArrayInputStream(configurationString.getBytes());
+      }
+    }).anyTimes();
+
+    RemoteAmbariClusterEntity entity = createNiceMock(RemoteAmbariClusterEntity.class);
+
+    replay(clusterStreamProvider,entity);
+
+    RemoteAmbariCluster cluster = new RemoteAmbariCluster("Test", clusterStreamProvider);
+
+    String value = cluster.getConfigurationValue("test-site", "test.property.name");
+    assertEquals(value, "test property value");
+    assertEquals(desiredConfigPolls[0], 1);
+    assertEquals(testConfigPolls[0], 1);
+
+    value = cluster.getConfigurationValue("test-site", "test.property.name");
+    assertEquals(value, "test property value");
+    assertEquals(desiredConfigPolls[0], 1);  // cache hit
+    assertEquals(testConfigPolls[0], 1);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/test/java/org/apache/ambari/server/view/ViewAmbariStreamProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewAmbariStreamProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewAmbariStreamProviderTest.java
index 8549d4a..98b57f0 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewAmbariStreamProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewAmbariStreamProviderTest.java
@@ -69,7 +69,7 @@ public class ViewAmbariStreamProviderTest {
 
     ViewAmbariStreamProvider viewAmbariStreamProvider = new ViewAmbariStreamProvider(streamProvider, sessionManager, controller);
 
-    Assert.assertEquals(inputStream, viewAmbariStreamProvider.readFrom("spec", "requestMethod", "params", headers, true));
+    Assert.assertEquals(inputStream, viewAmbariStreamProvider.readFrom("spec", "requestMethod", "params", headers));
 
     verify(streamProvider, sessionManager, urlConnection, inputStream);
   }
@@ -104,7 +104,7 @@ public class ViewAmbariStreamProviderTest {
 
     ViewAmbariStreamProvider viewAmbariStreamProvider = new ViewAmbariStreamProvider(streamProvider, sessionManager, controller);
 
-    Assert.assertEquals(inputStream, viewAmbariStreamProvider.readFrom("spec", "requestMethod", body, headers, true));
+    Assert.assertEquals(inputStream, viewAmbariStreamProvider.readFrom("spec", "requestMethod", body, headers));
 
     verify(streamProvider, sessionManager, urlConnection, inputStream);
   }
@@ -139,7 +139,7 @@ public class ViewAmbariStreamProviderTest {
 
     ViewAmbariStreamProvider viewAmbariStreamProvider = new ViewAmbariStreamProvider(streamProvider, sessionManager, controller);
 
-    Assert.assertEquals(inputStream, viewAmbariStreamProvider.readFrom("spec", "requestMethod", body, headers, true));
+    Assert.assertEquals(inputStream, viewAmbariStreamProvider.readFrom("spec", "requestMethod", body, headers));
 
     verify(streamProvider, sessionManager, urlConnection, inputStream);
   }
@@ -174,7 +174,7 @@ public class ViewAmbariStreamProviderTest {
 
     ViewAmbariStreamProvider viewAmbariStreamProvider = new ViewAmbariStreamProvider(streamProvider, sessionManager, controller);
 
-    Assert.assertEquals(inputStream, viewAmbariStreamProvider.readFrom("spec", "requestMethod", body, headers, true));
+    Assert.assertEquals(inputStream, viewAmbariStreamProvider.readFrom("spec", "requestMethod", body, headers));
 
     verify(streamProvider, sessionManager, urlConnection, inputStream);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java
index a41e137..f318dab 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java
@@ -57,6 +57,7 @@ public class ViewConfigTest {
       "    <system>true</system>\n" +
       "    <icon64>/this/is/the/icon/url/icon64.png</icon64>\n" +
       "    <icon>/this/is/the/icon/url/icon.png</icon>\n" +
+      "    <cluster-config-options>AMBARI-ONLY</cluster-config-options>\n" +
       "    <data-migrator-class>org.apache.ambari.server.view.configuration.ViewConfigTest$MyDataMigrator</data-migrator-class>" +
       "    <validator-class>org.apache.ambari.server.view.configuration.ViewConfigTest$MyValidator</validator-class>" +
       "    <masker-class>org.apache.ambari.server.view.DefaultMasker</masker-class>" +
@@ -352,6 +353,12 @@ public class ViewConfigTest {
     Assert.assertEquals("2.0.0", config.getMaxAmbariVersion());
   }
 
+  @Test
+  public void testGetClusterConfigOptions() throws Exception {
+    ViewConfig config = getConfig();
+    Assert.assertEquals("AMBARI-ONLY",config.getClusterConfigOptions());
+  }
+
   public static  ViewConfig getConfig() throws JAXBException {
       return getConfig(xml);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-views/src/main/java/org/apache/ambari/view/AmbariClusterStreamProvider.java
----------------------------------------------------------------------
diff --git a/ambari-views/src/main/java/org/apache/ambari/view/AmbariClusterStreamProvider.java b/ambari-views/src/main/java/org/apache/ambari/view/AmbariClusterStreamProvider.java
new file mode 100644
index 0000000..a48749a
--- /dev/null
+++ b/ambari-views/src/main/java/org/apache/ambari/view/AmbariClusterStreamProvider.java
@@ -0,0 +1,90 @@
+/**
+ * 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.ambari.view;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * Provider of Input stream for Ambari cluster
+ */
+public interface AmbariClusterStreamProvider {
+
+
+  /**
+   * Read from the input stream specified by the given path for cluster on Ambari server.
+   * Base path will be /api/v1/clusters/${clusterName}
+   *
+   * @param path              the String to parse as an Ambari cluster endpoint (e.g. /configurations)
+   * @param requestMethod     the HTTP method (GET,POST,PUT,etc.).
+   * @param body              the body of the request; may be null
+   * @param headers           the headers of the request; may be null
+   *
+   * @return the input stream
+   *
+   * @throws IOException, AmbariHttpException
+   */
+  public InputStream readFromCluster(String path, String requestMethod, String body, Map<String, String> headers) throws IOException, AmbariHttpException;
+
+  /**
+   * Read from the input stream specified by the given path for cluster on Ambari server.
+   * Base path will be /api/v1/clusters/${clusterName}
+   *
+   * @param path              the String to parse as an Ambari cluster endpoint (e.g. /configurations)
+   * @param requestMethod     the HTTP method (GET,POST,PUT,etc.).
+   * @param body              the body of the request; may be null
+   * @param headers           the headers of the request; may be null
+   *
+   * @return the input stream
+   *
+   * @throws IOException, AmbariHttpException
+   */
+  public InputStream readFromCluster(String path, String requestMethod, InputStream body, Map<String, String> headers) throws IOException, AmbariHttpException;
+
+  /**
+   * Read from the input stream specified by the given path for cluster on the Ambari server.
+   *
+   * @param path              the String to parse as an Ambari endpoint (e.g. /api/v1)
+   * @param requestMethod     the HTTP method (GET,POST,PUT,etc.).
+   * @param body              the body of the request; may be null
+   * @param headers           the headers of the request; may be null
+   *
+   * @return the input stream
+   *
+   * @throws java.io.IOException if an error occurred connecting to the server
+   *         AmbariHttpException if response to url is unsuccessful
+   */
+  public InputStream readFromAmbari(String path, String requestMethod, String body, Map<String, String> headers) throws IOException, AmbariHttpException;
+
+  /**
+   * Read from the input stream specified by the given path for cluster on the Ambari server.
+   *
+   * @param path              the String to parse as an Ambari endpoint (e.g. /api/v1)
+   * @param requestMethod     the HTTP method (GET,POST,PUT,etc.).
+   * @param body              the body of the request; may be null
+   * @param headers           the headers of the request; may be null
+   *
+   * @return the input stream
+   *
+   * @throws java.io.IOException if an error occurred connecting to the server ,
+   *         AmbariHttpException if response to url is unsuccessful
+   */
+  public InputStream readFromAmbari(String path, String requestMethod, InputStream body, Map<String, String> headers) throws IOException, AmbariHttpException;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-views/src/main/java/org/apache/ambari/view/AmbariHttpException.java
----------------------------------------------------------------------
diff --git a/ambari-views/src/main/java/org/apache/ambari/view/AmbariHttpException.java b/ambari-views/src/main/java/org/apache/ambari/view/AmbariHttpException.java
new file mode 100644
index 0000000..60c8e29
--- /dev/null
+++ b/ambari-views/src/main/java/org/apache/ambari/view/AmbariHttpException.java
@@ -0,0 +1,41 @@
+/**
+ * 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.ambari.view;
+
+/**
+ * Ambari Http Exception : used to indicate unsuccessful response of url
+ */
+public class AmbariHttpException extends Exception {
+
+  private int responseCode ;
+
+  public AmbariHttpException(String message, int responseCode) {
+    super(message);
+    this.responseCode = responseCode;
+  }
+
+  /**
+   * Get the Error code
+   *
+   * @return responseCode
+   */
+  public int getResponseCode() {
+    return responseCode;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-views/src/main/java/org/apache/ambari/view/AmbariStreamProvider.java
----------------------------------------------------------------------
diff --git a/ambari-views/src/main/java/org/apache/ambari/view/AmbariStreamProvider.java b/ambari-views/src/main/java/org/apache/ambari/view/AmbariStreamProvider.java
index ce91676..ec9e70c 100644
--- a/ambari-views/src/main/java/org/apache/ambari/view/AmbariStreamProvider.java
+++ b/ambari-views/src/main/java/org/apache/ambari/view/AmbariStreamProvider.java
@@ -33,14 +33,13 @@ public interface AmbariStreamProvider {
    * @param requestMethod     the HTTP method (GET,POST,PUT,etc.).
    * @param body              the body of the request; may be null
    * @param headers           the headers of the request; may be null
-   * @param useAmbariSession  indicates that the current Ambari session cookie should be set for the request
    *
    * @return the input stream
    *
    * @throws java.io.IOException if an error occurred connecting to the server
    */
   public InputStream readFrom(String path, String requestMethod, String body,
-                              Map<String, String> headers, boolean useAmbariSession) throws IOException;
+                              Map<String, String> headers) throws IOException,AmbariHttpException;
 
   /**
    * Read from the input stream specified by the given path on the Ambari server.
@@ -49,12 +48,11 @@ public interface AmbariStreamProvider {
    * @param requestMethod     the HTTP method (GET,POST,PUT,etc.).
    * @param body              the body of the request; may be null
    * @param headers           the headers of the request; may be null
-   * @param useAmbariSession  indicates that the current Ambari session cookie should be set for the request
    *
    * @return the input stream
    *
    * @throws java.io.IOException if an error occurred connecting to the server
    */
   public InputStream readFrom(String path, String requestMethod, InputStream body,
-                              Map<String, String> headers, boolean useAmbariSession) throws IOException;
+                              Map<String, String> headers) throws IOException,AmbariHttpException;
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-views/src/main/java/org/apache/ambari/view/ClusterType.java
----------------------------------------------------------------------
diff --git a/ambari-views/src/main/java/org/apache/ambari/view/ClusterType.java b/ambari-views/src/main/java/org/apache/ambari/view/ClusterType.java
new file mode 100644
index 0000000..bab2c41
--- /dev/null
+++ b/ambari-views/src/main/java/org/apache/ambari/view/ClusterType.java
@@ -0,0 +1,40 @@
+/**
+ * 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.ambari.view;
+
+/**
+ * Cluster type enums
+ */
+public enum ClusterType {
+
+  /**
+   *  Local ambari cluster
+   */
+  LOCAL_AMBARI,
+
+  /**
+   * Remote Ambari managed cluster
+   */
+  REMOTE_AMBARI,
+
+  /**
+   * Used when view is not managed by cluster
+   */
+  NONE
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java
----------------------------------------------------------------------
diff --git a/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java b/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java
index 7b7b025..7aaa799 100644
--- a/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java
+++ b/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java
@@ -172,6 +172,14 @@ public interface ViewContext {
   public AmbariStreamProvider getAmbariStreamProvider();
 
   /**
+   * Get Ambari stream provider attached as cluster to the view
+   * If view is not attached to ambari managed cluster then it will be null
+   *
+   * @return stream provider for the cluster
+   */
+  public AmbariStreamProvider getAmbariClusterStreamProvider();
+
+  /**
    * Get a data store for view persistence entities.
    *
    * @return a data store; null if no instance is associated

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-views/src/main/java/org/apache/ambari/view/ViewInstanceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-views/src/main/java/org/apache/ambari/view/ViewInstanceDefinition.java b/ambari-views/src/main/java/org/apache/ambari/view/ViewInstanceDefinition.java
index 0c3d3ce..cea4c88 100644
--- a/ambari-views/src/main/java/org/apache/ambari/view/ViewInstanceDefinition.java
+++ b/ambari-views/src/main/java/org/apache/ambari/view/ViewInstanceDefinition.java
@@ -61,6 +61,13 @@ public interface ViewInstanceDefinition {
    */
   public String getClusterHandle();
 
+  /**
+   *  Get the type of cluster the view instance is attached to
+   *
+   * @return clusterType the type of cluster for cluster handle
+   */
+  public ClusterType getClusterType();
+
 
   /**
    * Indicates whether or not the view instance should be visible.
@@ -89,4 +96,5 @@ public interface ViewInstanceDefinition {
    * @return the view definition
    */
   public ViewDefinition getViewDefinition();
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-views/src/main/resources/view.xsd
----------------------------------------------------------------------
diff --git a/ambari-views/src/main/resources/view.xsd b/ambari-views/src/main/resources/view.xsd
index 04902e9..cc18950 100644
--- a/ambari-views/src/main/resources/view.xsd
+++ b/ambari-views/src/main/resources/view.xsd
@@ -347,6 +347,11 @@
             <xs:documentation>The Masker class for masking view parameters.</xs:documentation>
           </xs:annotation>
         </xs:element>
+        <xs:element type="xs:string" name="cluster-config-options" minOccurs="0" maxOccurs="1">
+          <xs:annotation>
+            <xs:documentation>Enable views to display cluster options in case of no cluster config properties.</xs:documentation>
+          </xs:annotation>
+        </xs:element>
         <xs:element type="xs:string" name="data-migrator-class" minOccurs="0" maxOccurs="1">
           <xs:annotation>
             <xs:documentation>The data migration class.</xs:documentation>

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
index 7f1d489..718c5a5 100644
--- a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
+++ b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
@@ -118,8 +118,6 @@ public class ConfigurationService {
   public Response readLatestConfiguration() {
     Response response = null;
     try {
-      validateViewConfiguration();
-      
       String versionTag = getVersionTag();
       JSONObject configurations = getConfigurationFromAmbari(versionTag);
       response = Response.ok(configurations).build();
@@ -143,8 +141,6 @@ public class ConfigurationService {
   public Response readClusterInfo() {
     Response response = null;
     try {
-      validateViewConfiguration();
-
       JSONObject configurations = readFromCluster("");
       response = Response.ok(configurations).build();
     } catch (WebApplicationException ex) {
@@ -167,8 +163,6 @@ public class ConfigurationService {
   public Response readAllConfigurations() {
     Response response = null;
     try {
-      validateViewConfiguration();
-
       JSONObject responseJSON = readFromCluster(CONFIGURATION_URL);
       response = Response.ok( responseJSON ).build();
     } catch (WebApplicationException ex) {
@@ -191,8 +185,6 @@ public class ConfigurationService {
   public Response readConfigurationByTag(@PathParam("tag") String tag) {
     Response response = null;
     try {
-      validateViewConfiguration();
-
       JSONObject configurations = getConfigurationFromAmbari(tag);
       response = Response.ok(configurations).build();
     } catch (WebApplicationException ex) {
@@ -265,8 +257,7 @@ public class ConfigurationService {
    * @return    if <code>true</code>, the user is an operator; otherwise <code>false</code>
    */
   private   boolean isOperator() {
-      validateViewConfiguration();
-            
+
       // first check if the user is an CLUSTER.ADMINISTRATOR
       String url = String.format(CLUSTER_OPERATOR_PRIVILEGE_URL, context.getUsername());
       JSONObject json = readFromCluster(url);
@@ -316,33 +307,6 @@ public class ConfigurationService {
     return jsonObject;
   }
 
-  /**
-   * Validates the view configuration properties.
-   *
-   * @throws MisconfigurationFormattedException if one of the required view configuration properties are not set
-   */
-  private void validateViewConfiguration() {
-    // check if we are cluster config'd, if so, just go
-    if (ambariApi.isLocalCluster()) {
-      return;
-    }
-
-    String hostname = context.getProperties().get("ambari.server.url");
-    if (hostname == null) {
-      throw new MisconfigurationFormattedException("ambari.server.url");
-    }
-
-    String username = context.getProperties().get("ambari.server.username");
-    if (username == null) {
-      throw new MisconfigurationFormattedException("ambari.server.username");
-    }
-
-    String password = context.getProperties().get("ambari.server.password");
-    if (password == null) {
-      throw new MisconfigurationFormattedException("ambari.server.password");
-    }
-  }
-
   private JSONObject getConfigurationFromAmbari(String versionTag) {
     String url = String.format(CONFIGURATION_URL_BY_TAG, versionTag);
     JSONObject responseJSON = readFromCluster(url);
@@ -398,7 +362,6 @@ public class ConfigurationService {
   public Response writeConfiguration(JSONObject request) {
     JSONObject response;
     try {
-      validateViewConfiguration();
 
       if (isOperator() == false) {
         return Response.status(401).build();
@@ -467,7 +430,7 @@ public class ConfigurationService {
 
       String rmHosts = getRMHosts();
       JSONObject data = getJsonObject(String.format(RESTART_RM_REQUEST_DATA,
-          ambariApi.getCluster().getName(), rmHosts, rmHosts));
+          context.getCluster().getName(), rmHosts, rmHosts));
 
       Map<String, String> headers = new HashMap<String, String>();
       headers.put("Content-Type", "application/x-www-form-urlencoded");
@@ -491,7 +454,7 @@ public class ConfigurationService {
   @Path("/getConfig")
   public Response getConfigurationValue(@QueryParam("siteName") String siteName,@QueryParam("configName") String configName){
     try{
-      String configValue = ambariApi.getCluster().getConfigurationValue(siteName,configName);
+      String configValue = context.getCluster().getConfigurationValue(siteName,configName);
       JSONObject res = new JSONObject();
       JSONArray arr = new JSONArray();
       JSONObject conf = new JSONObject();

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/capacity-scheduler/src/main/resources/view.xml
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/view.xml b/contrib/views/capacity-scheduler/src/main/resources/view.xml
index 6dc3ffa..bd07a3f 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/view.xml
+++ b/contrib/views/capacity-scheduler/src/main/resources/view.xml
@@ -24,30 +24,7 @@
 
     <validator-class>org.apache.ambari.view.capacityscheduler.PropertyValidator</validator-class>
 
-    <parameter>
-        <name>ambari.server.url</name>
-        <description>Enter the Ambari Server REST API cluster resource.</description>
-        <label>Ambari Cluster URL</label>
-        <placeholder>http://ambari.server:8080/api/v1/clusters/MyCluster</placeholder>
-        <cluster-config>fake</cluster-config>
-        <required>true</required>
-    </parameter>
-    <parameter>
-        <name>ambari.server.username</name>
-        <description>Enter the Cluster Operator username (for example: admin).</description>
-        <label>Operator Username</label>
-        <placeholder>admin</placeholder>
-        <cluster-config>fake</cluster-config>
-        <required>true</required>
-    </parameter>
-    <parameter>
-        <name>ambari.server.password</name>
-        <description>Enter the Cluster Operator password (for example: password).</description>
-        <label>Operator Password</label>
-        <cluster-config>fake</cluster-config>
-        <required>true</required>
-        <masked>true</masked>
-    </parameter>
+    <cluster-config-options>AMBARI-ONLY</cluster-config-options>
 
   <resource>
       <name>scheduler</name>

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/capacity-scheduler/src/test/java/org/apache/ambari/view/capacityscheduler/ConfigurationServiceTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/test/java/org/apache/ambari/view/capacityscheduler/ConfigurationServiceTest.java b/contrib/views/capacity-scheduler/src/test/java/org/apache/ambari/view/capacityscheduler/ConfigurationServiceTest.java
index 62fa21e..add0d0f 100644
--- a/contrib/views/capacity-scheduler/src/test/java/org/apache/ambari/view/capacityscheduler/ConfigurationServiceTest.java
+++ b/contrib/views/capacity-scheduler/src/test/java/org/apache/ambari/view/capacityscheduler/ConfigurationServiceTest.java
@@ -57,11 +57,6 @@ public class ConfigurationServiceTest {
         httpHeaders = createNiceMock(HttpHeaders.class);
         ambariCluster = createNiceMock(Cluster.class);
 
-        properties = new HashMap<String, String>();
-        properties.put(AmbariApi.AMBARI_SERVER_URL_INSTANCE_PROPERTY, BASE_URI);
-        properties.put(AmbariApi.AMBARI_SERVER_USERNAME_INSTANCE_PROPERTY, "admin");
-        properties.put(AmbariApi.AMBARI_SERVER_PASSWORD_INSTANCE_PROPERTY, "admin");
-
         EasyMock.expect(ambariCluster.getConfigurationValue("ranger-yarn-plugin-properties", "ranger-yarn-plugin-enabled")).andReturn("Yes").anyTimes();
         EasyMock.expect(context.getCluster()).andReturn(ambariCluster).anyTimes();
         EasyMock.expect(context.getProperties()).andReturn(properties).anyTimes();

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/uploads/UploadService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/uploads/UploadService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/uploads/UploadService.java
index 83eae9b..4afe80b 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/uploads/UploadService.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/uploads/UploadService.java
@@ -329,7 +329,7 @@ public class UploadService extends BaseService {
   }
 
   private String getHiveMetaStoreLocation() {
-    String dir = this.getAmbariApi().getProperty(HIVE_SITE,HIVE_METASTORE_LOCATION_KEY,HIVE_METASTORE_LOCATION_KEY_VIEW_PROPERTY);
+    String dir = context.getProperties().get(HIVE_METASTORE_LOCATION_KEY_VIEW_PROPERTY);
     if(dir != null && !dir.trim().isEmpty()){
       return dir;
     }else{

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/rest/client/BaseHttpClient.java
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/rest/client/BaseHttpClient.java b/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/rest/client/BaseHttpClient.java
index b4fccf9..3d4a08d 100644
--- a/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/rest/client/BaseHttpClient.java
+++ b/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/rest/client/BaseHttpClient.java
@@ -27,7 +27,6 @@ import java.util.Map;
 import org.apache.ambari.view.URLStreamProvider;
 import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.utils.ambari.AmbariApi;
-import org.apache.ambari.view.utils.ambari.URLStreamProviderBasicAuth;
 import org.apache.commons.httpclient.HttpException;
 import org.apache.commons.io.IOUtils;
 
@@ -76,10 +75,6 @@ public class BaseHttpClient {
 		return viewContext.getURLStreamProvider();
 	}
 
-	public URLStreamProviderBasicAuth getUrlStreamProviderBasicAuth() {
-		return ambariApi.getUrlStreamProviderBasicAuth();
-	}
-
 	public String getUrl() {
 		return url;
 	}
@@ -118,42 +113,39 @@ public class BaseHttpClient {
 
 	public JsonElement doGetJson(String url, String path) throws HttpException,
 			IOException {
-		InputStream inputStream = null;
+		String response = null;
 		try {
 			Map<String, String> headers = new HashMap<String, String>();
 			if (isNeedsAuthentication()) {
-				inputStream = getUrlStreamProviderBasicAuth().readFrom(
+				response = ambariApi.readFromAmbari(
 						url + path, "GET", (String) null, headers);
 			} else {
-				inputStream = getUrlStreamProviderBasicAuth().readAsCurrent(
-						url + path, "GET", (String) null, headers);
+				response = IOUtils.toString(viewContext.getURLStreamProvider().readAsCurrent(
+						url + path, "GET", (String) null, headers));
 			}
-		} catch (IOException e) {
+		} catch (Exception e) {
 			logger.error("Error while reading from url " + url + path, e);
 			HttpException httpException = new HttpException(
 					e.getLocalizedMessage());
 			throw httpException;
 		}
-		JsonElement jsonElement = new JsonParser().parse(new JsonReader(
-				new InputStreamReader(inputStream)));
+		JsonElement jsonElement = new JsonParser().parse(response);
 		return jsonElement;
 	}
 
 	public String doGet(String path) throws HttpException, IOException {
 		String response = null;
 		try {
-			InputStream inputStream = null;
 			if (isNeedsAuthentication()) {
-				inputStream = getUrlStreamProviderBasicAuth().readFrom(
+				response = ambariApi.readFromAmbari(
 						getUrl() + path, "GET", (String) null,
 						new HashMap<String, String>());
 			} else {
-				inputStream = getUrlStreamProviderBasicAuth().readAsCurrent(
+				response = IOUtils.toString(viewContext.getURLStreamProvider().readAsCurrent(
 						getUrl() + path, "GET", (String) null,
-						new HashMap<String, String>());
+						new HashMap<String, String>()));
 			}
-			response = IOUtils.toString(inputStream);
-		} catch (IOException e) {
+		} catch (Exception e) {
 			logger.error("Error while reading from url " + getUrl() + path, e);
 			HttpException httpException = new HttpException(
 					e.getLocalizedMessage());

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/AmbariApi.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/AmbariApi.java b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/AmbariApi.java
index 6b88b6f..6072d28 100644
--- a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/AmbariApi.java
+++ b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/AmbariApi.java
@@ -18,6 +18,7 @@
 
 package org.apache.ambari.view.utils.ambari;
 
+import org.apache.ambari.view.AmbariHttpException;
 import org.apache.ambari.view.URLStreamProvider;
 import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.cluster.Cluster;
@@ -31,6 +32,7 @@ import java.io.InputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -40,54 +42,31 @@ import java.util.Map;
  * on both local and remote cluster.
  */
 public class AmbariApi {
-  public static final String AMBARI_SERVER_URL_INSTANCE_PROPERTY = "ambari.server.url";
-  public static final String AMBARI_SERVER_USERNAME_INSTANCE_PROPERTY = "ambari.server.username";
-  public static final String AMBARI_SERVER_PASSWORD_INSTANCE_PROPERTY = "ambari.server.password";
 
-  private Cluster cluster;
   private ViewContext context;
   private Services services;
 
-  private String remoteUrlCluster;
-  private String remoteUsername;
-  private String remotePassword;
   private String requestedBy = "views";
 
+  public static String API_PREFIX = "/api/v1/clusters/";
+
   /**
    * Constructor for Ambari API based on ViewContext
    * @param context View Context
    */
   public AmbariApi(ViewContext context) {
     this.context = context;
-
-    remoteUrlCluster = context.getProperties().get(AMBARI_SERVER_URL_INSTANCE_PROPERTY);
-    remoteUsername = context.getProperties().get(AMBARI_SERVER_USERNAME_INSTANCE_PROPERTY);
-    remotePassword = context.getProperties().get(AMBARI_SERVER_PASSWORD_INSTANCE_PROPERTY);
   }
 
   /**
-   * X-Requested-By header value
-   * @param requestedBy value of X-Requested-By header
+   *  Set requestedBy header
+   *
+   * @param requestedBy
    */
   public void setRequestedBy(String requestedBy) {
     this.requestedBy = requestedBy;
   }
 
-  private String parseAmbariHostname(String remoteUrlCluster) {
-    String hostname;
-    try {
-      URI uri = new URI(remoteUrlCluster);
-      hostname = String.format("%s://%s", uri.getScheme(), uri.getHost());
-      if (uri.getPort() != -1) {
-        hostname += ":" + uri.getPort();
-      }
-    } catch (URISyntaxException e) {
-      throw new AmbariApiException("RA060 Malformed URI of remote Ambari");
-    }
-
-    return hostname;
-  }
-
   /**
    * Provides ability to get cluster topology
    * @param requestComponent name of component
@@ -153,27 +132,24 @@ public class AmbariApi {
    */
   public String requestClusterAPI(String path, String method, String data, Map<String, String> headers) throws AmbariApiException {
     String response;
-    URLStreamProviderBasicAuth urlStreamProvider = getUrlStreamProviderBasicAuth();
 
     try {
-      String url;
 
-      if (isLocalCluster()) {
-        url = String.format("/api/v1/clusters/%s/%s", getCluster().getName(), path);
-
-      } else if (isRemoteCluster()) {
-
-        url = String.format("%s/%s", remoteUrlCluster, path);
-
-      } else {
+      if (context.getAmbariClusterStreamProvider() == null || context.getCluster() == null) {
         throw new NoClusterAssociatedException(
             "RA030 View is not associated with any cluster. No way to request Ambari.");
       }
 
-      InputStream inputStream = urlStreamProvider.readFrom(url, method, data, headers);
+      if(!path.startsWith("/")) path = "/" + path;
+
+      path =  API_PREFIX + context.getCluster().getName() + path;
+
+      InputStream inputStream = context.getAmbariClusterStreamProvider().readFrom(path, method, data, addRequestedByHeader(headers));
       response = IOUtils.toString(inputStream);
     } catch (IOException e) {
       throw new AmbariApiException("RA040 I/O error while requesting Ambari", e);
+    } catch (AmbariHttpException e) {
+      throw new AmbariApiException("RA040 Request to Ambari is unsuccessful with response code "+e.getResponseCode(), e);
     }
     return response;
   }
@@ -189,118 +165,25 @@ public class AmbariApi {
    */
   public String readFromAmbari(String path, String method, String data, Map<String, String> headers) throws AmbariApiException {
     String response;
-    URLStreamProviderBasicAuth urlStreamProvider = getUrlStreamProviderBasicAuth();
 
     try {
-      String url;
-
-      if (isLocalCluster()) {
-        url = path;
-
-      } else if (isRemoteCluster()) {
-        String ambariUrl = parseAmbariHostname(remoteUrlCluster);
-        url = ambariUrl + path;
 
-      } else {
+      if (context.getAmbariClusterStreamProvider() == null) {
         throw new NoClusterAssociatedException(
             "RA060 View is not associated with any cluster. No way to request Ambari.");
       }
 
-      InputStream inputStream = urlStreamProvider.readFrom(url, method, data, headers);
+      InputStream inputStream = context.getAmbariClusterStreamProvider().readFrom(path, method, data, addRequestedByHeader(headers));
       response = IOUtils.toString(inputStream);
     } catch (IOException e) {
       throw new AmbariApiException("RA050 I/O error while requesting Ambari", e);
+    } catch (AmbariHttpException e) {
+      throw new AmbariApiException("RA040 Request to Ambari is unsuccessful with response code "+e.getResponseCode(), e);
     }
     return response;
   }
 
 
-  /**
-   * Check if associated with local or remote cluster
-   * @return true if associated
-   */
-  public boolean isClusterAssociated() {
-    try {
-      getCluster();
-      return true;
-    } catch (NoClusterAssociatedException e) {
-      return false;
-    }
-  }
-
-  /**
-   * Cluster object that provides access for Ambari configuration
-   * @return cluster if locally associated or RemoteCluster
-   * @throws NoClusterAssociatedException
-   */
-  public Cluster getCluster() throws NoClusterAssociatedException {
-    if (cluster == null) {
-      if (isLocalCluster()) {
-        cluster = context.getCluster();
-
-      } else if (isRemoteCluster()) {
-        cluster = getRemoteCluster();
-
-      } else {
-        throw new NoClusterAssociatedException(
-            "RA050 View is not associated with any cluster. No way to request Ambari.");
-      }
-    }
-    return cluster;
-  }
-
-  /**
-   * Is associated with local cluster
-   * @return true if associated
-   */
-  public boolean isLocalCluster() {
-    return context.getCluster() != null;
-  }
-
-  /**
-   * Is associated with remote cluster
-   * @return true if associated
-   */
-  public boolean isRemoteCluster() {
-    return remoteUrlCluster != null && !remoteUrlCluster.isEmpty();
-  }
-
-  /**
-   * Build RemoteCluster instance based on viewContext properties
-   * @return RemoteCluster instance
-   */
-  public RemoteCluster getRemoteCluster() {
-    if (!isRemoteCluster())
-      return null;
-
-    URLStreamProvider urlStreamProviderBasicAuth = getUrlStreamProviderBasicAuth();
-    return new RemoteCluster(remoteUrlCluster, urlStreamProviderBasicAuth);
-  }
-
-  /**
-   * Build URLStreamProvider with Basic Authentication for Remote Cluster
-   * @return URLStreamProvider
-   */
-  public URLStreamProviderBasicAuth getUrlStreamProviderBasicAuth() {
-    URLStreamProviderBasicAuth urlStreamProviderBasicAuth;
-    if (isRemoteCluster()) {
-      if (remoteUsername == null || remoteUsername.isEmpty() ||
-          remotePassword == null || remotePassword.isEmpty()) {
-        throw new AmbariApiException("RA020 Remote Ambari username and password are not filled");
-      }
-
-      URLStreamProvider urlStreamProvider = context.getURLStreamProvider();
-      urlStreamProviderBasicAuth =
-          new URLStreamProviderBasicAuth(urlStreamProvider, remoteUsername, remotePassword);
-    } else if (isLocalCluster()) {
-      urlStreamProviderBasicAuth = new URLStreamProviderBasicAuth(context.getAmbariStreamProvider());
-    } else {
-      throw new NoClusterAssociatedException(
-          "RA070 Not associated with any cluster. URLStreamProvider is not available");
-    }
-    urlStreamProviderBasicAuth.setRequestedBy(requestedBy);
-    return urlStreamProviderBasicAuth;
-  }
 
   /**
    * Provides access to service-specific utilities
@@ -313,11 +196,23 @@ public class AmbariApi {
     return services;
   }
 
-  public String getProperty(String type, String key, String instanceProperty) {
-    try {
-      return this.getCluster().getConfigurationValue(type, key);
-    } catch (NoClusterAssociatedException e) {
-      return context.getProperties().get(instanceProperty);
+  private Map<String,String> addRequestedByHeader(Map<String,String> headers){
+    if(headers == null){
+      headers = new HashMap<String, String>();
     }
+
+    headers.put("X-Requested-By",this.requestedBy);
+
+    return headers;
   }
+
+  /**
+   * Check if view is associated with cluster
+   *
+   * @return isClusterAssociated
+   */
+  public boolean isClusterAssociated(){
+    return context.getCluster() != null;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/RemoteCluster.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/RemoteCluster.java b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/RemoteCluster.java
deleted file mode 100644
index 41dc88d..0000000
--- a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/RemoteCluster.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * 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.ambari.view.utils.ambari;
-
-import org.apache.ambari.view.URLStreamProvider;
-import org.apache.ambari.view.ViewContext;
-import org.apache.ambari.view.cluster.Cluster;
-import org.apache.commons.collections4.map.PassiveExpiringMap;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.input.NullInputStream;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
-import org.json.simple.JSONValue;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * Class that provides same interface as local Cluster, but
- * is able to retrieve configuration values by REST API
- */
-public class RemoteCluster implements Cluster {
-  protected String name;
-  protected String baseUrl;
-  protected URLStreamProvider urlStreamProvider;
-  protected Map<String, JSONObject> configurationCache;
-
-  /**
-   * Constructor for RemoteCluster
-   * @param ambariClusterUrl Ambari Server Cluster REST API URL (for example: http://ambari.server:8080/api/v1/clusters/c1)
-   * @param urlStreamProvider stream provider with authorization support
-   */
-  public RemoteCluster(String ambariClusterUrl, URLStreamProvider urlStreamProvider) {
-    this.baseUrl = ambariClusterUrl;
-    this.urlStreamProvider = urlStreamProvider;
-
-    String[] parts = ambariClusterUrl.split("/");
-    this.name = parts[parts.length-1];
-    PassiveExpiringMap<String, JSONObject> configurations = new PassiveExpiringMap<String, JSONObject>(10000L);  // keep cache for 10 seconds
-    configurationCache = Collections.synchronizedMap(configurations);
-  }
-
-  @Override
-  public String getName() {
-    return name;
-  }
-
-  @Override
-  public String getConfigurationValue(String type, String key) {
-    JSONObject config;
-    try {
-      String desiredTag = getDesiredConfig(type);
-      config = readFromUrlJSON(String.format("%s/configurations?(type=%s&tag=%s)", baseUrl, type, desiredTag));
-    } catch (IOException e) {
-      throw new AmbariApiException("RA010 Can't retrieve configuration from Remote Ambari", e);
-    }
-
-    JSONObject items = (JSONObject) ((JSONArray) config.get("items")).get(0);
-    JSONObject properties = (JSONObject) items.get("properties");
-    return (properties == null ? null : (String) properties.get(key));
-  }
-
-  private String getDesiredConfig(String type) throws IOException {
-    JSONObject desiredConfigResponse = readFromUrlJSON(
-        String.format("%s?fields=services/ServiceInfo,hosts,Clusters", baseUrl));
-    JSONObject clusters = (JSONObject) (desiredConfigResponse.get("Clusters"));
-    JSONObject desiredConfig = (JSONObject) (clusters.get("desired_configs"));
-    JSONObject desiredConfigForType = (JSONObject) desiredConfig.get(type);
-
-    return (String) desiredConfigForType.get("tag");
-  }
-
-  private JSONObject readFromUrlJSON(String url) throws IOException {
-    JSONObject jsonObject = configurationCache.get(url);
-    if (jsonObject == null) {
-      InputStream inputStream = urlStreamProvider.readFrom(url, "GET", (String)null, null);
-      String response = IOUtils.toString(inputStream);
-      jsonObject = (JSONObject) JSONValue.parse(response);
-
-      configurationCache.put(url, jsonObject);
-    }
-    return jsonObject;
-  }
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/Services.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/Services.java b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/Services.java
index a8ef43f..1bace94 100644
--- a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/Services.java
+++ b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/Services.java
@@ -75,7 +75,7 @@ public class Services {
   public String getRMUrl() {
     String url;
 
-    if (ambariApi.isClusterAssociated()) {
+    if (context.getCluster() != null) {
       url = getRMUrlFromClusterConfig();
     } else {
       url = getRmUrlFromCustomConfig();
@@ -216,7 +216,7 @@ public class Services {
   public String getWebHCatURL() {
     String host = null;
 
-    if (ambariApi.isClusterAssociated()) {
+    if (context.getCluster() != null) {
       List<String> hiveServerHosts = ambariApi.getHostsWithComponent("WEBHCAT_SERVER");
 
       if (!hiveServerHosts.isEmpty()) {
@@ -249,7 +249,7 @@ public class Services {
    * yarn-site.xml else it is retrieved from the view configuration.
    */
   public String getTimelineServerUrl() {
-    String url = ambariApi.isClusterAssociated() ? getATSUrlFromCluster() : getATSUrlFromCustom();
+    String url = context.getCluster() != null ? getATSUrlFromCluster() : getATSUrlFromCustom();
     return removeTrailingSlash(url);
   }
 
@@ -337,7 +337,7 @@ public class Services {
   }
 
   private String getYarnConfig(String key) {
-    return ambariApi.getCluster().getConfigurationValue(YARN_SITE, key);
+    return context.getCluster().getConfigurationValue(YARN_SITE, key);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuth.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuth.java b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuth.java
deleted file mode 100644
index c9f735a..0000000
--- a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuth.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/**
- * 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.ambari.view.utils.ambari;
-
-import org.apache.ambari.view.AmbariStreamProvider;
-import org.apache.ambari.view.URLStreamProvider;
-import org.apache.commons.codec.binary.Base64;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Wrapper for URLStreamProvider that adds authentication header.
- * Also supports AmbariStreamProvider. readAs or readAsCurrent should not be used
- * with AmbariStreamProvider.
- */
-public class URLStreamProviderBasicAuth implements URLStreamProvider {
-  private URLStreamProvider urlStreamProvider;
-  private AmbariStreamProvider ambariStreamProvider;
-  private String username;
-  private String password;
-  private String requestedBy = "views";
-
-  public URLStreamProviderBasicAuth(URLStreamProvider urlStreamProvider, String username, String password) {
-    this.urlStreamProvider = urlStreamProvider;
-    this.username = username;
-    this.password = password;
-  }
-
-  public URLStreamProviderBasicAuth(AmbariStreamProvider urlStreamProvider) {
-    this.ambariStreamProvider = urlStreamProvider;
-  }
-
-  /**
-   * X-Requested-By header value
-   * @param requestedBy value of X-Requested-By header
-   */
-  public void setRequestedBy(String requestedBy) {
-    this.requestedBy = requestedBy;
-  }
-
-  @Override
-  public InputStream readFrom(String url, String method, String data, Map<String, String> headers) throws IOException {
-    if (urlStreamProvider != null) {
-      return urlStreamProvider.readFrom(url, method, data, addHeaders(headers));
-    } else {
-      return ambariStreamProvider.readFrom(url, method, data, addHeaders(headers), true);
-    }
-  }
-
-  @Override
-  public InputStream readFrom(String url, String method, InputStream data, Map<String, String> headers) throws IOException {
-    if (urlStreamProvider != null) {
-      return urlStreamProvider.readFrom(url, method, data, addHeaders(headers));
-    } else {
-      return ambariStreamProvider.readFrom(url, method, data, addHeaders(headers), true);
-    }
-  }
-
-  @Override
-  public InputStream readAs(String url, String method, String data, Map<String, String> headers, String doAs) throws IOException {
-    return urlStreamProvider.readAs(url, method, data, addHeaders(headers), doAs);
-  }
-
-  @Override
-  public InputStream readAs(String url, String method, InputStream data, Map<String, String> headers, String doAs) throws IOException {
-    return urlStreamProvider.readAs(url, method, data, addHeaders(headers), doAs);
-  }
-
-  @Override
-  public InputStream readAsCurrent(String url, String method, String data, Map<String, String> headers) throws IOException {
-    return urlStreamProvider.readAsCurrent(url, method, data, addHeaders(headers));
-  }
-
-  @Override
-  public InputStream readAsCurrent(String url, String method, InputStream data, Map<String, String> headers) throws IOException {
-    return urlStreamProvider.readAsCurrent(url, method, data, addHeaders(headers));
-  }
-
-  private HashMap<String, String> addHeaders(Map<String, String> customHeaders) {
-    HashMap<String, String> newHeaders = new HashMap<String, String>();
-    if (customHeaders != null)
-      newHeaders.putAll(customHeaders);
-
-    if (urlStreamProvider != null) {
-      // basic auth is not needed for AmbariStreamProvider
-      addBasicAuthHeaders(newHeaders);
-    }
-    addRequestedByHeaders(newHeaders);
-    return newHeaders;
-  }
-
-  private void addRequestedByHeaders(HashMap<String, String> newHeaders) {
-    newHeaders.put("X-Requested-By", requestedBy);
-  }
-
-  private void addBasicAuthHeaders(HashMap<String, String> headers) {
-    String authString = username + ":" + password;
-    byte[] authEncBytes = Base64.encodeBase64(authString.getBytes());
-    String authStringEnc = new String(authEncBytes);
-
-    headers.put("Authorization", "Basic " + authStringEnc);
-  }
-}


[4/4] ambari git commit: AMBARI-16274. Views: Simplify View Configuration for Remote Ambari Managed Cluster. (Gaurav Nagar via dipayanb)

Posted by db...@apache.org.
AMBARI-16274. Views: Simplify View Configuration for Remote Ambari Managed Cluster. (Gaurav Nagar via dipayanb)


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

Branch: refs/heads/trunk
Commit: c150f0ded83c0a2f4eac1d5bf12a0fbb643c9610
Parents: bc82a6e
Author: Dipayan Bhowmick <di...@gmail.com>
Authored: Wed May 11 14:22:34 2016 +0530
Committer: Dipayan Bhowmick <di...@gmail.com>
Committed: Wed May 11 14:22:34 2016 +0530

----------------------------------------------------------------------
 .../main/resources/ui/admin-web/app/index.html  | 280 ++++++++--------
 .../ambariViews/CreateViewInstanceCtrl.js       |  70 +++-
 .../controllers/ambariViews/ViewsEditCtrl.js    |  86 +++--
 .../remoteClusters/RemoteClustersCreateCtrl.js  |  63 ++++
 .../remoteClusters/RemoteClustersEditCtrl.js    | 159 +++++++++
 .../remoteClusters/RemoteClustersListCtrl.js    | 106 ++++++
 .../ui/admin-web/app/scripts/i18n.config.js     |  20 +-
 .../ui/admin-web/app/scripts/routes.js          |  17 +
 .../app/scripts/services/RemoteCluster.js       | 118 +++++++
 .../ui/admin-web/app/scripts/services/View.js   |   3 +-
 .../resources/ui/admin-web/app/styles/main.css  |  25 ++
 .../admin-web/app/views/ambariViews/create.html |  38 ++-
 .../admin-web/app/views/ambariViews/edit.html   |  50 ++-
 .../ui/admin-web/app/views/leftNavbar.html      |  13 +-
 .../remoteClusters/editRemoteClusterPage.html   |  77 +++++
 .../app/views/remoteClusters/list.html          |  80 +++++
 .../remoteClusters/modals/changePassword.html   |  46 +++
 .../views/remoteClusters/remoteClusterPage.html |  66 ++++
 .../RemoteClusterResourceDefinition.java        |  51 +++
 .../resources/ResourceInstanceFactoryImpl.java  |   4 +
 .../api/services/RemoteClustersService.java     | 139 ++++++++
 .../internal/DefaultProviderModule.java         |   2 +
 .../internal/RemoteClusterResourceProvider.java | 327 +++++++++++++++++++
 .../internal/ViewInstanceResourceProvider.java  |   9 +
 .../ambari/server/controller/spi/Resource.java  |   4 +-
 .../server/orm/dao/RemoteAmbariClusterDAO.java  | 101 ++++++
 .../orm/entities/RemoteAmbariClusterEntity.java | 201 ++++++++++++
 .../RemoteAmbariClusterServiceEntity.java       | 108 ++++++
 .../ambari/server/orm/entities/ViewEntity.java  |   9 +
 .../server/orm/entities/ViewInstanceEntity.java |  29 ++
 .../server/upgrade/UpgradeCatalog240.java       |  37 +++
 .../ambari/server/view/RemoteAmbariCluster.java | 190 +++++++++++
 .../view/RemoteAmbariClusterRegistry.java       | 116 +++++++
 .../RemoteAmbariConfigurationReadException.java |  29 ++
 .../server/view/RemoteAmbariStreamProvider.java | 118 +++++++
 .../server/view/ViewAmbariStreamProvider.java   |  51 +--
 .../ambari/server/view/ViewContextImpl.java     |  23 +-
 .../apache/ambari/server/view/ViewRegistry.java |  50 ++-
 .../server/view/configuration/ViewConfig.java   |  15 +
 .../main/resources/Ambari-DDL-Derby-CREATE.sql  |  29 +-
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  |  25 +-
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql |  23 ++
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |  25 +-
 .../Ambari-DDL-Postgres-EMBEDDED-CREATE.sql     |  29 +-
 .../resources/Ambari-DDL-SQLAnywhere-CREATE.sql |  23 ++
 .../resources/Ambari-DDL-SQLServer-CREATE.sql   |  25 +-
 .../src/main/resources/META-INF/persistence.xml |   2 +
 .../RemoteClusterResourceProviderTest.java      | 193 +++++++++++
 .../server/upgrade/UpgradeCatalog240Test.java   |  30 ++
 .../server/view/RemoteAmbariClusterTest.java    |  90 +++++
 .../view/ViewAmbariStreamProviderTest.java      |   8 +-
 .../view/configuration/ViewConfigTest.java      |   7 +
 .../view/AmbariClusterStreamProvider.java       |  90 +++++
 .../apache/ambari/view/AmbariHttpException.java |  41 +++
 .../ambari/view/AmbariStreamProvider.java       |   6 +-
 .../org/apache/ambari/view/ClusterType.java     |  40 +++
 .../org/apache/ambari/view/ViewContext.java     |   8 +
 .../ambari/view/ViewInstanceDefinition.java     |   8 +
 ambari-views/src/main/resources/view.xsd        |   5 +
 .../capacityscheduler/ConfigurationService.java |  43 +--
 .../src/main/resources/view.xml                 |  25 +-
 .../ConfigurationServiceTest.java               |   5 -
 .../hive/resources/uploads/UploadService.java   |   2 +-
 .../view/slider/rest/client/BaseHttpClient.java |  28 +-
 .../ambari/view/utils/ambari/AmbariApi.java     | 177 ++--------
 .../ambari/view/utils/ambari/RemoteCluster.java | 104 ------
 .../ambari/view/utils/ambari/Services.java      |   8 +-
 .../ambari/URLStreamProviderBasicAuth.java      | 122 -------
 .../utils/hdfs/AuthConfigurationBuilder.java    |  14 +-
 .../view/utils/hdfs/ConfigurationBuilder.java   |  14 +-
 .../view/utils/ambari/RemoteClusterTest.java    | 137 --------
 .../ambari/view/utils/ambari/ServicesTest.java  |  14 +-
 .../ambari/URLStreamProviderBasicAuthTest.java  | 159 ---------
 73 files changed, 3458 insertions(+), 1031 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/index.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/index.html b/ambari-admin/src/main/resources/ui/admin-web/app/index.html
index 8d7e8e7..a4a5b28 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/index.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/index.html
@@ -17,156 +17,160 @@
 -->
 <!doctype html>
 <html class="no-js">
-  <head>
-    <meta charset="utf-8">
-    <title>Ambari</title>
-    <meta name="description" content="">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <link rel="shortcut icon" href="/img/logo.png" type="image/x-icon">
-    <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
+<head>
+  <meta charset="utf-8">
+  <title>Ambari</title>
+  <meta name="description" content="">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <link rel="shortcut icon" href="/img/logo.png" type="image/x-icon">
+  <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
 
-    <!-- build:css styles/vendor.css -->
-    <!-- bower:css -->
-    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
-    <link rel="stylesheet" href="bower_components/angular-bootstrap-toggle-switch/style/bootstrap3/angular-toggle-switch-bootstrap-3.css" />
-    <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css"/>
-    <!-- endbower -->
-    <!-- endbuild -->
+  <!-- build:css styles/vendor.css -->
+  <!-- bower:css -->
+  <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
+  <link rel="stylesheet" href="bower_components/angular-bootstrap-toggle-switch/style/bootstrap3/angular-toggle-switch-bootstrap-3.css" />
+  <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css"/>
+  <!-- endbower -->
+  <!-- endbuild -->
 
-    <!-- build:css styles/main.css -->
-    <link rel="stylesheet" href="styles/main.css">
-    <!-- endbuild -->
+  <!-- build:css styles/main.css -->
+  <link rel="stylesheet" href="styles/main.css">
+  <!-- endbuild -->
 
-  </head>
-  <body ng-app="ambariAdminConsole">
-    <!--[if lt IE 9]>
-      <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
-    <![endif]-->
+</head>
+<body ng-app="ambariAdminConsole">
+<!--[if lt IE 9]>
+<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
+<![endif]-->
 
-    <div id="top-nav" ng-controller="MainCtrl">
-      <header class="navbar navbar-static-top navbar-inverse">
-        <div class="navbar-inner">
-          <div class="container">
-            <a href="/#/main/dashboard" class="logo"><img src="/img/logo-white.png" alt="{{'common.apacheAmbari' | translate}}" title="{{'common.apacheAmbari' | translate}}"></a>
-            <a href="/#/main/dashboard" class="brand" title="{{'common.apacheAmbari' | translate}}">{{'common.ambari' | translate}}</a>
-            <ul class="nav navbar-nav navbar-right">
-              <li>
-                <div class="btn-group navbar-views-dropdown" dropdown is-open="viewsdropdown.isopen" ng-mouseover="viewsdropdown.isopen=true" ng-mouseout="viewsdropdown.isopen=false">
-                  <a href="/#/main/views" ng-click="gotoViewsDashboard()" class="dropdown-toggle"><i class="fa fa-th"></i></a>
-                  <ul class="dropdown-menu" role="menu">
-                    <li ng-repeat="instance in viewInstances"><a href="/#/main{{instance.context_path}}" ng-click="about()">{{instance.label}}</a></li>
-                    <li ng-show="!viewInstances.length" class="disabled"><a>{{'common.noViews' | translate}}</a></li>
-                  </ul>
-                </div>
-              </li>
-              <li>
-                <div class="btn-group" dropdown is-open="status.isopen">
-                  <button type="button" class="btn btn-default dropdown-toggle navbar-btn" ng-disabled="disabled">
-                    <i class="fa fa-user"></i> {{currentUser}} <span class="caret"></span>
-                  </button>
-                  <ul class="dropdown-menu" role="menu">
-                    <li><a href ng-click="about()">{{'common.about' | translate}}</a></li>
-                    <li class="divider"></li>
-                    <li><a href ng-click="signOut()">{{'common.signOut' | translate}}</a></li>
-                  </ul>
-                </div>
-              </li>
-            </ul>
-          </div>
-        </div>
-      </header>
+<div id="top-nav" ng-controller="MainCtrl">
+  <header class="navbar navbar-static-top navbar-inverse">
+    <div class="navbar-inner">
+      <div class="container">
+        <a href="/#/main/dashboard" class="logo"><img src="/img/logo-white.png" alt="{{'common.apacheAmbari' | translate}}" title="{{'common.apacheAmbari' | translate}}"></a>
+        <a href="/#/main/dashboard" class="brand" title="{{'common.apacheAmbari' | translate}}">{{'common.ambari' | translate}}</a>
+        <ul class="nav navbar-nav navbar-right">
+          <li>
+            <div class="btn-group navbar-views-dropdown" dropdown is-open="viewsdropdown.isopen" ng-mouseover="viewsdropdown.isopen=true" ng-mouseout="viewsdropdown.isopen=false">
+              <a href="/#/main/views" ng-click="gotoViewsDashboard()" class="dropdown-toggle"><i class="fa fa-th"></i></a>
+              <ul class="dropdown-menu" role="menu">
+                <li ng-repeat="instance in viewInstances"><a href="/#/main{{instance.context_path}}" ng-click="about()">{{instance.label}}</a></li>
+                <li ng-show="!viewInstances.length" class="disabled"><a>{{'common.noViews' | translate}}</a></li>
+              </ul>
+            </div>
+          </li>
+          <li>
+            <div class="btn-group" dropdown is-open="status.isopen">
+              <button type="button" class="btn btn-default dropdown-toggle navbar-btn" ng-disabled="disabled">
+                <i class="fa fa-user"></i> {{currentUser}} <span class="caret"></span>
+              </button>
+              <ul class="dropdown-menu" role="menu">
+                <li><a href ng-click="about()">{{'common.about' | translate}}</a></li>
+                <li class="divider"></li>
+                <li><a href ng-click="signOut()">{{'common.signOut' | translate}}</a></li>
+              </ul>
+            </div>
+          </li>
+        </ul>
+      </div>
     </div>
+  </header>
+</div>
 
 
-    <div class="container">
-      <div class="row">
-        <div class="col-sm-3">
-          <div ng-include="'views/leftNavbar.html'" ng-controller="NavbarCtrl"></div>
-        </div>
-        <div class="col-sm-9">
-          <ng-view></ng-view>
-        </div>
-      </div>
+<div class="container">
+  <div class="row">
+    <div class="col-sm-3">
+      <div ng-include="'views/leftNavbar.html'" ng-controller="NavbarCtrl"></div>
+    </div>
+    <div class="col-sm-9">
+      <ng-view></ng-view>
     </div>
+  </div>
+</div>
 
 
-    <!-- build:js scripts/vendor.js -->
-    <!-- bower:js -->
-    <script src="bower_components/jquery/dist/jquery.js"></script>
-    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
-    <script src="bower_components/angular/angular.js"></script>
-    <script src="bower_components/angular-animate/angular-animate.js"></script>
-    <script src="bower_components/angular-route/angular-route.js"></script>
-    <script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
-    <script src="bower_components/lodash/dist/lodash.compat.js"></script>
-    <script src="bower_components/restangular/dist/restangular.js"></script>
-    <script src="bower_components/angular-bootstrap-toggle-switch/angular-toggle-switch.min.js"></script>
-    <script src="bower_components/angular-translate/angular-translate.min.js"></script>
-    <!-- endbower -->
-    <!-- endbuild -->
+<!-- build:js scripts/vendor.js -->
+<!-- bower:js -->
+<script src="bower_components/jquery/dist/jquery.js"></script>
+<script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
+<script src="bower_components/angular/angular.js"></script>
+<script src="bower_components/angular-animate/angular-animate.js"></script>
+<script src="bower_components/angular-route/angular-route.js"></script>
+<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
+<script src="bower_components/lodash/dist/lodash.compat.js"></script>
+<script src="bower_components/restangular/dist/restangular.js"></script>
+<script src="bower_components/angular-bootstrap-toggle-switch/angular-toggle-switch.min.js"></script>
+<script src="bower_components/angular-translate/angular-translate.min.js"></script>
+<!-- endbower -->
+<!-- endbuild -->
 
-    <!-- build:js scripts/plugins.js -->
-    <script src="bower_components/bootstrap/js/affix.js"></script>
-    <script src="bower_components/bootstrap/js/alert.js"></script>
-    <script src="bower_components/bootstrap/js/dropdown.js"></script>
-    <script src="bower_components/bootstrap/js/tooltip.js"></script>
-    <script src="bower_components/bootstrap/js/modal.js"></script>
-    <script src="bower_components/bootstrap/js/transition.js"></script>
-    <script src="bower_components/bootstrap/js/button.js"></script>
-    <script src="bower_components/bootstrap/js/popover.js"></script>
-    <script src="bower_components/bootstrap/js/carousel.js"></script>
-    <script src="bower_components/bootstrap/js/scrollspy.js"></script>
-    <script src="bower_components/bootstrap/js/collapse.js"></script>
-    <script src="bower_components/bootstrap/js/tab.js"></script>
+<!-- build:js scripts/plugins.js -->
+<script src="bower_components/bootstrap/js/affix.js"></script>
+<script src="bower_components/bootstrap/js/alert.js"></script>
+<script src="bower_components/bootstrap/js/dropdown.js"></script>
+<script src="bower_components/bootstrap/js/tooltip.js"></script>
+<script src="bower_components/bootstrap/js/modal.js"></script>
+<script src="bower_components/bootstrap/js/transition.js"></script>
+<script src="bower_components/bootstrap/js/button.js"></script>
+<script src="bower_components/bootstrap/js/popover.js"></script>
+<script src="bower_components/bootstrap/js/carousel.js"></script>
+<script src="bower_components/bootstrap/js/scrollspy.js"></script>
+<script src="bower_components/bootstrap/js/collapse.js"></script>
+<script src="bower_components/bootstrap/js/tab.js"></script>
 
-    <!-- endbuild -->
+<!-- endbuild -->
 
-    <!-- build:js scripts/main.js -->
-    <script src="scripts/app.js"></script>
-    <script src="scripts/routes.js"></script>
-    <script src="scripts/i18n.config.js"></script>
-    <script src="scripts/controllers/mainCtrl.js"></script>
-    <script src="scripts/controllers/NavbarCtrl.js"></script>
-    <script src="scripts/controllers/authentication/AuthenticationMainCtrl.js"></script>
-    <script src="scripts/controllers/loginActivities/LoginActivitiesMainCtrl.js"></script>
-    <script src="scripts/controllers/loginActivities/LoginMessageMainCtrl.js"></script>
-    <script src="scripts/controllers/loginActivities/HomeDirectoryCtrl.js"></script>
-    <script src="scripts/controllers/users/UsersCreateCtrl.js"></script>
-    <script src="scripts/controllers/users/UsersListCtrl.js"></script>
-    <script src="scripts/controllers/users/UsersShowCtrl.js"></script>
-    <script src="scripts/controllers/groups/GroupsListCtrl.js"></script>
-    <script src="scripts/controllers/groups/GroupsCreateCtrl.js"></script>
-    <script src="scripts/controllers/groups/GroupsEditCtrl.js"></script>
-    <script src="scripts/controllers/ambariViews/ViewsListCtrl.js"></script>
-    <script src="scripts/controllers/ambariViews/ViewsEditCtrl.js"></script>
-    <script src="scripts/controllers/ambariViews/ViewUrlCtrl.js"></script>
-    <script src="scripts/controllers/ambariViews/ViewUrlEditCtrl.js"></script>
-    <script src="scripts/controllers/ambariViews/CreateViewInstanceCtrl.js"></script>
-    <script src="scripts/controllers/clusters/ClustersManageAccessCtrl.js"></script>
-    <script src="scripts/controllers/clusters/UserAccessListCtrl.js"></script>
-    <script src="scripts/controllers/stackVersions/StackVersionsCreateCtrl.js"></script>
-    <script src="scripts/controllers/stackVersions/StackVersionsListCtrl.js"></script>
-    <script src="scripts/controllers/stackVersions/StackVersionsEditCtrl.js"></script>
-    <script src="scripts/directives/linkToDir.js"></script>
-    <script src="scripts/directives/PasswordVerify.js"></script>
-    <script src="scripts/directives/disabledTooltip.js"></script>
-    <script src="scripts/directives/editableList.js"></script>
-    <script src="scripts/services/UserConstants.js"></script>
-    <script src="scripts/services/User.js"></script>
-    <script src="scripts/services/Group.js"></script>
-    <script src="scripts/services/View.js"></script>
-    <script src="scripts/services/Cluster.js"></script>
-    <script src="scripts/services/Alert.js"></script>
-    <script src="scripts/services/PermissionLoader.js"></script>
-    <script src="scripts/services/PermissionsSaver.js"></script>
-    <script src="scripts/services/ConfirmationModal.js"></script>
-    <script src="scripts/services/Auth.js"></script>
-    <script src="scripts/services/GetDifference.js"></script>
-    <script src="scripts/services/UnsavedDialog.js"></script>
-    <script src="scripts/services/Stack.js"></script>
-    <script src="scripts/services/AddRepositoryModal.js"></script>
-    <script src="scripts/services/RoleDetailsModal.js"></script>
-    <!-- endbuild -->
+<!-- build:js scripts/main.js -->
+<script src="scripts/app.js"></script>
+<script src="scripts/routes.js"></script>
+<script src="scripts/i18n.config.js"></script>
+<script src="scripts/controllers/mainCtrl.js"></script>
+<script src="scripts/controllers/NavbarCtrl.js"></script>
+<script src="scripts/controllers/authentication/AuthenticationMainCtrl.js"></script>
+<script src="scripts/controllers/loginActivities/LoginActivitiesMainCtrl.js"></script>
+<script src="scripts/controllers/loginActivities/LoginMessageMainCtrl.js"></script>
+<script src="scripts/controllers/loginActivities/HomeDirectoryCtrl.js"></script>
+<script src="scripts/controllers/users/UsersCreateCtrl.js"></script>
+<script src="scripts/controllers/users/UsersListCtrl.js"></script>
+<script src="scripts/controllers/users/UsersShowCtrl.js"></script>
+<script src="scripts/controllers/groups/GroupsListCtrl.js"></script>
+<script src="scripts/controllers/groups/GroupsCreateCtrl.js"></script>
+<script src="scripts/controllers/groups/GroupsEditCtrl.js"></script>
+<script src="scripts/controllers/ambariViews/ViewsListCtrl.js"></script>
+<script src="scripts/controllers/ambariViews/ViewsEditCtrl.js"></script>
+<script src="scripts/controllers/ambariViews/ViewUrlCtrl.js"></script>
+<script src="scripts/controllers/ambariViews/ViewUrlEditCtrl.js"></script>
+<script src="scripts/controllers/ambariViews/CreateViewInstanceCtrl.js"></script>
+<script src="scripts/controllers/clusters/ClustersManageAccessCtrl.js"></script>
+<script src="scripts/controllers/clusters/UserAccessListCtrl.js"></script>
+<script src="scripts/controllers/stackVersions/StackVersionsCreateCtrl.js"></script>
+<script src="scripts/controllers/stackVersions/StackVersionsListCtrl.js"></script>
+<script src="scripts/controllers/stackVersions/StackVersionsEditCtrl.js"></script>
+<script src="scripts/controllers/remoteClusters/RemoteClustersCreateCtrl.js"></script>
+<script src="scripts/controllers/remoteClusters/RemoteClustersListCtrl.js"></script>
+<script src="scripts/controllers/remoteClusters/RemoteClustersEditCtrl.js"></script>
+<script src="scripts/directives/linkToDir.js"></script>
+<script src="scripts/directives/PasswordVerify.js"></script>
+<script src="scripts/directives/disabledTooltip.js"></script>
+<script src="scripts/directives/editableList.js"></script>
+<script src="scripts/services/UserConstants.js"></script>
+<script src="scripts/services/User.js"></script>
+<script src="scripts/services/Group.js"></script>
+<script src="scripts/services/RemoteCluster.js"></script>
+<script src="scripts/services/View.js"></script>
+<script src="scripts/services/Cluster.js"></script>
+<script src="scripts/services/Alert.js"></script>
+<script src="scripts/services/PermissionLoader.js"></script>
+<script src="scripts/services/PermissionsSaver.js"></script>
+<script src="scripts/services/ConfirmationModal.js"></script>
+<script src="scripts/services/Auth.js"></script>
+<script src="scripts/services/GetDifference.js"></script>
+<script src="scripts/services/UnsavedDialog.js"></script>
+<script src="scripts/services/Stack.js"></script>
+<script src="scripts/services/AddRepositoryModal.js"></script>
+<script src="scripts/services/RoleDetailsModal.js"></script>
+<!-- endbuild -->
 </body>
 </html>

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/CreateViewInstanceCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/CreateViewInstanceCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/CreateViewInstanceCtrl.js
index 127bc74..034b113 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/CreateViewInstanceCtrl.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/CreateViewInstanceCtrl.js
@@ -18,7 +18,7 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('CreateViewInstanceCtrl',['$scope', 'View', 'Alert', 'Cluster', '$routeParams', '$location', 'UnsavedDialog', '$translate', function($scope, View, Alert, Cluster, $routeParams, $location, UnsavedDialog, $translate) {
+.controller('CreateViewInstanceCtrl',['$scope', 'View','RemoteCluster' , 'Alert', 'Cluster', '$routeParams', '$location', 'UnsavedDialog', '$translate', function($scope, View, RemoteCluster, Alert, Cluster, $routeParams, $location, UnsavedDialog, $translate) {
   var $t = $translate.instant;
   $scope.form = {};
   $scope.constants = {
@@ -54,9 +54,10 @@ angular.module('ambariAdminConsole')
         icon64_path: '',
         properties: parameters,
         description: '',
-        isLocalCluster: false
+        clusterType: 'NONE'
       };
       loadClusters();
+      loadRemoteClusters();
     });
   }
 
@@ -91,26 +92,46 @@ angular.module('ambariAdminConsole')
   $scope.clusterConfigurable = false;
   $scope.clusterConfigurableErrorMsg = "";
   $scope.clusters = [];
-  $scope.noClusterAvailible = true;
+  $scope.remoteClusters = [];
+  $scope.noLocalClusterAvailible = true;
+  $scope.noRemoteClusterAvailible = true;
   $scope.cluster = null;
+  $scope.data = {};
+  $scope.data.remoteCluster = null;
   $scope.numberOfClusterConfigs = 0;
   $scope.numberOfSettingConfigs = 0;
 
-  function loadClusters () {
-    Cluster.getAllClusters().then(function (clusters) {
-      if(clusters.length >0){
-        clusters.forEach(function(cluster) {
-          $scope.clusters.push(cluster.Clusters.cluster_name)
-        });
-        $scope.noClusterAvailible = false;
-        $scope.instance.isLocalCluster = $scope.clusterConfigurable;
-      }else{
-        $scope.clusters.push($t('common.noClusters'));
-      }
-      $scope.cluster = $scope.clusters[0];
-    });
+  function loadClusters() {
+       Cluster.getAllClusters().then(function (clusters) {
+         if(clusters.length >0){
+           clusters.forEach(function(cluster) {
+             $scope.clusters.push(cluster.Clusters.cluster_name)
+           });
+           $scope.noLocalClusterAvailible = false;
+           if($scope.clusterConfigurable){
+             $scope.instance.clusterType = "LOCAL_AMBARI";
+           }
+         }else{
+           $scope.clusters.push($t('common.noClusters'));
+         }
+         $scope.cluster = $scope.clusters[0];
+       });
   }
 
+   function loadRemoteClusters() {
+         RemoteCluster.listAll().then(function (clusters) {
+           if(clusters.length >0){
+             clusters.forEach(function(cluster) {
+               $scope.remoteClusters.push(cluster.ClusterInfo.name)
+             });
+             $scope.noRemoteClusterAvailible = false;
+           }else{
+             $scope.remoteClusters.push($t('common.noClusters'));
+           }
+           $scope.data.remoteCluster = $scope.remoteClusters[0];
+         });
+   }
+
 
   $scope.versions = [];
   $scope.version = null;
@@ -128,7 +149,20 @@ angular.module('ambariAdminConsole')
     $scope.form.instanceCreateForm.submitted = true;
     if($scope.form.instanceCreateForm.$valid){
       $scope.form.instanceCreateForm.isSaving = true;
-      $scope.instance.clusterName = $scope.cluster;
+
+      switch($scope.instance.clusterType) {
+        case 'LOCAL_AMBARI':
+          console.log($scope.cluster);
+          $scope.instance.clusterName = $scope.cluster;
+          break;
+        case 'REMOTE_AMBARI':
+          console.log($scope.data.remoteCluster);
+          $scope.instance.clusterName = $scope.data.remoteCluster;
+          break;
+        default:
+          $scope.instance.clusterName = null;
+      }
+      console.log($scope.instance.clusterName);
       View.createInstance($scope.instance)
         .then(function(data) {
           Alert.success($t('views.alerts.instanceCreated', {instanceName: $scope.instance.instance_name}));
@@ -145,7 +179,7 @@ angular.module('ambariAdminConsole')
           var errorMessage = data.message;
           var showGeneralError = true;
 
-          if (data.status >= 400 && !$scope.instance.isLocalCluster) {
+          if (data.status >= 400 && $scope.instance.clusterType == 'NONE') {
             try {
               var errorObject = JSON.parse(errorMessage);
               errorMessage = errorObject.detail;

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js
index 480f85d..b4a9656 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js
@@ -18,7 +18,7 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-  .controller('ViewsEditCtrl', ['$scope', '$routeParams' , 'Cluster', 'View', 'Alert', 'PermissionLoader', 'PermissionSaver', 'ConfirmationModal', '$location', 'UnsavedDialog', '$translate', function($scope, $routeParams, Cluster, View, Alert, PermissionLoader, PermissionSaver, ConfirmationModal, $location, UnsavedDialog, $translate) {
+  .controller('ViewsEditCtrl', ['$scope', '$routeParams', 'RemoteCluster', 'Cluster', 'View', 'Alert', 'PermissionLoader', 'PermissionSaver', 'ConfirmationModal', '$location', 'UnsavedDialog', '$translate', function($scope, $routeParams, RemoteCluster, Cluster, View, Alert, PermissionLoader, PermissionSaver, ConfirmationModal, $location, UnsavedDialog, $translate) {
     var $t = $translate.instant;
     $scope.identity = angular.identity;
     $scope.isConfigurationEmpty = true;
@@ -62,14 +62,16 @@ angular.module('ambariAdminConsole')
     }
 
     function initCtrlVariables(instance) {
-      if(instance.ViewInstanceInfo.cluster_handle) {
-        $scope.isLocalCluster = true;
-        $scope.cluster = instance.ViewInstanceInfo.cluster_handle;
-      }else{
-        $scope.isLocalCluster = false;
-        $scope.cluster = $scope.clusters.length > 0 ? $scope.clusters[0] : $t('common.noClusters');
-      }
-      $scope.originalLocalCluster = $scope.isLocalCluster;
+       $scope.data.clusterType = instance.ViewInstanceInfo.cluster_type;
+       switch($scope.data.clusterType) {
+          case 'LOCAL_AMBARI':
+            $scope.cluster = instance.ViewInstanceInfo.cluster_handle;
+            break;
+          case 'REMOTE_AMBARI':
+            $scope.data.remoteCluster = instance.ViewInstanceInfo.cluster_handle;
+            break;
+       }
+      $scope.originalClusterType = $scope.data.clusterType;
       $scope.isConfigurationEmpty = !$scope.numberOfClusterConfigs;
       $scope.isSettingsEmpty = !$scope.numberOfSettingsConfigs;
     }
@@ -101,7 +103,7 @@ angular.module('ambariAdminConsole')
 
     function filterClusterConfigs() {
       $scope.configurationMeta.forEach(function (element) {
-        if (element.masked && !$scope.editConfigurationDisabled && element.clusterConfig && !$scope.isLocalCluster) {
+        if (element.masked && !$scope.editConfigurationDisabled && element.clusterConfig && $scope.data.clusterType == 'NONE') {
           $scope.configuration[element.name] = '';
         }
         if(!element.clusterConfig) {
@@ -150,9 +152,13 @@ angular.module('ambariAdminConsole')
     $scope.clusterConfigurable = false;
     $scope.clusterConfigurableErrorMsg = "";
     $scope.clusters = [];
+    $scope.remoteClusters = [];
     $scope.cluster = null;
-    $scope.noClusterAvailible = true;
-
+    $scope.noLocalClusterAvailible = true;
+    $scope.noRemoteClusterAvailible = true;
+    $scope.data = {};
+    $scope.data.remoteCluster = null;
+    $scope.data.clusterType = 'NONE';
 
     $scope.editSettingsDisabled = true;
     $scope.editDetailsSettingsDisabled = true;
@@ -199,13 +205,29 @@ angular.module('ambariAdminConsole')
         clusters.forEach(function(cluster) {
           $scope.clusters.push(cluster.Clusters.cluster_name)
         });
-        $scope.noClusterAvailible = false;
+        $scope.noLocalClusterAvailible = false;
       }else{
         $scope.clusters.push($t('common.noClusters'));
       }
       $scope.cluster = $scope.clusters[0];
     });
 
+    loadRemoteClusters();
+
+    function loadRemoteClusters() {
+      RemoteCluster.listAll().then(function (clusters) {
+        if(clusters.length >0){
+          clusters.forEach(function(cluster) {
+            $scope.remoteClusters.push(cluster.ClusterInfo.name)
+          });
+          $scope.noRemoteClusterAvailible = false;
+          }else{
+            $scope.remoteClusters.push($t('common.noClusters'));
+          }
+          $scope.data.remoteCluster = $scope.remoteClusters[0];
+       });
+     }
+
 
     $scope.saveSettings = function(callback) {
       if( $scope.settingsForm.$valid ){
@@ -292,18 +314,28 @@ angular.module('ambariAdminConsole')
             'properties':{}
           }
         };
-        if($scope.isLocalCluster) {
-          data.ViewInstanceInfo.cluster_handle = $scope.cluster;
-        } else {
-          data.ViewInstanceInfo.cluster_handle = null;
-          $scope.configurationMeta.forEach(function (element) {
-            if(element.clusterConfig) {
-              data.ViewInstanceInfo.properties[element.name] = $scope.configuration[element.name];
-            }
-          });
-          $scope.clearClusterInheritedPermissions();
-        }
-        $scope.originalLocalCluster = $scope.isLocalCluster;
+
+        data.ViewInstanceInfo.cluster_type = $scope.data.clusterType;
+
+        switch($scope.data.clusterType) {
+          case 'LOCAL_AMBARI':
+            data.ViewInstanceInfo.cluster_handle = $scope.cluster;
+            break;
+          case 'REMOTE_AMBARI':
+            data.ViewInstanceInfo.cluster_handle = $scope.data.remoteCluster;
+            break;
+          default :
+            data.ViewInstanceInfo.cluster_handle = null;
+            $scope.configurationMeta.forEach(function (element) {
+              if(element.clusterConfig) {
+                data.ViewInstanceInfo.properties[element.name] = $scope.configuration[element.name];
+              }
+            });
+            $scope.clearClusterInheritedPermissions();
+
+          }
+
+        $scope.originalClusterType = $scope.data.clusterType;
         return View.updateInstance($routeParams.viewId, $routeParams.version, $routeParams.instanceId, data)
           .success(function() {
             $scope.editConfigurationDisabled = true;
@@ -315,7 +347,7 @@ angular.module('ambariAdminConsole')
             //TODO: maybe the BackEnd should sanitize the string beforehand?
             errorMessage = errorMessage.substr(errorMessage.indexOf("\{"));
 
-            if (data.status >= 400 && !$scope.isLocalCluster) {
+            if (data.status >= 400 && $scope.data.clusterType == 'NONE') {
               try {
                 var errorObject = JSON.parse(errorMessage);
                 errorMessage = errorObject.detail;
@@ -335,7 +367,7 @@ angular.module('ambariAdminConsole')
     };
     $scope.cancelConfiguration = function() {
       angular.extend($scope.configuration, $scope.configurationBeforeEdit);
-      $scope.isLocalCluster = $scope.originalLocalCluster;
+      $scope.data.clusterType = $scope.originalClusterType;
       $scope.editConfigurationDisabled = true;
       $scope.propertiesForm.$setPristine();
     };

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersCreateCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersCreateCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersCreateCtrl.js
new file mode 100644
index 0000000..e669c82
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersCreateCtrl.js
@@ -0,0 +1,63 @@
+/**
+ * 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.
+ */
+'use strict';
+
+angular.module('ambariAdminConsole')
+.controller('RemoteClustersCreateCtrl', ['$scope', '$routeParams', '$location', 'Alert', '$translate', 'Cluster', 'AddRepositoryModal' , 'Settings', 'RemoteCluster', function($scope, $routeParams, $location, Alert, $translate, Cluster, AddRepositoryModal, Settings, RemoteCluster) {
+  var $t = $translate.instant;
+
+  $scope.cluster = {};
+
+  $scope.registerRemoteCluster = function () {
+    $scope.form.submitted = true;
+    if ($scope.form.$valid){
+     var payload = {
+        "ClusterInfo" :{
+          "name" : $scope.cluster.cluster_name,
+          "url" : $scope.cluster.cluster_url,
+          "username" : $scope.cluster.cluster_user,
+          "password" : $scope.cluster.cluster_password
+        }
+      };
+
+      var config = {
+        headers : {
+          'X-Requested-By': 'Ambari;'
+        }
+      }
+
+      RemoteCluster.register(payload, config).then(function(data) {
+          Alert.success($t('common.alerts.remoteClusterRegistered', {clusterName: payload.ClusterInfo.name}));
+          $scope.form.$setPristine();
+          $location.path('/remoteClusters/'+ $scope.cluster.cluster_name +'/edit')
+        })
+        .catch(function(data) {
+          console.log(data);
+          Alert.error(data.message);
+       });
+
+    }
+  };
+
+  $scope.cancel = function () {
+    $scope.editVersionDisabled = true;
+    $location.path('/remoteClusters');
+  };
+
+
+}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersEditCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersEditCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersEditCtrl.js
new file mode 100644
index 0000000..20eb552
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersEditCtrl.js
@@ -0,0 +1,159 @@
+/**
+ * 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.
+ */
+'use strict';
+
+angular.module('ambariAdminConsole')
+.controller('RemoteClustersEditCtrl', ['$scope', '$modal', '$routeParams', '$location', 'Alert', '$translate', 'Cluster', 'Settings','RemoteCluster', 'ConfirmationModal', function($scope, $modal, $routeParams, $location, Alert, $translate, Cluster, Settings, RemoteCluster, ConfirmationModal) {
+  var $t = $translate.instant;
+
+  $scope.cluster = {};
+
+  $scope.openChangePwdDialog = function() {
+    var modalInstance = $modal.open({
+      templateUrl: 'views/remoteClusters/modals/changePassword.html',
+      resolve: {
+        clusterName: function() {
+          return $scope.cluster.cluster_name;
+        },
+        clusterUrl: function() {
+          return $scope.cluster.cluster_url;
+        },
+        clusterUser: function() {
+          return $scope.cluster.cluster_user;
+        }
+      },
+      controller: ['$scope', 'clusterName', 'clusterUrl', 'clusterUser', 'Settings','Alert',  function($scope, clusterName, clusterUrl , clusterUser , Settings, Alert) {
+        $scope.passwordData = {
+          password: '',
+          currentUserName: clusterUser || ''
+        };
+
+        $scope.form = {};
+
+        $scope.currentUser = clusterUser;
+        $scope.clusterName = clusterName;
+        $scope.clusterUrl = clusterUrl;
+
+        $scope.ok = function() {
+          $scope.form.passwordChangeForm.submitted = true;
+
+
+          if ($scope.form.passwordChangeForm.$valid){
+
+            var payload = {
+              "ClusterInfo" :{
+                "name" : $scope.clusterName,
+                "url" : $scope.clusterUrl,
+                "username" : $scope.passwordData.currentUserName,
+                "password" : $scope.passwordData.password //This field will go once backend API changes are done.
+              }
+            };
+
+            var config = {
+              headers : {
+                'X-Requested-By': 'Ambari;'
+              }
+            }
+
+            RemoteCluster.edit(payload, config).then(function(data) {
+                Alert.success($t('views.alerts.credentialsUpdated'));
+                $scope.form.passwordChangeForm.$setPristine();
+              })
+              .catch(function(data) {
+                console.log(data);
+                Alert.error(data.message);
+              });
+
+            modalInstance.dismiss('cancel');
+          }
+
+        };
+        $scope.cancel = function() {
+          modalInstance.dismiss('cancel');
+        };
+      }]
+    });
+  };
+
+  $scope.deleteCluster = function() {
+      ConfirmationModal.show(
+        $t('common.deregisterCluster', {
+          term: $t('common.cluster')
+        }),
+        $t('common.deleteConfirmation', {
+          instanceType: $t('common.cluster').toLowerCase(),
+          instanceName: '"' + $scope.cluster.cluster_name + '"'
+        })
+      ).then(function() {
+        RemoteCluster.deregister($scope.cluster.cluster_name).then(function() {
+          $location.path('/remoteClusters');
+        });
+      });
+  };
+
+  $scope.editRemoteCluster = function () {
+    $scope.form.submitted = true;
+    if ($scope.form.$valid){
+      var payload = {
+        "ClusterInfo" :{
+          "name" : $scope.cluster.cluster_name,
+          "url" : $scope.cluster.cluster_url,
+          "username" : $scope.cluster.cluster_user
+        }
+      };
+
+      var config = {
+        headers : {
+          'X-Requested-By': 'Ambari;'
+        }
+      }
+
+      RemoteCluster.edit(payload, config).then(function(data) {
+          Alert.success($t('views.alerts.savedRemoteClusterInformation'));
+          $scope.form.$setPristine();
+        })
+        .catch(function(data) {
+          console.log(data);
+          Alert.error(data.message);
+        });
+    }
+  };
+
+  $scope.cancel = function () {
+    $scope.editVersionDisabled = true;
+    $location.path('/remoteClusters');
+  };
+
+  // Fetch remote cluster details
+  $scope.fetchRemoteClusterDetails = function (clusterName) {
+
+    RemoteCluster.getDetails(clusterName).then(function(response) {
+        $scope.cluster.cluster_name = response.ClusterInfo.name
+        $scope.cluster.cluster_url = response.ClusterInfo.url;
+        $scope.cluster.cluster_user = response.ClusterInfo.username;
+      })
+      .catch(function(data) {
+        console.log(data);
+      });
+
+  };
+
+  $scope.fetchRemoteClusterDetails($routeParams.clusterName);
+
+
+}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersListCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersListCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersListCtrl.js
new file mode 100644
index 0000000..4eb3633
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersListCtrl.js
@@ -0,0 +1,106 @@
+/**
+ * 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.
+ */
+'use strict';
+
+angular.module('ambariAdminConsole')
+.controller('RemoteClustersListCtrl', ['$scope', '$routeParams', '$translate', 'RemoteCluster' ,'UserConstants', function ($scope, $routeParams, $translate, RemoteCluster, UserConstants) {
+  var $t = $translate.instant;
+
+  $scope.clusterName = $routeParams.clusterName;
+
+  $scope.constants = {
+    groups: $t('common.clusters').toLowerCase()
+  };
+
+  $scope.groupsPerPage = 10;
+  $scope.currentPage = 1;
+  $scope.totalGroups = 1;
+  $scope.currentNameFilter = '';
+  $scope.maxVisiblePages=20;
+  $scope.tableInfo = {
+    total: 0,
+    showed: 0
+  };
+
+  $scope.isNotEmptyFilter = true;
+
+  $scope.pageChanged = function() {
+    loadRemoteClusters();
+  };
+  $scope.groupsPerPageChanges = function() {
+    loadRemoteClusters();
+  };
+
+  $scope.resetPagination = function() {
+    $scope.currentPage = 1;
+    loadRemoteClusters();
+  };
+
+  $scope.typeFilterOptions = [
+    $t('common.any')
+  ];
+
+  $scope.currentTypeFilter = $scope.typeFilterOptions[0];
+
+  $scope.clearFilters = function () {
+    $scope.currentNameFilter = '';
+    $scope.currentTypeFilter = $scope.typeFilterOptions[0];
+    $scope.resetPagination();
+  };
+
+  function loadRemoteClusters(){
+      RemoteCluster.all({
+        currentPage: $scope.currentPage,
+        groupsPerPage: $scope.groupsPerPage,
+        searchString: $scope.currentNameFilter,
+        service: $scope.currentTypeFilter
+      }).then(function(remoteclusters) {
+
+        $scope.totalGroups = remoteclusters.itemTotal;
+        $scope.tableInfo.total = remoteclusters.itemTotal;
+        $scope.tableInfo.showed = remoteclusters.items.length;
+
+        $scope.remoteClusters = remoteclusters.items;
+
+        remoteclusters.items.map(function(clusteritem){
+          clusteritem.ClusterInfo.services.map(function(service){
+            var serviceIndex = $scope.typeFilterOptions.indexOf(service);
+            if(serviceIndex == -1){
+              $scope.typeFilterOptions.push(service);
+            }
+          })
+        })
+
+      })
+      .catch(function(data) {
+        console.error('Error in fetching remote clusters.', data);
+      });
+  };
+
+  loadRemoteClusters();
+
+  $scope.$watch(
+    function (scope) {
+      return Boolean(scope.currentNameFilter || (scope.currentTypeFilter));
+    },
+    function (newValue, oldValue, scope) {
+      scope.isNotEmptyFilter = newValue;
+    }
+  );
+
+}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
index 80a5352..c74ed77 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
@@ -30,6 +30,7 @@ angular.module('ambariAdminConsole')
       'about': 'About',
       'version': 'Version',
       'signOut': 'Sign out',
+      'register':'Register',
       'clusters': 'Clusters',
       'views': 'Views',
       'viewUrls': 'View URLs',
@@ -50,9 +51,12 @@ angular.module('ambariAdminConsole')
       'type': 'Type',
       'add': 'Add {{term}}',
       'delete': 'Delete {{term}}',
+      'deregisterCluster': 'Deregister Cluster',
       'cannotDelete': 'Cannot Delete {{term}}',
       'privileges': 'Privileges',
       'cluster': 'Cluster',
+      'remoteClusters': 'Remote Clusters',
+      'services':'Services',
       'clusterRole': 'Cluster Role',
       'viewPermissions': 'View Permissions',
       'getInvolved': 'Get involved!',
@@ -126,6 +130,7 @@ angular.module('ambariAdminConsole')
         'fieldIsRequired': 'This field is required.',
         'noSpecialChars': 'Must not contain special characters!',
         'nothingToDisplay': 'No {{term}} to display.',
+        'noRemoteClusterDisplay':'No Remote Clusters to display.',
         'noPrivileges': 'No {{term}} privileges',
         'noPrivilegesDescription': 'This {{term}} does not have any privileges.',
         'timeOut': 'You will be automatically logged out in <b>{{time}}</b> seconds due to inactivity.',
@@ -134,11 +139,13 @@ angular.module('ambariAdminConsole')
         'cannotLoadPrivileges': 'Cannot load privileges',
         'cannotLoadClusterStatus': 'Cannot load cluster status',
         'clusterRenamed': 'The cluster has been renamed to {{clusterName}}.',
+        'remoteClusterRegistered': 'The cluster has been registered as {{clusterName}}.',
         'cannotRenameCluster': 'Cannot rename cluster to {{clusterName}}',
         'tooShort': 'Too short',
         'tooLong': 'Too long',
         'onlyText': 'Only lowercase alphanumeric characters are allowed.',
-        'onlyAnScore': 'Invalid input, only alphanumerics allowed eg: My_default_view'
+        'onlyAnScore': 'Invalid input, only alphanumerics allowed eg: My_default_view',
+        'passwordRequired':'Password Required'
       }
     },
 
@@ -195,6 +202,8 @@ angular.module('ambariAdminConsole')
       'instanceDescription': 'Instance Description',
       'clusterConfiguration': 'Cluster Configuration',
       'localCluster': 'Local Ambari Managed Cluster',
+      'remoteCluster': 'Remote Cluster',
+      'registerRemoteCluster' : 'Register Remote Cluster',
       'clusterName': 'Cluster Name',
       'custom': 'Custom',
       'icon': 'Icon',
@@ -241,7 +250,9 @@ angular.module('ambariAdminConsole')
         'cannotDeleteInstance': 'Cannot delete instance',
         'cannotLoadViews': 'Cannot load views',
         'cannotLoadViewUrls': 'Cannot load view URLs',
-        'cannotLoadViewUrl': 'Cannot load view URL'
+        'cannotLoadViewUrl': 'Cannot load view URL',
+        'savedRemoteClusterInformation':'Remote cluster information is saved.',
+        'credentialsUpdated':'Credentials Updated.'
       }
     },
 
@@ -296,7 +307,9 @@ angular.module('ambariAdminConsole')
       'userName': 'User name',
       'admin': 'Admin',
       'ambariAdmin': 'Ambari Admin',
+      'ambariClusterURL':'Ambari Cluster URL',
       'changePassword': 'Change Password',
+      'updateCredentials':'Update Credentials',
       'changePasswordFor': 'Change Password for {{userName}}',
       'yourPassword': 'Your Password',
       'newPassword': 'New User Password',
@@ -335,8 +348,9 @@ angular.module('ambariAdminConsole')
       },
 
       'alerts': {
-        'passwordRequired': 'Password required!',
+        'passwordRequired': 'Password required',
         'wrongPassword': 'Password must match!',
+        'usernameRequired':'Username Required',
         'cannotChange': 'Cannot Change {{term}}',
         'userCreated': 'Created user <a href="#/users/{{encUserName}}">{{userName}}</a>',
         'userCreationError': 'User creation error',

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js
index 0566969..81b4d28 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js
@@ -136,6 +136,23 @@ angular.module('ambariAdminConsole')
       controller: 'StackVersionsEditCtrl'
     }
   },
+  remoteClusters: {
+    list: {
+      url: '/remoteClusters',
+      templateUrl: 'views/remoteClusters/list.html',
+      controller: 'RemoteClustersListCtrl'
+    },
+    create: {
+      url: '/remoteClusters/create',
+      templateUrl: 'views/remoteClusters/remoteClusterPage.html',
+      controller: 'RemoteClustersCreateCtrl'
+    },
+     edit: {
+     url: '/remoteClusters/:clusterName/edit',
+     templateUrl: 'views/remoteClusters/editRemoteClusterPage.html',
+     controller: 'RemoteClustersEditCtrl'
+     }
+  },
   clusters: {
     manageAccess: {
       url: '/clusters/:id/manageAccess',

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/RemoteCluster.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/RemoteCluster.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/RemoteCluster.js
new file mode 100644
index 0000000..e4826e6
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/RemoteCluster.js
@@ -0,0 +1,118 @@
+/**
+ * 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.
+ */
+'use strict';
+
+angular.module('ambariAdminConsole')
+  .factory('RemoteCluster', ['$http', '$q', 'Settings', function($http, $q, Settings) {
+
+    function RemoteCluster(){
+    }
+
+    RemoteCluster.edit = function(payload, config){
+      var deferred = $q.defer();
+
+      $http.put(Settings.baseUrl + '/remoteclusters/' + payload.ClusterInfo.name , payload, config)
+        .success(function (data) {
+          deferred.resolve(data)
+        })
+        .error(function (data) {
+          deferred.reject(data);
+        });
+      return deferred.promise;
+    }
+
+
+    RemoteCluster.getDetails = function(clusterName) {
+      var deferred = $q.defer();
+
+      $http.get( Settings.baseUrl  + '/remoteclusters/' + clusterName)
+        .success(function(response) {
+          deferred.resolve(response);
+        })
+        .error(function(data) {
+          deferred.reject(data);
+        });
+
+      return deferred.promise;
+
+    };
+
+    RemoteCluster.deregister = function(clusterName){
+      var deferred = $q.defer();
+
+      $http.delete( Settings.baseUrl  + '/remoteclusters/' + clusterName)
+        .success(function(response) {
+          deferred.resolve(response);
+        })
+        .error(function(data) {
+          deferred.reject(data);
+        });
+
+      return deferred.promise;
+
+    };
+
+    RemoteCluster.register = function(payload, config){
+      var deferred = $q.defer();
+
+      $http.post(Settings.baseUrl + '/remoteclusters/' + payload.ClusterInfo.name , payload, config)
+        .success(function (data) {
+          deferred.resolve(data)
+        })
+        .error(function (data) {
+          deferred.reject(data);
+        });
+        return deferred.promise;
+    }
+
+    RemoteCluster.all = function(params) {
+      var deferred = $q.defer();
+
+      $http.get(Settings.baseUrl + "/remoteclusters?"
+          + 'ClusterInfo/name.matches(.*'+params.searchString+'.*)'
+          + '&fields=*'
+          + '&from='+ (params.currentPage-1)*params.groupsPerPage
+          + '&page_size=' + params.groupsPerPage
+          + (params.service === 'Any' ? '' : '&ClusterInfo/services.matches(.*'+params.service+'.*)')
+        )
+        .success(function(response) {
+          deferred.resolve(response);
+        })
+        .error(function(data) {
+          deferred.reject(data);
+        });
+      return deferred.promise;
+    };
+
+    RemoteCluster.listAll = function() {
+      var deferred = $q.defer();
+
+      /* TODO :: Add params like RemoteCluster.matches and &from , &page_size */
+      $http.get(Settings.baseUrl + "/remoteclusters?fields=ClusterInfo/services")
+        .success(function(response) {
+          deferred.resolve(response.items);
+        })
+        .error(function(data) {
+          deferred.reject(data);
+        });
+      return deferred.promise;
+    };
+
+    return RemoteCluster;
+
+  }]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/View.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/View.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/View.js
index 780352d..dfe7ba8 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/View.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/View.js
@@ -325,8 +325,9 @@ angular.module('ambariAdminConsole')
     });
 
     data.properties = settings;
+    data.cluster_type = instanceInfo.clusterType;
 
-    if(instanceInfo.isLocalCluster) {
+    if(instanceInfo.clusterName != null) {
       data.cluster_handle = instanceInfo.clusterName;
     } else {
       angular.extend(data.properties, properties);

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css b/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
index c573b72..fbad224 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
@@ -1626,6 +1626,31 @@ thead.view-permission-header > tr > th {
   margin:0px;
 }
 
+.proxied-field-wrap {
+  display:block;
+  position:absolute;
+  height:0;
+  width:0;
+  overflow:hidden;
+}
+
+legend.legendStyle {
+  padding-left: 5px;
+  padding-right: 5px;
+  font-size: 16px;
+}
+
+fieldset.fsStyle {
+  border: 1px solid #DDD;
+  padding: 4px;
+  margin: 15px 0;
+}
+
+legend {
+  width: auto;
+  border-bottom: 0;
+}
+
 .cluster-inherited-permission {
     border-bottom: 2px solid #ddd;
     padding-bottom: 10px;

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/create.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/create.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/create.html
index eaff70b..051d720 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/create.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/create.html
@@ -115,7 +115,7 @@
     </div>
   </div>
 
-  <div class="panel panel-default" ng-hide="!numberOfClusterConfigs">
+  <div class="panel panel-default" ng-hide="!numberOfClusterConfigs && !clusterConfigurable">
     <div class="panel-heading">
       <h3 class="panel-title">{{'views.clusterConfiguration' | translate}}</h3>
     </div>
@@ -123,7 +123,7 @@
     <div class="panel-body property-form" popover="{{clusterConfigurableErrorMsg}}" popover-trigger="mouseenter">
       <div class="checkbox create-checkbox-cluster">
         <label>
-          <input type="radio" ng-disabled="!clusterConfigurable || noClusterAvailible" ng-model="instance.isLocalCluster" ng-change="enableLocalCluster()" ng-value="true" class="visibilityCheckbox"> {{'views.localCluster' | translate}}
+          <input type="radio" ng-disabled="!clusterConfigurable || noLocalClusterAvailible" ng-model="instance.clusterType" ng-change="enableLocalCluster()" value="LOCAL_AMBARI" class="visibilityCheckbox"> {{'views.localCluster' | translate}}
         </label>
       </div>
 
@@ -133,17 +133,39 @@
           <label for="" class="control-label col-sm-3 ng-binding not-required" >{{'views.clusterName' | translate}}</label>
           <div>
             <div class="col-sm-9">
-              <select ng-model="cluster" ng-disabled="!instance.isLocalCluster" ng-change="onClusterChange()" class="clusters-name-dropdown form-control"  ng-options="o as o for o in clusters"></select>
+              <select ng-model="cluster" ng-disabled="instance.clusterType != 'LOCAL_AMBARI' || noLocalClusterAvailible" ng-change="onClusterChange()" class="clusters-name-dropdown form-control"  ng-options="o as o for o in clusters"></select>
             </div>
           </div>
         </div>
       </div>
 
     </div>
-    <div class="panel-body property-form">
+
+    <div class="panel-body property-form" popover="{{clusterConfigurableErrorMsg}}" popover-trigger="mouseenter">
+      <div class="checkbox create-checkbox-cluster">
+        <label>
+          <input type="radio" ng-disabled="!clusterConfigurable || noRemoteClusterAvailible" ng-model="instance.clusterType" ng-change="enableLocalCluster()" value="REMOTE_AMBARI" class="visibilityCheckbox"> {{'views.remoteCluster' | translate}}
+        </label>
+      </div>
+
+      <div class="form-horizontal property-form">
+        <div class="form-group">
+
+          <label for="" class="control-label col-sm-3 ng-binding not-required" >{{'views.clusterName' | translate}}</label>
+          <div>
+            <div class="col-sm-9">
+              <select ng-model="data.remoteCluster" ng-disabled="instance.clusterType != 'REMOTE_AMBARI' || noRemoteClusterAvailable" ng-change="onClusterChange()" class="clusters-name-dropdown form-control"  ng-options="o as o for o in remoteClusters"></select>
+            </div>
+          </div>
+        </div>
+      </div>
+
+    </div>
+
+    <div class="panel-body property-form" ng-hide="!numberOfClusterConfigs">
       <div class="checkbox">
         <label>
-          <input type="radio" ng-model="instance.isLocalCluster" ng-value="false" class="visibilityCheckbox"> {{'views.custom' | translate}}
+          <input type="radio" ng-model="instance.clusterType" value="NONE" class="visibilityCheckbox"> {{'views.custom' | translate}}
         </label>
       </div>
       <div class="alert alert-danger bottom-margin top-margin" ng-show='form.instanceCreateForm.generalValidationError'>
@@ -154,11 +176,11 @@
         <label for="" class="col-sm-3 control-label" ng-class="{'not-required': !parameter.required}">{{parameter.label || parameter.displayName}}{{parameter.required ? '*' : ''}}</label>
         <div ng-switch="parameter.type">
           <div class="col-sm-9 checkbox" ng-switch-when="boolean">
-            <input type="checkbox" class="viewproperty-input" name="{{parameter.name}}" ng-disabled="instance.isLocalCluster" ng-required="parameter.required && instance.isLocalCluster" ng-model="parameter.value" popover="{{parameter.description}}" popover-title="{{parameter.name}}" popover-trigger="mouseenter">
+            <input type="checkbox" class="viewproperty-input" name="{{parameter.name}}" ng-disabled="instance.clusterType == 'NONE'" ng-required="parameter.required && (instance.clusterType == 'NONE')" ng-model="parameter.value" popover="{{parameter.description}}" popover-title="{{parameter.name}}" popover-trigger="mouseenter">
           </div>
           <div class="col-sm-9" ng-switch-default>
-            <input type="{{parameter.masked ? 'password' : 'text'}}" class="form-control viewproperty-input" name="{{parameter.name}}" ng-disabled="instance.isLocalCluster" ng-change="form.instanceCreateForm[parameter.name].validationError=''" ng-required="parameter.required && !instance.isLocalCluster" ng-model="parameter.value" autocomplete="off" popover="{{parameter.description}}" popover-title="{{parameter.name}}" popover-trigger="mouseenter" placeholder="{{parameter.placeholder}}">
-            <div class="alert alert-danger no-margin-bottom top-margin" ng-show='form.instanceCreateForm[parameter.name].$error.required && form.instanceCreateForm.submitted && !instance.isLocalCluster'>
+            <input type="{{parameter.masked ? 'password' : 'text'}}" class="form-control viewproperty-input" name="{{parameter.name}}" ng-disabled="instance.clusterType != 'NONE'" ng-change="form.instanceCreateForm[parameter.name].validationError=''" ng-required="parameter.required && (instance.clusterType == 'NONE')" ng-model="parameter.value" autocomplete="off" popover="{{parameter.description}}" popover-title="{{parameter.name}}" popover-trigger="mouseenter" placeholder="{{parameter.placeholder}}">
+            <div class="alert alert-danger no-margin-bottom top-margin" ng-show="form.instanceCreateForm[parameter.name].$error.required && form.instanceCreateForm.submitted && (instance.clusterType == 'NONE')">
               {{'common.alerts.fieldIsRequired' | translate}}
             </div>
             <div class="alert alert-danger no-margin-bottom top-margin" ng-show='form.instanceCreateForm[parameter.name].validationError'>

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html
index 4003137..92cde74 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html
@@ -158,18 +158,18 @@
   </div>
 </div>
 
-<div class="panel panel-default" ng-hide="isConfigurationEmpty">
+<div class="panel panel-default" ng-hide="isConfigurationEmpty && !clusterConfigurable">
   <div class="panel-heading clearfix">
     <h3 class="panel-title pull-left">{{'views.clusterConfiguration' | translate}}</h3>
     <div class="pull-right" ng-switch="instance.ViewInstanceInfo.static">
-      <a href ng-switch-when="false" ng-hide="isConfigurationEmpty" ng-click="togglePropertiesEditing()" ng-show="editConfigurationDisabled" class="properties-toggle"> <span class="glyphicon glyphicon-pencil"></span> {{'views.edit' | translate}}</a>
-      <a href ng-switch-when="true" ng-hide="isConfigurationEmpty"  class="properties-toggle disabled"> <span class="glyphicon glyphicon-pencil"></span> {{'views.edit' | translate}}</a>
+      <a href ng-switch-when="false" ng-hide="isConfigurationEmpty && !clusterConfigurable" ng-click="togglePropertiesEditing()" ng-show="editConfigurationDisabled" class="properties-toggle"> <span class="glyphicon glyphicon-pencil"></span> {{'views.edit' | translate}}</a>
+      <a href ng-switch-when="true" ng-hide="isConfigurationEmpty && !clusterConfigurable"  class="properties-toggle disabled"> <span class="glyphicon glyphicon-pencil"></span> {{'views.edit' | translate}}</a>
     </div>
   </div>
   <div class="panel-body property-form" popover="{{clusterConfigurableErrorMsg}}" popover-trigger="mouseenter">
     <div class="checkbox">
       <label>
-        <input type="radio" ng-model="$parent.isLocalCluster" ng-change="enableLocalCluster()" ng-disabled="!clusterConfigurable || editConfigurationDisabled || noClusterAvailible" ng-value="true" class="visibilityCheckbox"> {{'views.localCluster' | translate}}
+        <input type="radio" ng-model="data.clusterType" ng-change="enableLocalCluster()" ng-disabled="!clusterConfigurable || editConfigurationDisabled || noLocalClusterAvailible" value="LOCAL_AMBARI" class="visibilityCheckbox"> {{'views.localCluster' | translate}}
       </label>
     </div>
     <div class="form-horizontal property-form">
@@ -177,18 +177,34 @@
         <label for="" class="control-label col-sm-3 ng-binding not-required" >{{'views.clusterName' | translate}}</label>
         <div>
           <div class="col-sm-9">
-            <select ng-model="cluster" ng-disabled="!$parent.isLocalCluster || editConfigurationDisabled" ng-change="onClusterChange()" class="clusters-name-dropdown form-control"  ng-options="o as o for o in clusters"></select>
+            <select ng-model="cluster" ng-disabled="(data.clusterType != 'LOCAL_AMBARI') || editConfigurationDisabled" ng-change="onClusterChange()" class="clusters-name-dropdown form-control"  ng-options="o as o for o in clusters"></select>
           </div>
         </div>
       </div>
     </div>
     <p>&nbsp</p>
-    <div class="checkbox">
+      <div class="checkbox">
+        <label>
+          <input type="radio" ng-model="data.clusterType" ng-change="enableLocalCluster()" ng-disabled="!clusterConfigurable || editConfigurationDisabled || noRemoteClusterAvailible" value="REMOTE_AMBARI" class="visibilityCheckbox"> {{'views.remoteCluster' | translate}}
+        </label>
+      </div>
+      <div class="form-horizontal property-form">
+        <div class="form-group">
+          <label for="" class="control-label col-sm-3 ng-binding not-required" >{{'views.clusterName' | translate}}</label>
+          <div>
+            <div class="col-sm-9">
+              <select ng-model="data.remoteCluster" ng-disabled="(data.clusterType != 'REMOTE_AMBARI') || editConfigurationDisabled" ng-change="onClusterChange()" class="clusters-name-dropdown form-control"  ng-options="o as o for o in remoteClusters"></select>
+            </div>
+          </div>
+        </div>
+      </div>
+      <p>&nbsp</p>
+    <div class="checkbox" ng-hide="isConfigurationEmpty">
       <label>
         <input type="radio"
-         ng-model="$parent.isLocalCluster"
+         ng-model="data.clusterType"
          ng-disabled="editConfigurationDisabled"
-         ng-value="false"
+         value="NONE"
          ng-change="disableLocalCluster()"
          class="visibilityCheckbox">
         {{'views.custom' | translate}}
@@ -196,20 +212,20 @@
     </div>
   </div>
   <div class="panel-body edit-view-custom-wrap">
-    <form name="propertiesForm" class="form-horizontal property-form" ng-hide="isConfigurationEmpty" novalidate>
+    <form name="propertiesForm" class="form-horizontal property-form"  novalidate>
       <fieldset>
-        <div class="form-group" ng-repeat="property in configurationMeta | filter:{clusterConfig:true}" ng-class="{'has-error' : (!editConfigurationDisabled && !isLocalCluster && ((property.required && propertiesForm[property.name].$error.required && !editConfigurationDisabled) || propertiesForm[property.name].validationError))}">
+        <div class="form-group" ng-hide="isConfigurationEmpty" ng-repeat="property in configurationMeta | filter:{clusterConfig:true}" ng-class="{'has-error' : (!editConfigurationDisabled && (data.clusterType == 'NONE') && ((property.required && propertiesForm[property.name].$error.required && !editConfigurationDisabled) || propertiesForm[property.name].validationError))}">
           <label for="" class="control-label col-sm-3" ng-class="{'not-required': !property.required}">{{property.label || property.displayName}}{{property.required ? '*' : ''}}</label>
           <div ng-switch="property.type">
             <div class="col-sm-9 checkbox" ng-switch-when="boolean">
-              <input type="checkbox" class="propertie-input" ng-disabled="isLocalCluster || editConfigurationDisabled" name="{{property.name}}" ng-model="configuration[property.name]" ng-true-value="true" ng-false-value="false" popover="{{property.description}}" popover-title="{{property.name}}" popover-trigger="mouseenter">
+              <input type="checkbox" class="propertie-input" ng-disabled="(data.clusterType != 'NONE') || editConfigurationDisabled" name="{{property.name}}" ng-model="configuration[property.name]" ng-true-value="true" ng-false-value="false" popover="{{property.description}}" popover-title="{{property.name}}" popover-trigger="mouseenter">
             </div>
             <div class="col-sm-9" ng-switch-default>
-              <input type="{{property.masked ? 'password' : 'text'}}" class="form-control propertie-input" ng-required="property.required && !isLocalCluster" ng-change="propertiesForm[property.name].validationError=''" ng-disabled="isLocalCluster || editConfigurationDisabled" name="{{property.name}}" ng-model="configuration[property.name]" popover="{{property.description}}" popover-title="{{property.name}}" popover-trigger="mouseenter" placeholder="{{property.placeholder}}">
-              <div class="alert alert-danger no-margin-bottom top-margin" ng-show='property.required && propertiesForm[property.name].$error.required && !editConfigurationDisabled && !isLocalCluster'>
+              <input type="{{property.masked ? 'password' : 'text'}}" class="form-control propertie-input" ng-required="property.required && (data.clusterType == 'NONE')" ng-change="propertiesForm[property.name].validationError=''" ng-disabled="(data.clusterType != 'NONE') || editConfigurationDisabled" name="{{property.name}}" ng-model="configuration[property.name]" popover="{{property.description}}" popover-title="{{property.name}}" popover-trigger="mouseenter" placeholder="{{property.placeholder}}">
+              <div class="alert alert-danger no-margin-bottom top-margin" ng-show="property.required && propertiesForm[property.name].$error.required && !editConfigurationDisabled && (data.clusterType == 'NONE')">
                 {{'common.alerts.fieldIsRequired' | translate}}
               </div>
-              <div class="alert alert-danger no-margin-bottom top-margin" ng-show='property.required && propertiesForm[property.name].validationError && !editConfigurationDisabled && !isLocalCluster'>
+              <div class="alert alert-danger no-margin-bottom top-margin" ng-show="property.required && propertiesForm[property.name].validationError && !editConfigurationDisabled && (data.clusterType == 'NONE')">
                 {{propertiesForm[property.name].validationMessage}}
               </div>
             </div>
@@ -223,7 +239,7 @@
         </div>
       </fieldset>
     </form>
-    <div ng-show="isConfigurationEmpty">
+    <div ng-show="isConfigurationEmpty && !clusterConfigurable">
       <div class="alert alert-info">{{'views.alerts.notDefined' | translate: '{term: constants.props}'}}</div>
     </div>
   </div>
@@ -258,7 +274,7 @@
       </tr>
       </tbody>
     </table>
-    <div ng-show="!isPermissionsEmpty && isLocalCluster && editConfigurationDisabled">
+    <div ng-show="!isPermissionsEmpty && (data.clusterType == 'LOCAL_AMBARI') && editConfigurationDisabled">
       <div class="col-sm-12 cluster-inherited-permission">
         <strong>{{'views.clusterPermissions.label' | translate}}</strong>
       </div>
@@ -277,4 +293,4 @@
       <div class="alert alert-info">{{'views.alerts.notDefined' | translate: '{term: constants.perms}'}}</div>
     </div>
   </div>
-</div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html
index f93867c..f99e0cc 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html
@@ -53,9 +53,9 @@
                 <i class="glyphicon glyphicon-remove"></i>
               </button>
               <button
-                    type="submit"
-                    class="btn btn-primary btn-xs"
-                    ng-class="{'disabled': editClusterNameForm.newClusterName.$invalid || editCluster.name == cluster.Clusters.cluster_name}">
+                  type="submit"
+                  class="btn btn-primary btn-xs"
+                  ng-class="{'disabled': editClusterNameForm.newClusterName.$invalid || editCluster.name == cluster.Clusters.cluster_name}">
                 <i class="glyphicon glyphicon-ok"></i>
               </button>
             </div>
@@ -78,10 +78,13 @@
           <li><p class="noclusters">{{'common.noClusters' | translate}}</p></li>
         </ul>
       </div>
-      <ul class="nav nav-pills nav-stacked" ng-show="cluster && totalRepos > 0">
-        <li ng-class="{active: isActive('stackVersions.list')}">
+      <ul class="nav nav-pills nav-stacked" >
+        <li ng-class="{active: isActive('stackVersions.list')}" ng-show="cluster && totalRepos > 0">
           <a href="#/stackVersions">{{'common.versions' | translate}}</a>
         </li>
+        <li ng-class="{active: isActive('remoteClusters.list')}">
+          <a href="#/remoteClusters">{{'common.remoteClusters' | translate}}</a>
+        </li>
       </ul>
     </div>
   </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/editRemoteClusterPage.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/editRemoteClusterPage.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/editRemoteClusterPage.html
new file mode 100644
index 0000000..b5890bf
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/editRemoteClusterPage.html
@@ -0,0 +1,77 @@
+<!--
+* 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.
+-->
+<div class="clearfix">
+  <ol class="breadcrumb pull-left">
+    <li><a href="#/remoteClusters">{{'common.remoteClusters' | translate}}</a></li>
+    <li class="active">{{cluster.cluster_name}}</li>
+  </ol>
+  <div class="pull-right top-margin-4">
+    <div>
+      <button class="btn deleteuser-btn btn-danger" ng-click="deleteCluster()">{{'common.deregisterCluster' | translate}}</button>
+    </div>
+  </div>
+</div>
+<hr>
+
+
+
+
+<form class="form-horizontal create-user-form" role="form" novalidate name="form" autocomplete="off">
+
+  <div class="form-group" ng-class="{'has-error' : form.user_name.$error.required && form.submitted}">
+    <label for="clustername" class="col-sm-2 control-label">{{'views.clusterName' | translate}}</label>
+    <div class="col-sm-10">
+      <input type="text" id="clustername" disabled class="form-control" name="cluster_name" ng-pattern="/[a-zA-Z0-9\-]*/" placeholder="Cluster Name" ng-model="cluster.cluster_name" required autocomplete="off">
+      <div class="alert alert-danger top-margin" ng-show="form.cluster_name.$error.required && form.submitted"> {{'common.alerts.fieldIsRequired' | translate}}</div>
+    </div>
+  </div>
+
+  <div class="form-group" ng-class="{'has-error' : form.user_name.$error.required && form.submitted}">
+    <label for="clusterurl" class="col-sm-2 control-label">{{'users.ambariClusterURL' | translate}}</label>
+    <div class="col-sm-10">
+      <input type="text" id="clusterurl" class="form-control" name="cluster_url" placeholder="Ambari Cluster URL" ng-model="cluster.cluster_url" required autocomplete="off">
+      <div class="alert alert-danger top-margin" ng-show="form.cluster_url.$error.required && form.submitted"> {{'common.alerts.fieldIsRequired' | translate}}</div>
+    </div>
+  </div>
+
+  <div class="form-group" ng-class="{'has-error' : form.user_name.$error.required && form.submitted}" style="display: none" >
+    <label for="clusteruser" class="col-sm-2 control-label"> {{'users.roles.clusterUser' | translate}}</label>
+    <div class="col-sm-10">
+      <input type="text" id="clusteruser" class="form-control" name="cluster_user" placeholder="Cluster User" ng-model="cluster.cluster_user" required autocomplete="off">
+      <div class="alert alert-danger top-margin" ng-show="form.cluster_user.$error.required && form.submitted"> {{'common.alerts.fieldIsRequired' | translate}}</div>
+    </div>
+  </div>
+
+  <div class="form-group">
+    <label for="" class="col-sm-2 control-label"></label>
+    <div class="col-sm-10">
+      <div>
+        <a href ng-click="openChangePwdDialog()" class="btn btn-default changepassword">{{'users.updateCredentials' | translate}}</a>
+      </div>
+    </div>
+  </div>
+
+
+  <div class="form-group">
+    <div class="col-sm-offset-2 col-sm-10">
+      <button class="btn btn-primary pull-right left-margin saveremotecluster" ng-click="editRemoteCluster()">{{'common.controls.save' | translate}}</button>
+      <a class="btn btn-default pull-right cancel" href ng-click="cancel()">{{'common.controls.cancel' | translate}}</a>
+    </div>
+  </div>
+
+</form>

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/list.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/list.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/list.html
new file mode 100644
index 0000000..e386ffd
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/list.html
@@ -0,0 +1,80 @@
+<!--
+* 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.
+-->
+
+<div class="users-pane">
+  <div class="clearfix">
+    <ol class="breadcrumb pull-left">
+      <li class="active">Remote Clusters</li>
+    </ol>
+    <div class="pull-right top-margin-4">
+      <a href="#/remoteClusters/create" class="btn btn-primary">
+        <span class="glyphicon glyphicon-plus"></span>
+        {{'views.registerRemoteCluster' | translate}}
+      </a>
+    </div>
+  </div>
+  <hr>
+  <table class="table table-striped table-hover">
+    <thead>
+      <tr>
+        <th class="col-sm-3">
+          <div class="search-container">
+            <label for="">{{'views.clusterName' | translate}}</label>
+            <input type="text" class="form-control namefilter" placeholder="{{'common.any' | translate}}" ng-model="currentNameFilter" ng-change="resetPagination()">
+            <button type="button" class="close" ng-show="currentNameFilter" ng-click="currentNameFilter=''; resetPagination()"><span aria-hidden="true">&times;</span><span class="sr-only">{{'common.controls.close' | translate}}</span></button>
+          </div>
+        </th>
+        <th class="col-sm-9">
+          <label for="">{{'common.services' | translate}}</label>
+          <select class="form-control typefilter"
+                  ng-model="currentTypeFilter"
+                  ng-options="item for item in typeFilterOptions"
+                  ng-change="resetPagination();">
+          </select>
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+    <tr ng-repeat="remoteCluster in remoteClusters" >
+      <td class="col-sm-3"><a href="#/remoteClusters/{{remoteCluster.ClusterInfo.name}}/edit">{{ remoteCluster.ClusterInfo.name }}</a></td>
+      <td class="col-sm-9">
+        <span ng-repeat="service in remoteCluster.ClusterInfo.services" ng-if="remoteCluster.ClusterInfo.services.length > 0">{{ service }} {{$last ? '' : ','}} </span>
+        <span ng-if="remoteCluster.ClusterInfo.services.length == 0">--</span>
+      </td>
+    </tr>
+    </tbody>
+
+  </table>
+  <div class="alert alert-info col-sm-12" ng-show="!remoteClusters.length">
+    {{'common.alerts.noRemoteClusterDisplay' | translate}}
+  </div>
+  <div class="col-sm-12 table-bar">
+    <div class="pull-left filtered-info">
+      <span>{{'common.filterInfo' | translate: '{showed: tableInfo.showed, total: tableInfo.total, term: constants.groups}'}}</span>
+      <span ng-show="isNotEmptyFilter">- <a href ng-click="clearFilters()">{{'common.controls.clearFilters' | translate}}</a></span>
+    </div>
+    <div class="pull-right left-margin">
+      <pagination class="paginator" total-items="totalGroups" max-size="maxVisiblePages" items-per-page="groupsPerPage" ng-model="currentPage" ng-change="pageChanged()"></pagination>
+    </div>
+    <div class="pull-right">
+      <select class="form-control" ng-model="groupsPerPage" ng-change="groupsPerPageChanges()" ng-options="currOption for currOption in [10, 25, 50, 100]"></select>
+    </div>
+  </div>
+
+</div>
+


[3/4] ambari git commit: AMBARI-16274. Views: Simplify View Configuration for Remote Ambari Managed Cluster. (Gaurav Nagar via dipayanb)

Posted by db...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/modals/changePassword.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/modals/changePassword.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/modals/changePassword.html
new file mode 100644
index 0000000..a05e13d
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/modals/changePassword.html
@@ -0,0 +1,46 @@
+<!--
+* 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.
+-->
+<div class="modal-header">
+  <h3 class="modal-title">{{'users.updateCredentials' | translate}}</h3>
+</div>
+<div class="modal-body">
+  <form class="form-horizontal" novalidate name="form.passwordChangeForm" role="form" >
+
+    <div class="form-group" ng-class="{'has-error' : (form.passwordChangeForm.currentPassword.$error.required && form.passwordChangeForm.submitted)}">
+      <label for="" class="col-sm-4 control-label" >{{'users.roles.clusterUser' | translate}}</label>
+      <div class="col-sm-8">
+        <input type="text" name="currentUserName" class="form-control bottom-margin" placeholder="Cluster User" required ng-model="passwordData.currentUserName" autocomplete="off">
+        <div class="alert alert-danger no-margin-bottom" ng-show='form.passwordChangeForm.currentUserName.$error.required && form.passwordChangeForm.submitted'>{{'users.alerts.usernameRequired' | translate}}</div>
+      </div>
+    </div>
+
+    <div class="form-group no-margin-bottom" ng-class="{'has-error' : (form.passwordChangeForm.password.$error.required && form.passwordChangeForm.submitted) || form.passwordChangeForm.confirmPassword.$error.passwordVerify}">
+      <label for="" class="col-sm-4 control-label">{{'users.password' | translate}}</label>
+      <div class="col-sm-8">
+        <input type="password" class="form-control bottom-margin" name="password" placeholder="Password" required ng-model="passwordData.password" autocomplete="off">
+        <div class="alert alert-danger no-margin-bottom" ng-show='form.passwordChangeForm.password.$error.required && form.passwordChangeForm.submitted'>{{'users.alerts.passwordRequired' | translate}}</div>
+        <div class="alert alert-danger no-margin-bottom" ng-show='form.passwordChangeForm.confirmPassword.$error.passwordVerify'>{{'users.alerts.wrongPassword' | translate}}</div>
+      </div>
+
+    </div>
+  </form>
+</div>
+<div class="modal-footer">
+  <button class="btn btn-default" ng-click="cancel()">{{'common.controls.cancel' | translate}}</button>
+  <button class="btn btn-primary" ng-click="ok()">{{'common.controls.ok' | translate}}</button>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/remoteClusterPage.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/remoteClusterPage.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/remoteClusterPage.html
new file mode 100644
index 0000000..0f82360
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/remoteClusterPage.html
@@ -0,0 +1,66 @@
+<!--
+* 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.
+-->
+<ol class="breadcrumb">
+  <li><a href="#/remoteClusters">{{'common.remoteClusters' | translate}}</a></li>
+  <li class="active">{{'common.register' | translate}}</li>
+</ol>
+<hr>
+<form class="form-horizontal create-user-form" role="form" novalidate name="form" autocomplete="off">
+
+  <div class="form-group" ng-class="{'has-error' : form.user_name.$error.required && form.submitted}">
+    <label for="clustername" class="col-sm-2 control-label">{{'views.clusterName' | translate}} </label>
+    <div class="col-sm-10">
+      <input type="text" id="clustername" class="form-control" ng-pattern="/[a-zA-Z0-9\-]*/" name="cluster_name" placeholder="Ambari Cluster Name" ng-model="cluster.cluster_name" required autocomplete="off">
+      <div class="alert alert-danger top-margin" ng-show="form.cluster_name.$error.required && form.submitted"> {{'common.alerts.fieldIsRequired' | translate}}</div>
+      <div class="alert alert-danger top-margin" ng-show="form.cluster_name.$error.pattern && form.submitted"> {{'views.alerts.noSpecialChars' | translate}}</div>
+    </div>
+  </div>
+
+  <div class="form-group" ng-class="{'has-error' : form.user_name.$error.required && form.submitted}">
+    <label for="clusterurl" class="col-sm-2 control-label">{{'users.ambariClusterURL' | translate}}</label>
+    <div class="col-sm-10">
+      <input type="text" id="clusterurl" class="form-control" name="cluster_url" placeholder="http://ambari.server:8080/api/v1/clusters/c1" ng-model="cluster.cluster_url" required autocomplete="off">
+      <div class="alert alert-danger top-margin" ng-show="form.cluster_url.$error.required && form.submitted"> {{'common.alerts.fieldIsRequired' | translate}}</div>
+    </div>
+  </div>
+
+  <div class="form-group" ng-class="{'has-error' : form.user_name.$error.required && form.submitted}">
+    <label for="clusteruser" class="col-sm-2 control-label"> {{'users.roles.clusterUser' | translate}}</label>
+    <div class="col-sm-10">
+      <input type="text" id="clusteruser" class="form-control" name="cluster_user" placeholder="Cluster User" ng-model="cluster.cluster_user" required autocomplete="off">
+      <div class="alert alert-danger top-margin" ng-show="form.cluster_user.$error.required && form.submitted"> {{'common.alerts.fieldIsRequired' | translate}}</div>
+    </div>
+  </div>
+
+  <div class="form-group" ng-class="{'has-error' : (form.password.$error.required && form.submitted) || form.confirmPassword.$error.passwordVerify}">
+    <label for="password" class="col-sm-2 control-label">{{'users.password' | translate}}</label>
+    <div class="col-sm-10">
+      <input type="password" id="password" class="form-control bottom-margin userpassword" name="password" placeholder="Password" required ng-model="cluster.cluster_password" autocomplete="off">
+      <div class="alert alert-danger" ng-show='form.confirmPassword.$error.passwordVerify'>Wrong Password</div>
+      <div class="alert alert-danger" ng-show='form.password.$error.required && form.submitted'>{{'common.alerts.passwordRequired' | translate}}</div>
+    </div>
+  </div>
+
+  <div class="form-group">
+    <div class="col-sm-offset-2 col-sm-10">
+      <button class="btn btn-primary pull-right left-margin saveremotecluster" ng-click="registerRemoteCluster()">{{'common.controls.save' | translate}}</button>
+      <a class="btn btn-default pull-right cancel" href ng-click="cancel()">  {{'common.controls.cancel' | translate}}</a>
+    </div>
+  </div>
+
+</form>

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RemoteClusterResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RemoteClusterResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RemoteClusterResourceDefinition.java
new file mode 100644
index 0000000..b656a30
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RemoteClusterResourceDefinition.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.ambari.server.api.resources;
+
+import org.apache.ambari.server.controller.spi.Resource;
+
+
+/**
+ * Remote Cluster resource definition.
+ */
+public class RemoteClusterResourceDefinition extends BaseResourceDefinition {
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct a view resource definition.
+   */
+  public RemoteClusterResourceDefinition() {
+    super(Resource.Type.RemoteCluster);
+  }
+
+
+  // ----- ResourceDefinition ------------------------------------------------
+
+  @Override
+  public String getPluralName() {
+    return "remote_clusters";
+  }
+
+  @Override
+  public String getSingularName() {
+    return "remote_cluster";
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
index 0b77511..9c864b6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
@@ -437,6 +437,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
         resourceDefinition = new LoggingResourceDefinition();
         break;
 
+      case RemoteCluster:
+        resourceDefinition = new RemoteClusterResourceDefinition();
+        break;
+
       default:
         throw new IllegalArgumentException("Unsupported resource type: " + type);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/api/services/RemoteClustersService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/RemoteClustersService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/RemoteClustersService.java
new file mode 100644
index 0000000..d7c7a20
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/RemoteClustersService.java
@@ -0,0 +1,139 @@
+/**
+ * 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.ambari.server.api.services;
+
+import com.google.common.base.Optional;
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.security.authorization.AuthorizationException;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.Collections;
+
+
+/**
+ * Service responsible for Remote Cluster resource requests.
+ */
+@Path("/remoteclusters")
+public class RemoteClustersService extends BaseService {
+
+  /**
+   * Get the list of all Remote Clusters
+   * @param headers
+   * @param ui
+   * @return collections of all remote clusters
+   */
+  @GET
+  @Produces("text/plain")
+  public Response getRemoteClusters(@Context HttpHeaders headers, @Context UriInfo ui) {
+    return handleRequest(headers, null, ui, Request.Type.GET, createRemoteClusterResource(null));
+  }
+
+
+  /**
+   * Create a new RemoteAmbariCluster
+   * @param body
+   * @param headers
+   * @param ui
+   * @param clusterName
+   * @return
+   */
+  @POST
+  @Path("{clusterName}")
+  @Produces("text/plain")
+  public Response createRemoteCluster(String body, @Context HttpHeaders headers, @Context UriInfo ui,
+                                @PathParam("clusterName") String clusterName) {
+    return handleRequest(headers, body, ui, Request.Type.POST, createRemoteClusterResource(clusterName));
+  }
+
+
+  /**
+   * Update a Remote Cluster
+   * @param body
+   * @param headers
+   * @param ui
+   * @param clusterName
+   * @return
+   */
+  @PUT
+  @Path("{clusterName}")
+  @Produces("text/plain")
+  public Response updateRemoteCluster(String body, @Context HttpHeaders headers, @Context UriInfo ui,
+                            @PathParam("clusterName") String clusterName) {
+    return handleRequest(headers, body, ui, Request.Type.PUT, createRemoteClusterResource(clusterName));
+  }
+
+  /**
+   * Delete a Remote Cluster
+   * @param body
+   * @param headers
+   * @param ui
+   * @param clusterName
+   * @return
+   */
+  @DELETE
+  @Path("{clusterName}")
+  @Produces("text/plain")
+  public Response deleteRemoteCluster(String body, @Context HttpHeaders headers, @Context UriInfo ui,
+                            @PathParam("clusterName") String clusterName) {
+    return handleRequest(headers, body, ui, Request.Type.DELETE, createRemoteClusterResource(clusterName));
+  }
+
+
+  /**
+   * Get information about a Remote Cluster
+   * @param headers
+   * @param ui
+   * @param clusterName
+   * @return
+   */
+  @GET
+  @Path("{clusterName}")
+  @Produces("text/plain")
+  public Response getRemoteCluster(@Context HttpHeaders headers, @Context UriInfo ui,
+                                @PathParam("clusterName") String clusterName) {
+    return handleRequest(headers, null, ui, Request.Type.GET, createRemoteClusterResource(clusterName));
+  }
+
+
+
+
+  // ----- helper methods ----------------------------------------------------
+
+  /**
+   * Create a Remote Cluster resource.
+   *
+   * @param clusterName Name of the Cluster
+   *
+   * @return a RemoteCluster resource Instance
+   */
+  private ResourceInstance createRemoteClusterResource(String clusterName) {
+    return createResource(Resource.Type.RemoteCluster,Collections.singletonMap(Resource.Type.RemoteCluster, clusterName));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
index 4e7a032..35fdcf6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
@@ -122,6 +122,8 @@ public class DefaultProviderModule extends AbstractProviderModule {
         return new SettingResourceProvider();
       case Artifact:
         return new ArtifactResourceProvider(managementController);
+      case RemoteCluster:
+        return new RemoteClusterResourceProvider();
 
       default:
         return AbstractControllerResourceProvider.getResourceProvider(type, propertyIds,

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RemoteClusterResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RemoteClusterResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RemoteClusterResourceProvider.java
new file mode 100644
index 0000000..413dbff
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RemoteClusterResourceProvider.java
@@ -0,0 +1,327 @@
+/**
+ * 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.ambari.server.controller.internal;
+
+import com.google.common.base.Strings;
+import com.google.inject.Inject;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.DuplicateResourceException;
+import org.apache.ambari.server.StaticallyInject;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.controller.predicate.EqualsPredicate;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.NoSuchResourceException;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.orm.dao.RemoteAmbariClusterDAO;
+import org.apache.ambari.server.orm.entities.RemoteAmbariClusterEntity;
+import org.apache.ambari.server.orm.entities.RemoteAmbariClusterServiceEntity;
+import org.apache.ambari.server.security.authorization.RoleAuthorization;
+import org.apache.ambari.server.view.RemoteAmbariClusterRegistry;
+import org.apache.ambari.view.MaskException;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *  Resource Provider for Remote Cluster
+ */
+@StaticallyInject
+public class RemoteClusterResourceProvider extends AbstractAuthorizedResourceProvider {
+
+  /**
+   * Remote Cluster property id constants.
+   */
+  public static final String CLUSTER_NAME_PROPERTY_ID = "ClusterInfo/name";
+  public static final String CLUSTER_URL_PROPERTY_ID  = "ClusterInfo/url";
+  public static final String USERNAME_PROPERTY_ID = "ClusterInfo/username";
+  public static final String PASSWORD_PROPERTY_ID = "ClusterInfo/password";
+  public static final String SERVICES_PROPERTY_ID = "ClusterInfo/services";
+
+  /**
+   * The logger.
+   */
+  private final static Logger LOG = LoggerFactory.getLogger(RemoteClusterResourceProvider.class);
+
+  /**
+   * The key property ids for a Remote Cluster resource.
+   */
+  private static Map<Resource.Type, String> keyPropertyIds = new HashMap<Resource.Type, String>();
+  static {
+    keyPropertyIds.put(Resource.Type.RemoteCluster, CLUSTER_NAME_PROPERTY_ID);
+  }
+
+  /**
+   * The property ids for a Remote Cluster resource.
+   */
+  private static Set<String> propertyIds = new HashSet<String>();
+  static {
+    propertyIds.add(CLUSTER_NAME_PROPERTY_ID);
+    propertyIds.add(CLUSTER_URL_PROPERTY_ID);
+    propertyIds.add(USERNAME_PROPERTY_ID);
+    propertyIds.add(PASSWORD_PROPERTY_ID);
+    propertyIds.add(SERVICES_PROPERTY_ID);
+  }
+
+  @Inject
+  private static RemoteAmbariClusterDAO remoteAmbariClusterDAO;
+
+  @Inject
+  private static Configuration configuration;
+
+  @Inject
+  private static RemoteAmbariClusterRegistry remoteAmbariClusterRegistry;
+
+  /**
+   * Create a  new resource provider.
+   */
+  protected RemoteClusterResourceProvider() {
+    super(propertyIds, keyPropertyIds);
+
+    EnumSet<RoleAuthorization> requiredAuthorizations = EnumSet.of(RoleAuthorization.AMBARI_ADD_DELETE_CLUSTERS);
+    setRequiredCreateAuthorizations(requiredAuthorizations);
+    setRequiredDeleteAuthorizations(requiredAuthorizations);
+    setRequiredUpdateAuthorizations(requiredAuthorizations);
+  }
+
+  @Override
+  public Map<Resource.Type, String> getKeyPropertyIds() {
+    return keyPropertyIds;
+  }
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return new HashSet<String>(keyPropertyIds.values());
+  }
+
+  @Override
+  public RequestStatus createResourcesAuthorized(Request request) throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException {
+    for (Map<String, Object> properties : request.getProperties()) {
+      createResources(getCreateCommand(properties));
+    }
+    notifyCreate(Resource.Type.RemoteCluster, request);
+
+    return getRequestStatus(null);
+  }
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    Set<Resource> resources    = new HashSet<Resource>();
+    Set<String>   requestedIds = getRequestPropertyIds(request, predicate);
+
+    Set<Map<String, Object>> propertyMaps = getPropertyMaps(predicate);
+    if (propertyMaps.isEmpty()) {
+      propertyMaps.add(Collections.<String, Object>emptyMap());
+    }
+
+    for (Map<String, Object> propertyMap : propertyMaps) {
+
+      String clusterName = (String) propertyMap.get(CLUSTER_NAME_PROPERTY_ID);
+
+      if(!Strings.isNullOrEmpty(clusterName)){
+        RemoteAmbariClusterEntity cluster = remoteAmbariClusterDAO.findByName(clusterName);
+        if(cluster == null) {
+          throw new NoSuchResourceException(String.format("Cluster with name %s cannot be found",clusterName) );
+        }
+        resources.add(toResource(requestedIds, cluster));
+      }else {
+        for (RemoteAmbariClusterEntity cluster : remoteAmbariClusterDAO.findAll()){
+          Resource resource = toResource(requestedIds, cluster);
+          resources.add(resource);
+        }
+      }
+    }
+    return resources;
+  }
+
+  protected Resource toResource(Set<String> requestedIds, RemoteAmbariClusterEntity cluster) {
+    Resource   resource   = new ResourceImpl(Resource.Type.RemoteCluster);
+    setResourceProperty(resource, CLUSTER_NAME_PROPERTY_ID, cluster.getName(), requestedIds);
+    setResourceProperty(resource, CLUSTER_URL_PROPERTY_ID, cluster.getUrl(), requestedIds);
+    setResourceProperty(resource, USERNAME_PROPERTY_ID, cluster.getUsername(), requestedIds);
+    ArrayList<String> services = new ArrayList<String>();
+    for (RemoteAmbariClusterServiceEntity remoteClusterServiceEntity : cluster.getServices()) {
+      services.add(remoteClusterServiceEntity.getServiceName());
+    }
+    setResourceProperty(resource, SERVICES_PROPERTY_ID,services, requestedIds);
+    return resource;
+  }
+
+  @Override
+  public RequestStatus updateResourcesAuthorized(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    Iterator<Map<String,Object>> iterator = request.getProperties().iterator();
+    if (iterator.hasNext()) {
+      for (Map<String, Object> propertyMap : getPropertyMaps(iterator.next(), predicate)) {
+        modifyResources(getUpdateCommand(propertyMap));
+      }
+    }
+    notifyUpdate(Resource.Type.RemoteCluster, request, predicate);
+
+    return getRequestStatus(null);
+  }
+
+  @Override
+  protected RequestStatus deleteResourcesAuthorized(Request request, Predicate predicate)
+    throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+
+    modifyResources(getDeleteCommand(predicate));
+    notifyDelete(Resource.Type.ViewInstance, predicate);
+    return getRequestStatus(null);
+
+  }
+
+  /**
+   * Get the command to create the RemoteAmbariCluster
+   * @param properties
+   * @return A command to create the RemoteAmbariCluster
+   */
+  private Command<Void> getCreateCommand(final Map<String, Object> properties) {
+    return new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        String name = (String)properties.get(CLUSTER_NAME_PROPERTY_ID);
+
+        if(StringUtils.isEmpty(name)){
+          throw new IllegalArgumentException("Cluster Name cannot ne null or Empty");
+        }
+
+        if(remoteAmbariClusterDAO.findByName(name) != null){
+          throw new DuplicateResourceException(String.format("Remote cluster with name %s already exists",name));
+        }
+
+        saveOrUpdateRemoteAmbariClusterEntity(properties,false);
+
+        return null;
+      }
+    };
+  }
+
+  /**
+   * Get the command to update the RemoteAmbariCluster
+   * @param properties
+   * @return A command to update the RemoteAmbariCluster
+   */
+  private Command<Void> getUpdateCommand(final Map<String, Object> properties) {
+    return new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        String name = (String)properties.get(CLUSTER_NAME_PROPERTY_ID);
+
+        if(StringUtils.isEmpty(name)){
+          throw new IllegalArgumentException("Cluster Name cannot ne null or Empty");
+        }
+
+        saveOrUpdateRemoteAmbariClusterEntity(properties,true);
+        return null;
+
+      }
+    };
+  }
+
+  /**
+   *  Save or update Remote Ambari Cluster Entity to database
+   *
+   * @param properties
+   * @param update
+   * @throws AmbariException
+   */
+  private void saveOrUpdateRemoteAmbariClusterEntity(Map<String, Object> properties,boolean update) throws AmbariException {
+    String name = (String)properties.get(CLUSTER_NAME_PROPERTY_ID);
+    String url = (String)properties.get(CLUSTER_URL_PROPERTY_ID);
+    String username = (String)properties.get(USERNAME_PROPERTY_ID);
+    String password = (String)properties.get(PASSWORD_PROPERTY_ID);
+
+    if(StringUtils.isEmpty(url) && StringUtils.isEmpty(username)){
+      throw new IllegalArgumentException("Url or username cannot be null");
+    }
+
+    RemoteAmbariClusterEntity entity = remoteAmbariClusterDAO.findByName(name);
+
+    if(update && entity == null){
+      throw new IllegalArgumentException(String.format("Cannot find cluster with name : \"%s\"",name));
+    }else if(!update && entity != null){
+      throw new DuplicateResourceException(String.format("Cluster with name : \"%s\" already exists",name));
+    }
+
+    // Check Password not null for create
+    //Check username matches the entity username if password not present
+    if(StringUtils.isBlank(password) && !update){
+      throw new IllegalArgumentException("Password cannot be null");
+    }else if(StringUtils.isBlank(password) && update && !username.equals(entity.getUsername())){
+      throw new IllegalArgumentException("Failed to update. Username does not match.");
+    }
+
+    if (entity == null) {
+      entity = new RemoteAmbariClusterEntity();
+    }
+
+    entity.setName(name);
+    entity.setUrl(url);
+    try {
+      if(password != null) {
+        entity.setUsername(username);
+        entity.setPassword(password);
+      }
+    } catch (MaskException e) {
+      throw new IllegalArgumentException("Failed to create new Remote Cluster " + name + ". Illegal Password");
+    }
+
+    try {
+      remoteAmbariClusterRegistry.saveOrUpdate(entity,update);
+    } catch (Exception e) {
+      throw new IllegalArgumentException("Failed to create new Remote Cluster " + name +". " + e.getMessage(),e);
+    }
+  }
+
+  /**
+   * Get the command to delete the Cluster
+   * @param predicate
+   * @return The delete command
+   */
+  private Command<Void> getDeleteCommand(final Predicate predicate) {
+    return new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        Comparable deletedCluster = ((EqualsPredicate) predicate).getValue();
+        String toDelete = deletedCluster.toString();
+        RemoteAmbariClusterEntity clusterEntity = remoteAmbariClusterDAO.findByName(toDelete);
+        if(clusterEntity == null){
+          throw new IllegalArgumentException("The Cluster "+ toDelete +" does not exist");
+        }
+
+        remoteAmbariClusterRegistry.delete(clusterEntity);
+        return null;
+      }
+    };
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java
index 605f68d..b8ed215 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java
@@ -41,6 +41,7 @@ import org.apache.ambari.server.view.ViewRegistry;
 import org.apache.ambari.server.view.validation.InstanceValidationResultImpl;
 import org.apache.ambari.server.view.validation.ValidationException;
 import org.apache.ambari.server.view.validation.ValidationResultImpl;
+import org.apache.ambari.view.ClusterType;
 import org.apache.ambari.view.validation.Validator;
 
 import java.util.Collections;
@@ -72,6 +73,7 @@ public class ViewInstanceResourceProvider extends AbstractAuthorizedResourceProv
   public static final String CONTEXT_PATH_PROPERTY_ID   = "ViewInstanceInfo/context_path";
   public static final String STATIC_PROPERTY_ID         = "ViewInstanceInfo/static";
   public static final String CLUSTER_HANDLE_PROPERTY_ID = "ViewInstanceInfo/cluster_handle";
+  public static final String CLUSTER_TYPE_PROPERTY_ID = "ViewInstanceInfo/cluster_type";
   public static final String SHORT_URL_PROPERTY_ID      = "ViewInstanceInfo/short_url";
   public static final String SHORT_URL_NAME_PROPERTY_ID = "ViewInstanceInfo/short_url_name";
 
@@ -113,6 +115,7 @@ public class ViewInstanceResourceProvider extends AbstractAuthorizedResourceProv
     propertyIds.add(CONTEXT_PATH_PROPERTY_ID);
     propertyIds.add(STATIC_PROPERTY_ID);
     propertyIds.add(CLUSTER_HANDLE_PROPERTY_ID);
+    propertyIds.add(CLUSTER_TYPE_PROPERTY_ID);
     propertyIds.add(SHORT_URL_PROPERTY_ID);
     propertyIds.add(SHORT_URL_NAME_PROPERTY_ID);
     propertyIds.add(VALIDATION_RESULT_PROPERTY_ID);
@@ -245,6 +248,7 @@ public class ViewInstanceResourceProvider extends AbstractAuthorizedResourceProv
     setResourceProperty(resource, VISIBLE_PROPERTY_ID, viewInstanceEntity.isVisible(), requestedIds);
     setResourceProperty(resource, STATIC_PROPERTY_ID, viewInstanceEntity.isXmlDriven(), requestedIds);
     setResourceProperty(resource, CLUSTER_HANDLE_PROPERTY_ID, viewInstanceEntity.getClusterHandle(), requestedIds);
+    setResourceProperty(resource, CLUSTER_TYPE_PROPERTY_ID, viewInstanceEntity.getClusterType(), requestedIds);
     ViewURLEntity viewUrl = viewInstanceEntity.getViewUrl();
     if(viewUrl != null) {
       setResourceProperty(resource, SHORT_URL_PROPERTY_ID, viewUrl.getUrlSuffix(), requestedIds);
@@ -351,6 +355,11 @@ public class ViewInstanceResourceProvider extends AbstractAuthorizedResourceProv
       viewInstanceEntity.setClusterHandle((String) properties.get(CLUSTER_HANDLE_PROPERTY_ID));
     }
 
+    if (properties.containsKey(CLUSTER_TYPE_PROPERTY_ID)) {
+      String clusterType = (String) properties.get(CLUSTER_TYPE_PROPERTY_ID);
+      viewInstanceEntity.setClusterType(ClusterType.valueOf(clusterType));
+    }
+
     Map<String, String> instanceProperties = new HashMap<String, String>();
 
     boolean isUserAdmin = viewRegistry.checkAdmin();

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
index 386e657..99e4ccd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
@@ -156,7 +156,8 @@ public interface Resource {
     UserAuthorization,
     VersionDefinition,
     ClusterKerberosDescriptor,
-    LoggingQuery;
+    LoggingQuery,
+    RemoteCluster;
 
     /**
      * Get the {@link Type} that corresponds to this InternalType.
@@ -274,6 +275,7 @@ public interface Resource {
     public static final Type VersionDefinition = InternalType.VersionDefinition.getType();
     public static final Type ClusterKerberosDescriptor = InternalType.ClusterKerberosDescriptor.getType();
     public static final Type LoggingQuery = InternalType.LoggingQuery.getType();
+    public static final Type RemoteCluster = InternalType.RemoteCluster.getType();
 
     /**
      * The type name.

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RemoteAmbariClusterDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RemoteAmbariClusterDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RemoteAmbariClusterDAO.java
new file mode 100644
index 0000000..72ab368
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RemoteAmbariClusterDAO.java
@@ -0,0 +1,101 @@
+/**
+ * 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.ambari.server.orm.dao;
+
+import com.google.common.base.Optional;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.RemoteAmbariClusterEntity;
+import org.apache.ambari.server.orm.entities.ViewURLEntity;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.List;
+
+/**
+ * Remote Ambari Cluster Data Access Object.
+ */
+@Singleton
+public class RemoteAmbariClusterDAO {
+  /**
+   * JPA entity manager
+   */
+  @Inject
+  private Provider<EntityManager> entityManagerProvider;
+  @Inject
+  private DaoUtils daoUtils;
+
+
+  /**
+   * Find all view instances.
+   *
+   * @return all views or an empty List
+   */
+  @RequiresSession
+  public List<RemoteAmbariClusterEntity> findAll() {
+    TypedQuery<RemoteAmbariClusterEntity> query = entityManagerProvider.get().
+        createNamedQuery("allRemoteAmbariClusters", RemoteAmbariClusterEntity.class);
+
+    return query.getResultList();
+  }
+
+  /**
+   * Find Cluster by name
+   * @param clusterName
+   * @return
+     */
+  @RequiresSession
+  public RemoteAmbariClusterEntity findByName(String clusterName) {
+    TypedQuery<RemoteAmbariClusterEntity> query = entityManagerProvider.get().
+            createNamedQuery("remoteAmbariClusterByName", RemoteAmbariClusterEntity.class);
+    query.setParameter("clusterName", clusterName);
+    return daoUtils.selectSingle(query);
+  }
+
+  /**
+   * Save a Cluster entity
+   * @param entity
+     */
+  @Transactional
+  public void save(RemoteAmbariClusterEntity entity) {
+    entityManagerProvider.get().persist(entity);
+  }
+
+  /**
+   * Update and merge a Remote Ambari Cluster entity
+   * @param entity
+     */
+  @Transactional
+  public void update(RemoteAmbariClusterEntity entity) {
+    entityManagerProvider.get().merge(entity);
+
+  }
+
+  /**
+   * Remove a cluster entity
+   * @param clusterEntity
+     */
+  @Transactional
+  public void delete(RemoteAmbariClusterEntity clusterEntity) {
+    entityManagerProvider.get().remove(clusterEntity);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RemoteAmbariClusterEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RemoteAmbariClusterEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RemoteAmbariClusterEntity.java
new file mode 100644
index 0000000..99c9f2a
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RemoteAmbariClusterEntity.java
@@ -0,0 +1,201 @@
+/**
+ * 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.ambari.server.orm.entities;
+
+import org.apache.ambari.server.view.DefaultMasker;
+import org.apache.ambari.view.MaskException;
+import org.apache.ambari.view.Masker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.TableGenerator;
+import java.util.Collection;
+
+/**
+ * Remote Ambari Managed Cluster
+ */
+@Table(name = "remoteambaricluster")
+@TableGenerator(name = "remote_cluster_id_generator",
+  table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value"
+  , pkColumnValue = "remote_cluster_id_seq"
+  , initialValue = 1
+)
+
+@NamedQueries({
+  @NamedQuery(name = "allRemoteAmbariClusters",
+    query = "SELECT remoteAmbariCluster FROM RemoteAmbariClusterEntity remoteambaricluster"),
+  @NamedQuery(name = "remoteAmbariClusterByName", query =
+    "SELECT remoteAmbariCluster " +
+      "FROM RemoteAmbariClusterEntity remoteAmbariCluster " +
+      "WHERE remoteAmbariCluster.name=:clusterName")})
+@Entity
+public class RemoteAmbariClusterEntity {
+
+  /**
+   * The logger.
+   */
+  protected final static Logger LOG = LoggerFactory.getLogger(RemoteAmbariClusterEntity.class);
+
+  @Id
+  @Column(name = "cluster_id", nullable = false)
+  @GeneratedValue(strategy = GenerationType.TABLE, generator = "remote_cluster_id_generator")
+  private Long id;
+
+  @Column(name = "name", nullable = false, insertable = true, updatable = false)
+  private String name;
+
+  @Column(name = "url", nullable = false, insertable = true, updatable = true)
+  private String url;
+
+  @Column(name = "username", nullable = false, insertable = true, updatable = true)
+  private String username;
+
+  @Column(name = "password", nullable = false, insertable = true, updatable = true)
+  private String password;
+
+  @OneToMany(cascade = CascadeType.ALL, mappedBy = "cluster")
+  private Collection<RemoteAmbariClusterServiceEntity> services;
+
+  private static Masker masker = new DefaultMasker();
+
+  /**
+   * Get the id
+   *
+   * @return id
+   */
+  public Long getId() {
+    return id;
+  }
+
+  /**
+   * Set the id of cluster
+   *
+   * @param id
+   */
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  /**
+   * Get the name of cluster
+   *
+   * @return name
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Set the cluster name
+   *
+   * @param name
+   */
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  /**
+   * Get the cluster url
+   *
+   * @return url
+   */
+  public String getUrl() {
+    return url;
+  }
+
+  /**
+   * Set the url
+   *
+   * @param url
+   */
+  public void setUrl(String url) {
+    this.url = url;
+  }
+
+  /**
+   * Get username
+   *
+   * @return username
+   */
+  public String getUsername() {
+    return username;
+  }
+
+  /**
+   * Set the username
+   *
+   * @param username
+   */
+  public void setUsername(String username) {
+    this.username = username;
+  }
+
+  /**
+   *  Get the password
+   *
+   * @return password
+   */
+  public String getPassword() {
+    try {
+      return masker.unmask(password);
+    } catch (MaskException e) {
+      // Log exception
+      LOG.error("Unable to unmask password for Remote Cluster : "+name , e);
+    }
+    return null;
+  }
+
+  /**
+   *  Set the password
+   *
+   * @param password
+   * @throws MaskException
+   */
+  public void setPassword(String password) throws MaskException {
+    this.password = masker.mask(password);
+  }
+
+  /**
+   * Get the services installed on the cluster
+   *
+   * @return services
+   */
+  public Collection<RemoteAmbariClusterServiceEntity> getServices() {
+    return services;
+  }
+
+  /**
+   * Set the services installed on the cluster
+   *
+   * @param services
+   */
+  public void setServices(Collection<RemoteAmbariClusterServiceEntity> services) {
+    this.services = services;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RemoteAmbariClusterServiceEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RemoteAmbariClusterServiceEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RemoteAmbariClusterServiceEntity.java
new file mode 100644
index 0000000..55e8b2c
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RemoteAmbariClusterServiceEntity.java
@@ -0,0 +1,108 @@
+/**
+ * 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.ambari.server.orm.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.TableGenerator;
+
+/**
+ * Remote Ambari Service to Remote Cluster Mapping
+ */
+@Table(name = "remoteambariclusterservice")
+@TableGenerator(name = "remote_cluster_service_id_generator",
+  table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value"
+  , pkColumnValue = "remote_cluster_service_id_seq"
+  , initialValue = 1
+)
+@Entity
+public class RemoteAmbariClusterServiceEntity {
+
+  @Id
+  @Column(name = "id")
+  @GeneratedValue(strategy = GenerationType.TABLE, generator = "remote_cluster_service_id_generator")
+  private Long id;
+
+  @Column(name = "service_name", nullable = false, insertable = true, updatable = false)
+  private String serviceName;
+
+  @ManyToOne
+  @JoinColumn(name = "cluster_id", referencedColumnName = "cluster_id", nullable = false)
+  private RemoteAmbariClusterEntity cluster;
+
+  /**
+   * Get Id
+   *
+   * @return id
+   */
+  public Long getId() {
+    return id;
+  }
+
+  /**
+   * Set id for the service
+   *
+   * @param id
+   */
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  /**
+   * Get cluster attached to the service
+   *
+   * @return cluster
+   */
+  public RemoteAmbariClusterEntity getCluster() {
+    return cluster;
+  }
+
+  /**
+   * Set cluster for the service
+   *
+   * @param cluster
+   */
+  public void setCluster(RemoteAmbariClusterEntity cluster) {
+    this.cluster = cluster;
+  }
+
+  /**
+   * Get service name
+   *
+   * @return service name
+   */
+  public String getServiceName() {
+    return serviceName;
+  }
+
+  /**
+   * Set service name
+   *
+   * @param serviceName
+   */
+  public void setServiceName(String serviceName) {
+    this.serviceName = serviceName;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java
index 29dc2a7..74de530 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java
@@ -56,6 +56,9 @@ import java.util.Set;
     query = "SELECT view FROM ViewEntity view")
 @Entity
 public class ViewEntity implements ViewDefinition {
+
+  public static final String AMBARI_ONLY = "AMBARI-ONLY";
+
   /**
    * The unique view name.
    */
@@ -729,6 +732,12 @@ public class ViewEntity implements ViewDefinition {
     this.configuration       = configuration;
     this.clusterConfigurable = false;
 
+    if(configuration.getClusterConfigOptions() != null &&
+       configuration.getClusterConfigOptions().equals(AMBARI_ONLY)){
+      this.clusterConfigurable = true;
+      return;
+    }
+
     // if any of the parameters contain a cluster config element then the view is cluster configurable
     for (ParameterConfig parameterConfig : configuration.getParameters()) {
       String clusterConfig = parameterConfig.getClusterConfig();

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java
index 2d6e5ba..ba5774f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java
@@ -28,6 +28,8 @@ import javax.persistence.Basic;
 import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
@@ -122,6 +124,13 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
   private String clusterHandle;
 
   /**
+   *  Cluster Type for cluster Handle
+   */
+  @Enumerated(EnumType.STRING)
+  @Column(name = "cluster_type", nullable = false)
+  private ClusterType clusterType;
+
+  /**
    * Visible flag.
    */
   @Column
@@ -251,6 +260,7 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
     this.clusterHandle = null;
     this.visible = instanceConfig.isVisible() ? 'Y' : 'N';
     this.alterNames = 1;
+    this.clusterType = ClusterType.LOCAL_AMBARI;
 
     String label = instanceConfig.getLabel();
     this.label = (label == null || label.length() == 0) ? view.getLabel() : label;
@@ -442,6 +452,24 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
   }
 
   /**
+   *  Get the type of cluster the view instance is attached to
+   *
+   * @return clusterType the type of cluster for cluster handle
+   */
+  public ClusterType getClusterType() {
+    return clusterType;
+  }
+
+  /**
+   * Set the type of cluster for cluster handle
+   *
+   * @param clusterType
+   */
+  public void setClusterType(ClusterType clusterType) {
+    this.clusterType = clusterType;
+  }
+
+  /**
    * Set the visible flag.
    *
    * @param visible visible flag
@@ -1029,4 +1057,5 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
       return instanceName;
     }
   }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java
index 4ed4a13..3547ad3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java
@@ -75,6 +75,7 @@ import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.State;
+import org.apache.ambari.view.ClusterType;
 import org.apache.ambari.server.state.stack.WidgetLayout;
 import org.apache.ambari.server.state.stack.WidgetLayoutInfo;
 import org.apache.commons.lang.StringUtils;
@@ -219,6 +220,14 @@ public class UpgradeCatalog240 extends AbstractUpgradeCatalog {
     return "2.3.0";
   }
 
+  public static final String CLUSTER_TYPE_COLUMN = "cluster_type";
+  public static final String REMOTE_AMBARI_CLUSTER_TABLE = "remoteambaricluster";
+  public static final String REMOTE_AMBARI_CLUSTER_SERVICE_TABLE = "remoteambariclusterservice";
+
+  public static final String CLUSTER_ID = "cluster_id";
+  public static final String SERVICE_NAME = "service_name";
+  public static final String CLUSTER_NAME = "name";
+
 
   @Override
   protected void executeDDLUpdates() throws AmbariException, SQLException {
@@ -236,6 +245,32 @@ public class UpgradeCatalog240 extends AbstractUpgradeCatalog {
     updateHostRoleCommandTableDDL();
     createViewUrlTableDDL();
     updateViewInstanceEntityTable();
+    createRemoteClusterTable();
+  }
+
+  private void createRemoteClusterTable() throws SQLException {
+
+    List<DBColumnInfo> columns = new ArrayList<>();
+    LOG.info("Creating {} table", REMOTE_AMBARI_CLUSTER_TABLE);
+    columns.add(new DBColumnInfo(CLUSTER_ID, Long.class, null, null, false));
+    columns.add(new DBColumnInfo(CLUSTER_NAME, String.class, 255, null, false));
+    columns.add(new DBColumnInfo("url", String.class, 255, null, false));
+    columns.add(new DBColumnInfo("username", String.class, 255, null, false));
+    columns.add(new DBColumnInfo("password", String.class, 255, null, false));
+    dbAccessor.createTable(REMOTE_AMBARI_CLUSTER_TABLE, columns, CLUSTER_ID);
+    dbAccessor.addUniqueConstraint(REMOTE_AMBARI_CLUSTER_TABLE , "unq_remote_ambari_cluster" , CLUSTER_NAME);
+    addSequence("remote_cluster_id_seq", 1L, false);
+
+    List<DBColumnInfo> remoteClusterServiceColumns = new ArrayList<>();
+    LOG.info("Creating {} table", REMOTE_AMBARI_CLUSTER_SERVICE_TABLE);
+    remoteClusterServiceColumns.add(new DBColumnInfo(ID, Long.class, null, null, false));
+    remoteClusterServiceColumns.add(new DBColumnInfo(SERVICE_NAME, String.class, 255, null, false));
+    remoteClusterServiceColumns.add(new DBColumnInfo(CLUSTER_ID, Long.class, null, null, false));
+    dbAccessor.createTable(REMOTE_AMBARI_CLUSTER_SERVICE_TABLE, remoteClusterServiceColumns, ID);
+    dbAccessor.addFKConstraint(REMOTE_AMBARI_CLUSTER_SERVICE_TABLE, "FK_remote_ambari_cluster_id",
+      CLUSTER_ID, REMOTE_AMBARI_CLUSTER_TABLE, CLUSTER_ID, false);
+    addSequence("remote_cluster_service_id_seq", 1L, false);
+
   }
 
   private void createViewUrlTableDDL() throws SQLException {
@@ -256,6 +291,8 @@ public class UpgradeCatalog240 extends AbstractUpgradeCatalog {
       new DBColumnInfo(SHORT_URL_COLUMN, Long.class, null, null, true));
     dbAccessor.addFKConstraint(VIEWINSTANCE_TABLE, "FK_instance_url_id",
       SHORT_URL_COLUMN, VIEWURL_TABLE, URL_ID_COLUMN, false);
+    dbAccessor.addColumn(VIEWINSTANCE_TABLE,
+      new DBColumnInfo(CLUSTER_TYPE_COLUMN, String.class, 100, ClusterType.LOCAL_AMBARI, false));
   }
 
   private void updateClusterTableDDL() throws SQLException {

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariCluster.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariCluster.java b/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariCluster.java
new file mode 100644
index 0000000..f661844
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariCluster.java
@@ -0,0 +1,190 @@
+/**
+ * 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.ambari.server.view;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.entities.RemoteAmbariClusterEntity;
+import org.apache.ambari.view.AmbariHttpException;
+import org.apache.ambari.view.AmbariStreamProvider;
+import org.apache.ambari.view.cluster.Cluster;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * View associated  Remote cluster implementation.
+ */
+public class RemoteAmbariCluster implements Cluster {
+
+  private String name;
+
+  private AmbariStreamProvider streamProvider;
+
+  private final LoadingCache<String, JsonElement> configurationCache = CacheBuilder.newBuilder()
+    .expireAfterWrite(10, TimeUnit.SECONDS)
+    .build(new CacheLoader<String, JsonElement>() {
+      @Override
+      public JsonElement load(String url) throws Exception {
+        return readFromUrlJSON(url);
+      }
+    });
+
+
+  /**
+   * Constructor for Remote Ambari Cluster
+   *
+   * @param remoteAmbariClusterEntity
+   */
+  public RemoteAmbariCluster(RemoteAmbariClusterEntity remoteAmbariClusterEntity, Configuration config) {
+    String [] urlSplit = remoteAmbariClusterEntity.getUrl().split("/");
+
+    // remoteAmbariClusterEntity.getName() is not the actual name of Remote Cluster
+    // We need to extract the name from cluster url which is like. http://host:port/api/vi/clusters/${clusterName}
+    this.name = urlSplit[urlSplit.length -1];
+    
+    this.streamProvider = new RemoteAmbariStreamProvider(
+      remoteAmbariClusterEntity.getUrl(), remoteAmbariClusterEntity.getUsername(),
+      remoteAmbariClusterEntity.getPassword(),config.getRequestConnectTimeout(),config.getRequestReadTimeout());
+  }
+
+  /**
+   * Constructor for Remote Ambari Cluster
+   *
+   * @param name
+   * @param streamProvider
+   */
+  public RemoteAmbariCluster(String name, AmbariStreamProvider streamProvider) {
+    this.name = name;
+    this.streamProvider = streamProvider;
+  }
+
+  @Override
+  public String getName() {
+    return this.name;
+  }
+
+  @Override
+  public String getConfigurationValue(String type, String key) {
+    JsonElement config = null;
+    try {
+      String desiredTag = getDesiredConfig(type);
+      if(desiredTag != null){
+        config = configurationCache.get(String.format("/configurations?(type=%s&tag=%s)", type, desiredTag));
+      }
+    } catch (ExecutionException e) {
+      throw new RemoteAmbariConfigurationReadException("Can't retrieve configuration from Remote Ambari", e);
+    }
+
+    if(config == null || !config.isJsonObject()) return null;
+    JsonElement items = config.getAsJsonObject().get("items");
+
+    if(items == null || !items.isJsonArray()) return null;
+    JsonElement item = items.getAsJsonArray().get(0);
+
+    if(item == null || !item.isJsonObject()) return null;
+    JsonElement properties = item.getAsJsonObject().get("properties");
+
+    if(properties == null || !properties.isJsonObject()) return null;
+    JsonElement property = properties.getAsJsonObject().get(key);
+
+    if(property == null || !property.isJsonPrimitive()) return null;
+
+    return property.getAsJsonPrimitive().getAsString();
+  }
+
+  /**
+   * Get list of services installed on the remote cluster
+   *
+   * @return list of services Available on cluster
+   */
+  public Set<String> getServices() throws IOException, AmbariHttpException {
+    Set<String> services = new HashSet<String>();
+    String path = "?fields=services/ServiceInfo/service_name";
+    JsonElement config = configurationCache.getUnchecked(path);
+
+    if(config != null && config.isJsonObject()){
+      JsonElement items = config.getAsJsonObject().get("services");
+      if(items != null && items.isJsonArray()){
+        for (JsonElement item : items.getAsJsonArray()) {
+          JsonElement serviceInfo =  item.getAsJsonObject().get("ServiceInfo");
+          if(serviceInfo != null && serviceInfo.isJsonObject()){
+            String serviceName = serviceInfo.getAsJsonObject().get("service_name").getAsString();
+            services.add(serviceName);
+          }
+        }
+      }
+    }
+
+    return services;
+  }
+
+  /**
+   * Get the current tag for the config type
+   *
+   * @param type
+   * @return
+   * @throws ExecutionException
+   */
+  private String getDesiredConfig(String type) throws ExecutionException {
+    JsonElement desiredConfigResponse = configurationCache.get("?fields=services/ServiceInfo,hosts,Clusters");
+
+    if(desiredConfigResponse == null || !desiredConfigResponse.isJsonObject()) return null;
+    JsonElement clusters = desiredConfigResponse.getAsJsonObject().get("Clusters");
+
+    if(clusters == null || !clusters.isJsonObject()) return null;
+    JsonElement desiredConfig = clusters.getAsJsonObject().get("desired_configs");
+
+    if(desiredConfig == null || !desiredConfig.isJsonObject()) return null;
+    JsonElement desiredConfigForType = desiredConfig.getAsJsonObject().get(type);
+
+    if(desiredConfigForType == null || !desiredConfigForType.isJsonObject()) return null;
+    JsonElement typeJson = desiredConfigForType.getAsJsonObject().get("tag");
+
+    if( typeJson == null || !(typeJson.isJsonPrimitive())) return null;
+
+    return typeJson.getAsJsonPrimitive().getAsString();
+  }
+
+  /**
+   * Read the content of the url from remote cluster
+   *
+   * @param url
+   * @return
+   * @throws IOException
+   * @throws AmbariHttpException
+   */
+  private JsonElement readFromUrlJSON(String url) throws IOException, AmbariHttpException {
+    InputStream inputStream = streamProvider.readFrom(url, "GET", (String)null, null);
+    String response = IOUtils.toString(inputStream);
+    return new JsonParser().parse(response);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariClusterRegistry.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariClusterRegistry.java b/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariClusterRegistry.java
new file mode 100644
index 0000000..38a47a4
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariClusterRegistry.java
@@ -0,0 +1,116 @@
+/**
+ * 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.ambari.server.view;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.dao.RemoteAmbariClusterDAO;
+import org.apache.ambari.server.orm.entities.RemoteAmbariClusterEntity;
+import org.apache.ambari.server.orm.entities.RemoteAmbariClusterServiceEntity;
+import org.apache.ambari.view.AmbariHttpException;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Registry for Remote Ambari Cluster
+ */
+@Singleton
+public class RemoteAmbariClusterRegistry {
+
+  private ConcurrentHashMap<String,RemoteAmbariCluster> clusterMap = new ConcurrentHashMap<String,RemoteAmbariCluster>();
+
+  @Inject
+  private RemoteAmbariClusterDAO remoteAmbariClusterDAO;
+
+  @Inject
+  private Configuration configuration;
+
+  public RemoteAmbariCluster get(String clusterName){
+    RemoteAmbariCluster remoteAmbariCluster = clusterMap.get(clusterName);
+    if(remoteAmbariCluster == null){
+      RemoteAmbariCluster cluster = getCluster(clusterName);
+      RemoteAmbariCluster oldCluster = clusterMap.putIfAbsent(clusterName, cluster);
+      if(oldCluster == null) remoteAmbariCluster = cluster;
+      else remoteAmbariCluster = oldCluster;
+    }
+    return remoteAmbariCluster;
+  }
+
+
+  private RemoteAmbariCluster getCluster(String clusterName) {
+    RemoteAmbariClusterEntity remoteAmbariClusterEntity = remoteAmbariClusterDAO.findByName(clusterName);
+    RemoteAmbariCluster remoteAmbariCluster = new RemoteAmbariCluster(remoteAmbariClusterEntity, configuration);
+    return remoteAmbariCluster;
+  }
+
+  /**
+   * Update the remote cluster properties
+   *
+   * @param entity
+   */
+  public void update(RemoteAmbariClusterEntity entity){
+    remoteAmbariClusterDAO.update(entity);
+    clusterMap.remove(entity.getName());
+  }
+
+  /**
+   * Remove the cluster entity from registry and database
+   *
+   * @param entity
+   */
+  public void delete(RemoteAmbariClusterEntity entity) {
+    remoteAmbariClusterDAO.delete(entity);
+    clusterMap.remove(entity.getName());
+  }
+
+  /**
+   * Save Remote Cluster Entity after setting services.
+   *
+   * @param entity
+   * @param update
+   * @throws IOException
+   * @throws AmbariHttpException
+   */
+  public void saveOrUpdate(RemoteAmbariClusterEntity entity, boolean update) throws IOException, AmbariHttpException {
+    RemoteAmbariCluster cluster = new RemoteAmbariCluster(entity,configuration);
+    Set<String> services = cluster.getServices();
+    Collection<RemoteAmbariClusterServiceEntity> serviceEntities = new ArrayList<RemoteAmbariClusterServiceEntity>();
+
+    for (String service : services) {
+      RemoteAmbariClusterServiceEntity serviceEntity = new RemoteAmbariClusterServiceEntity();
+      serviceEntity.setServiceName(service);
+      serviceEntity.setCluster(entity);
+      serviceEntities.add(serviceEntity);
+    }
+
+    entity.setServices(serviceEntities);
+
+    if(update){
+      update(entity);
+    }else{
+      remoteAmbariClusterDAO.save(entity);
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariConfigurationReadException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariConfigurationReadException.java b/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariConfigurationReadException.java
new file mode 100644
index 0000000..a80fe35
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariConfigurationReadException.java
@@ -0,0 +1,29 @@
+/**
+ * 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.ambari.server.view;
+
+/**
+ * Exception while reading Remote Ambari Configuration
+ */
+public class RemoteAmbariConfigurationReadException extends RuntimeException{
+
+  public RemoteAmbariConfigurationReadException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariStreamProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariStreamProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariStreamProvider.java
new file mode 100644
index 0000000..f043521
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/RemoteAmbariStreamProvider.java
@@ -0,0 +1,118 @@
+/**
+ * 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.ambari.server.view;
+
+import org.apache.ambari.server.configuration.ComponentSSLConfiguration;
+import org.apache.ambari.server.controller.internal.URLStreamProvider;
+import org.apache.ambari.server.proxy.ProxyService;
+import org.apache.ambari.view.AmbariHttpException;
+import org.apache.ambari.view.AmbariStreamProvider;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provider of an input stream for a request to the Remote Ambari server.
+ */
+public class RemoteAmbariStreamProvider implements AmbariStreamProvider {
+
+  private String baseUrl;
+
+  private String username;
+
+  private String password;
+
+  private URLStreamProvider urlStreamProvider;
+
+  public RemoteAmbariStreamProvider(String baseUrl, String username, String password, int connectTimeout, int readTimeout) {
+    this.baseUrl = baseUrl;
+    this.username = username;
+    this.password = password;
+
+    ComponentSSLConfiguration sslConfiguration = ComponentSSLConfiguration.instance();
+    this.urlStreamProvider =
+      new URLStreamProvider(
+        connectTimeout,
+        readTimeout,
+        sslConfiguration.getTruststorePath(),
+        sslConfiguration.getTruststorePassword(),
+        sslConfiguration.getTruststoreType());
+  }
+
+  @Override
+  public InputStream readFrom(String path, String requestMethod, String body, Map<String, String> headers) throws IOException, AmbariHttpException {
+    return getInputStream(urlStreamProvider.processURL(getUrl(path), requestMethod, body, addHeaders(headers)));
+  }
+
+  @Override
+  public InputStream readFrom(String path, String requestMethod, InputStream body, Map<String, String> headers) throws IOException, AmbariHttpException {
+    return getInputStream(urlStreamProvider.processURL(getUrl(path), requestMethod, body, addHeaders(headers)));
+  }
+
+  private InputStream getInputStream(HttpURLConnection connection) throws IOException, AmbariHttpException {
+    int responseCode = connection.getResponseCode();
+    if(responseCode >= ProxyService.HTTP_ERROR_RANGE_START){
+      throw new AmbariHttpException(IOUtils.toString(connection.getErrorStream()),responseCode);
+    }
+    return connection.getInputStream();
+  }
+
+  private String getUrl(String path){
+    String basePath = baseUrl;
+    return path.startsWith("/") ? basePath + path : basePath + "/" + path;
+  }
+
+  private void addRequestedByHeaders(HashMap<String, String> newHeaders) {
+    newHeaders.put("X-Requested-By", "AMBARI");
+  }
+
+  private Map<String,List<String>> modifyHeaders(Map<String,String> headers){
+    Map<String, List<String>> headerMap = new HashMap<String, List<String>>();
+    for (Map.Entry<String, String> entry : headers.entrySet()) {
+      headerMap.put(entry.getKey(), Collections.singletonList(entry.getValue()));
+    }
+    return headerMap;
+  }
+
+  private Map<String, List<String>> addHeaders(Map<String, String> customHeaders) {
+    HashMap<String, String> newHeaders = new HashMap<String, String>();
+    if (customHeaders != null)
+      newHeaders.putAll(customHeaders);
+
+    addBasicAuthHeaders(newHeaders);
+    addRequestedByHeaders(newHeaders);
+    return modifyHeaders(newHeaders);
+  }
+
+  private void addBasicAuthHeaders(HashMap<String, String> headers) {
+    String authString = username + ":" + password;
+    byte[] authEncBytes = Base64.encodeBase64(authString.getBytes());
+    String authStringEnc = new String(authEncBytes);
+
+    headers.put("Authorization", "Basic " + authStringEnc);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/view/ViewAmbariStreamProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewAmbariStreamProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewAmbariStreamProvider.java
index 1dacd92..5e0f3fa 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewAmbariStreamProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewAmbariStreamProvider.java
@@ -21,11 +21,14 @@ package org.apache.ambari.server.view;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.AmbariSessionManager;
 import org.apache.ambari.server.controller.internal.URLStreamProvider;
+import org.apache.ambari.server.proxy.ProxyService;
+import org.apache.ambari.view.AmbariHttpException;
 import org.apache.ambari.view.AmbariStreamProvider;
 import org.apache.commons.io.IOUtils;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.HttpURLConnection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -73,39 +76,37 @@ public class ViewAmbariStreamProvider implements AmbariStreamProvider {
   // ----- AmbariStreamProvider -----------------------------------------------
 
   @Override
-  public InputStream readFrom(String path, String requestMethod, String body, Map<String, String> headers,
-                              boolean useAmbariSession) throws IOException {
-    return getInputStream(path, requestMethod, headers, useAmbariSession, body == null ? null : body.getBytes());
+  public InputStream readFrom(String path, String requestMethod, String body, Map<String, String> headers
+                              ) throws IOException, AmbariHttpException {
+    return getInputStream(path, requestMethod, headers, body == null ? null : body.getBytes());
   }
 
   @Override
-  public InputStream readFrom(String path, String requestMethod, InputStream body, Map<String, String> headers,
-                              boolean useAmbariSession) throws IOException {
+  public InputStream readFrom(String path, String requestMethod, InputStream body, Map<String, String> headers
+                              ) throws IOException, AmbariHttpException {
 
-    return getInputStream(path, requestMethod, headers, useAmbariSession, body == null ? null : IOUtils.toByteArray(body));
+    return getInputStream(path, requestMethod, headers, body == null ? null : IOUtils.toByteArray(body));
   }
 
 
   // ----- helper methods ----------------------------------------------------
 
-  private InputStream getInputStream(String path, String requestMethod, Map<String, String> headers,
-                                     boolean useAmbariSession, byte[] body) throws IOException {
+  private InputStream getInputStream(String path, String requestMethod, Map<String, String> headers
+                                     , byte[] body) throws IOException, AmbariHttpException {
     // add the Ambari session cookie to the given headers
-    if (useAmbariSession) {
-      String sessionId = ambariSessionManager.getCurrentSessionId();
-      if (sessionId != null) {
+    String sessionId = ambariSessionManager.getCurrentSessionId();
+    if (sessionId != null) {
 
-        String ambariSessionCookie = ambariSessionManager.getSessionCookie() + "=" + sessionId;
+      String ambariSessionCookie = ambariSessionManager.getSessionCookie() + "=" + sessionId;
 
-        if (headers == null || headers.isEmpty()) {
-          headers = Collections.singletonMap(URLStreamProvider.COOKIE, ambariSessionCookie);
-        } else {
-          headers = new HashMap<String, String>(headers);
+      if (headers == null || headers.isEmpty()) {
+        headers = Collections.singletonMap(URLStreamProvider.COOKIE, ambariSessionCookie);
+      } else {
+        headers = new HashMap<String, String>(headers);
 
-          String cookies = headers.get(URLStreamProvider.COOKIE);
+        String cookies = headers.get(URLStreamProvider.COOKIE);
 
-          headers.put(URLStreamProvider.COOKIE, URLStreamProvider.appendCookie(cookies, ambariSessionCookie));
-        }
+        headers.put(URLStreamProvider.COOKIE, URLStreamProvider.appendCookie(cookies, ambariSessionCookie));
       }
     }
 
@@ -115,8 +116,16 @@ public class ViewAmbariStreamProvider implements AmbariStreamProvider {
       headerMap.put(entry.getKey(), Collections.singletonList(entry.getValue()));
     }
 
-    return streamProvider.processURL(controller.getAmbariServerURI(path.startsWith("/") ? path : "/" + path),
-        requestMethod, body, headerMap).getInputStream();
+    return getInputStream(streamProvider.processURL(controller.getAmbariServerURI(path.startsWith("/") ? path : "/" + path),
+        requestMethod, body, headerMap));
+  }
+
+  private InputStream getInputStream(HttpURLConnection connection) throws IOException, AmbariHttpException {
+    int responseCode = connection.getResponseCode();
+    if(responseCode >= ProxyService.HTTP_ERROR_RANGE_START){
+      throw new AmbariHttpException(IOUtils.toString(connection.getErrorStream()),responseCode);
+    }
+    return connection.getInputStream();
   }
 }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java
index ba7f446..e98a4cd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java
@@ -31,6 +31,7 @@ import org.apache.ambari.server.view.persistence.DataStoreImpl;
 import org.apache.ambari.server.view.persistence.DataStoreModule;
 import org.apache.ambari.server.view.validation.ValidationException;
 import org.apache.ambari.view.AmbariStreamProvider;
+import org.apache.ambari.view.ClusterType;
 import org.apache.ambari.view.DataStore;
 import org.apache.ambari.view.ImpersonatorSetting;
 import org.apache.ambari.view.MaskException;
@@ -54,7 +55,6 @@ import org.apache.hadoop.security.authentication.util.KerberosUtil;
 import org.apache.velocity.VelocityContext;
 import org.apache.velocity.app.Velocity;
 import org.apache.velocity.exception.ParseErrorException;
-import sun.security.krb5.KrbException;
 
 import java.io.StringWriter;
 import java.io.Writer;
@@ -305,6 +305,27 @@ public class ViewContextImpl implements ViewContext, ViewController {
   }
 
   @Override
+  public AmbariStreamProvider getAmbariClusterStreamProvider() {
+
+    String clusterHandle = viewInstanceEntity.getClusterHandle();
+    ClusterType clusterType = viewInstanceEntity.getClusterType();
+
+    AmbariStreamProvider clusterStreamProvider = null;
+
+    if(clusterHandle != null && clusterType == ClusterType.LOCAL_AMBARI){
+
+      clusterStreamProvider = getAmbariStreamProvider();
+
+    } else if(clusterHandle != null && clusterType == ClusterType.REMOTE_AMBARI){
+
+      clusterStreamProvider = viewRegistry.createRemoteAmbariStreamProvider(clusterHandle);
+
+    }
+
+    return clusterStreamProvider;
+  }
+
+  @Override
   public synchronized DataStore getDataStore() {
     if (viewInstanceEntity != null) {
       if (dataStore == null) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/c150f0de/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
index d2d48a9..6d4ef82 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
@@ -45,6 +45,7 @@ import org.apache.ambari.server.orm.dao.MemberDAO;
 import org.apache.ambari.server.orm.dao.PermissionDAO;
 import org.apache.ambari.server.orm.dao.PrincipalDAO;
 import org.apache.ambari.server.orm.dao.PrivilegeDAO;
+import org.apache.ambari.server.orm.dao.RemoteAmbariClusterDAO;
 import org.apache.ambari.server.orm.dao.ResourceDAO;
 import org.apache.ambari.server.orm.dao.ResourceTypeDAO;
 import org.apache.ambari.server.orm.dao.UserDAO;
@@ -55,6 +56,7 @@ import org.apache.ambari.server.orm.entities.MemberEntity;
 import org.apache.ambari.server.orm.entities.PermissionEntity;
 import org.apache.ambari.server.orm.entities.PrincipalEntity;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
+import org.apache.ambari.server.orm.entities.RemoteAmbariClusterEntity;
 import org.apache.ambari.server.orm.entities.ResourceEntity;
 import org.apache.ambari.server.orm.entities.ResourceTypeEntity;
 import org.apache.ambari.server.orm.entities.UserEntity;
@@ -83,6 +85,8 @@ import org.apache.ambari.server.view.configuration.PropertyConfig;
 import org.apache.ambari.server.view.configuration.ResourceConfig;
 import org.apache.ambari.server.view.configuration.ViewConfig;
 import org.apache.ambari.server.view.validation.ValidationException;
+import org.apache.ambari.view.AmbariStreamProvider;
+import org.apache.ambari.view.ClusterType;
 import org.apache.ambari.view.Masker;
 import org.apache.ambari.view.SystemException;
 import org.apache.ambari.view.View;
@@ -134,6 +138,7 @@ public class ViewRegistry {
   protected static final int DEFAULT_REQUEST_READ_TIMEOUT    = 10000;
   private static final String VIEW_AMBARI_VERSION_REGEXP = "^((\\d+\\.)?)*(\\*|\\d+)$";
   private static final String VIEW_LOG_FILE = "view.log4j.properties";
+  public static final String API_PREFIX = "/api/v1/clusters/";
 
   /**
    * Thread pool
@@ -281,6 +286,18 @@ public class ViewRegistry {
   @Inject
   AmbariSessionManager ambariSessionManager;
 
+  /**
+   * Registry for Remote Ambari Cluster
+   */
+  @Inject
+  RemoteAmbariClusterRegistry remoteAmbariClusterRegistry;
+
+  /**
+   * Remote Ambari Cluster Dao
+   */
+  @Inject
+  RemoteAmbariClusterDAO remoteAmbariClusterDAO;
+
 
  // ----- Constructors -----------------------------------------------------
 
@@ -896,12 +913,14 @@ public class ViewRegistry {
     if (viewInstance != null) {
       String clusterId = viewInstance.getClusterHandle();
 
-      if (clusterId != null) {
+      if (clusterId != null && viewInstance.getClusterType() == ClusterType.LOCAL_AMBARI) {
         try {
           return new ClusterImpl(clustersProvider.get().getCluster(clusterId));
         } catch (AmbariException e) {
           LOG.warn("Could not find the cluster identified by " + clusterId + ".");
         }
+      } else if(clusterId != null && viewInstance.getClusterType() == ClusterType.REMOTE_AMBARI){
+        return remoteAmbariClusterRegistry.get(clusterId);
       }
     }
     return null;
@@ -1409,6 +1428,7 @@ public class ViewRegistry {
     instance1.setResource(instance2.getResource());
     instance1.setViewInstanceId(instance2.getViewInstanceId());
     instance1.setClusterHandle(instance2.getClusterHandle());
+    instance1.setClusterType(instance2.getClusterType());
     instance1.setData(instance2.getData());
     instance1.setEntities(instance2.getEntities());
     instance1.setProperties(instance2.getProperties());
@@ -1884,6 +1904,34 @@ public class ViewRegistry {
     return new ViewAmbariStreamProvider(streamProvider, ambariSessionManager, AmbariServer.getController());
   }
 
+  /**
+   * Get Remote Ambari Cluster Stream provider
+   *
+   * @param clusterName
+   * @return
+   */
+  protected AmbariStreamProvider createRemoteAmbariStreamProvider(String clusterName){
+    RemoteAmbariClusterEntity clusterEntity = remoteAmbariClusterDAO.findByName(clusterName);
+    if(clusterEntity != null) {
+      return new RemoteAmbariStreamProvider(getBaseurl(clusterEntity.getUrl()),
+        clusterEntity.getUsername(),clusterEntity.getPassword(),
+        configuration.getViewAmbariRequestConnectTimeout(),configuration.getViewAmbariRequestReadTimeout());
+    }
+    return null;
+  }
+
+  /**
+   *  Get baseurl of the cluster
+   *  baseurl wil be http://host:port
+   *
+   * @param url will be in format like http://host:port/api/v1/clusters/clusterName
+   * @return baseurl
+   */
+  private String getBaseurl(String url){
+    int index = url.indexOf(API_PREFIX);
+    return url.substring(0,index);
+  }
+