ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dbhowm...@apache.org
Subject [3/4] ambari git commit: AMBARI-16274. Views: Simplify View Configuration for Remote Ambari Managed Cluster. (Gaurav Nagar via dipayanb)
Date Wed, 11 May 2016 09:15:45 GMT
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);
+  }
+
 
 
 


Mime
View raw message