You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jl...@apache.org on 2016/06/27 23:36:55 UTC

[29/34] ambari git commit: AMBARI-17355 & AMBARI-17354: POC: FE & BE changes for first class support for Yarn hosted services

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupEvent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupEvent.java
new file mode 100644
index 0000000..9ff7eb9
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupEvent.java
@@ -0,0 +1,49 @@
+/**
+ * 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.events;
+
+
+/**
+ * The {@link ServiceEvent} class is the base for all service events in Ambari.
+ */
+public abstract class ServiceGroupEvent extends ClusterEvent {
+
+  /**
+   * The name of the service group.
+   */
+  protected final String m_serviceGroupName;
+
+   /**
+   * Constructor.
+   *
+   * @param eventType
+   * @param clusterId
+   */
+  public ServiceGroupEvent(AmbariEventType eventType, long clusterId, String serviceGroupName) {
+    super(eventType, clusterId);
+    m_serviceGroupName = serviceGroupName;
+  }
+
+  /**
+   * @return the service group name (never {@code null}).
+   */
+  public String getServiceGroupName() {
+    return m_serviceGroupName;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupInstalledEvent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupInstalledEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupInstalledEvent.java
new file mode 100644
index 0000000..71a6ab8
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupInstalledEvent.java
@@ -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.
+ */
+package org.apache.ambari.server.events;
+
+/**
+ * The {@link ServiceGroupInstalledEvent} class is fired when a service group is
+ * successfully installed.
+ */
+public class ServiceGroupInstalledEvent extends ServiceGroupEvent {
+  /**
+   * Constructor.
+   *
+   * @param clusterId
+   * @param serviceGroupName
+   */
+  public ServiceGroupInstalledEvent(long clusterId, String serviceGroupName) {
+    super(AmbariEventType.SERVICE_GROUP_INSTALL_SUCCESS, clusterId, serviceGroupName);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder buffer = new StringBuilder("ServiceGroupInstalledEvent{");
+    buffer.append("cluserId=").append(m_clusterId);
+    buffer.append(", serviceGroupName=").append(m_serviceGroupName);
+    buffer.append("}");
+    return buffer.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupRemovedEvent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupRemovedEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupRemovedEvent.java
new file mode 100644
index 0000000..45923a0
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceGroupRemovedEvent.java
@@ -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.
+ */
+package org.apache.ambari.server.events;
+
+/**
+ * The {@link ServiceRemovedEvent} class is fired when a service is successfully
+ * removed.
+ */
+public class ServiceGroupRemovedEvent extends ServiceGroupEvent {
+  /**
+   * Constructor.
+   *
+   * @param clusterId
+  * @param serviceGroupName
+   */
+  public ServiceGroupRemovedEvent(long clusterId, String serviceGroupName) {
+    super(AmbariEventType.SERVICE_GROUP_REMOVED_SUCCESS, clusterId, serviceGroupName);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder buffer = new StringBuilder("ServiceGroupRemovedEvent{");
+    buffer.append("cluserId=").append(m_clusterId);
+    buffer.append(", serviceGroupName=").append(m_serviceGroupName);
+    buffer.append("}");
+    return buffer.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceInstalledEvent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceInstalledEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceInstalledEvent.java
index bd5799d..0475ad9 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceInstalledEvent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceInstalledEvent.java
@@ -31,9 +31,9 @@ public class ServiceInstalledEvent extends ServiceEvent {
    * @param serviceName
    */
   public ServiceInstalledEvent(long clusterId, String stackName,
-      String stackVersion, String serviceName) {
+      String stackVersion, String serviceName, String stackServiceName, String serviceGroupName) {
     super(AmbariEventType.SERVICE_INSTALL_SUCCESS, clusterId, stackName,
-        stackVersion, serviceName);
+        stackVersion, serviceName, stackServiceName, serviceGroupName);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceRemovedEvent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceRemovedEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceRemovedEvent.java
index 7de2749..4df1651 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceRemovedEvent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceRemovedEvent.java
@@ -31,9 +31,9 @@ public class ServiceRemovedEvent extends ServiceEvent {
    * @param serviceName
    */
   public ServiceRemovedEvent(long clusterId, String stackName,
-      String stackVersion, String serviceName) {
+      String stackVersion, String serviceName, String stackServiceName, String serviceGroupName) {
     super(AmbariEventType.SERVICE_REMOVED_SUCCESS, clusterId, stackName,
-        stackVersion, serviceName);
+        stackVersion, serviceName, stackServiceName, serviceGroupName);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterServiceGroupDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterServiceGroupDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterServiceGroupDAO.java
new file mode 100644
index 0000000..efae45d
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterServiceGroupDAO.java
@@ -0,0 +1,93 @@
+/**
+ * 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 java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.ClusterServiceGroupEntity;
+import org.apache.ambari.server.orm.entities.ClusterServiceGroupEntityPK;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+
+@Singleton
+public class ClusterServiceGroupDAO {
+  @Inject
+  Provider<EntityManager> entityManagerProvider;
+  @Inject
+  DaoUtils daoUtils;
+
+  @RequiresSession
+  public ClusterServiceGroupEntity findByPK(ClusterServiceGroupEntityPK clusterServiceGroupEntityPK) {
+    return entityManagerProvider.get().find(ClusterServiceGroupEntity.class, clusterServiceGroupEntityPK);
+  }
+
+  @RequiresSession
+  public ClusterServiceGroupEntity findByClusterAndServiceGroupNames(String  clusterName, String serviceGroupName) {
+    TypedQuery<ClusterServiceGroupEntity> query = entityManagerProvider.get()
+        .createNamedQuery("clusterServiceGroupByClusterAndServiceGroupNames", ClusterServiceGroupEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("serviceGroupName", serviceGroupName);
+
+    try {
+      return query.getSingleResult();
+    } catch (NoResultException ignored) {
+      return null;
+    }
+  }
+
+  @RequiresSession
+  public List<ClusterServiceGroupEntity> findAll() {
+    return daoUtils.selectAll(entityManagerProvider.get(), ClusterServiceGroupEntity.class);
+  }
+
+  @Transactional
+  public void refresh(ClusterServiceGroupEntity clusterServiceGroupEntity) {
+    entityManagerProvider.get().refresh(clusterServiceGroupEntity);
+  }
+
+  @Transactional
+  public void create(ClusterServiceGroupEntity clusterServiceGroupEntity) {
+    entityManagerProvider.get().persist(clusterServiceGroupEntity);
+  }
+
+  @Transactional
+  public ClusterServiceGroupEntity merge(ClusterServiceGroupEntity clusterServiceGroupEntity) {
+    return entityManagerProvider.get().merge(clusterServiceGroupEntity);
+  }
+
+  @Transactional
+  public void remove(ClusterServiceGroupEntity clusterServiceGroupEntity) {
+    entityManagerProvider.get().remove(merge(clusterServiceGroupEntity));
+  }
+
+  @Transactional
+  public void removeByPK(ClusterServiceGroupEntityPK clusterServiceGroupEntityPK) {
+    ClusterServiceGroupEntity entity = findByPK(clusterServiceGroupEntityPK);
+    entityManagerProvider.get().remove(entity);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/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
index 72ab368..e817aff 100644
--- 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
@@ -72,6 +72,19 @@ public class RemoteAmbariClusterDAO {
   }
 
   /**
+   * Find Cluster by Id
+   * @param clusterId
+   * @return
+   */
+  @RequiresSession
+  public RemoteAmbariClusterEntity findById(Long clusterId) {
+    TypedQuery<RemoteAmbariClusterEntity> query = entityManagerProvider.get().
+      createNamedQuery("remoteAmbariClusterById", RemoteAmbariClusterEntity.class);
+    query.setParameter("clusterId", clusterId);
+    return daoUtils.selectSingle(query);
+  }
+
+  /**
    * Save a Cluster entity
    * @param entity
      */

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java
index bf0faee..54d4d63 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java
@@ -71,6 +71,9 @@ public class ClusterConfigEntity {
   @Column(name = "type_name")
   private String type;
 
+  @Column(name = "stack_type_name")
+  private String stackType;
+
   @Column(name = "version")
   private Long version;
 
@@ -131,6 +134,14 @@ public class ClusterConfigEntity {
     type = typeName;
   }
 
+  public String getStackType() {
+    return stackType;
+  }
+
+  public void setStackType(String stackTypeName) {
+    stackType = stackTypeName;
+  }
+
   public Long getVersion() {
     return version;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
index 2e0a15d..1875bbd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
@@ -106,6 +106,9 @@ public class ClusterEntity {
   @OneToMany(mappedBy = "clusterEntity")
   private Collection<ClusterServiceEntity> clusterServiceEntities;
 
+  @OneToMany(mappedBy = "clusterEntity")
+  private Collection<ClusterServiceGroupEntity> clusterServiceGroupEntities;
+
   @OneToOne(mappedBy = "clusterEntity", cascade = CascadeType.REMOVE)
   private ClusterStateEntity clusterStateEntity;
 
@@ -268,6 +271,14 @@ public class ClusterEntity {
     this.clusterServiceEntities = clusterServiceEntities;
   }
 
+  public Collection<ClusterServiceGroupEntity> getClusterServiceGroupEntities() {
+    return clusterServiceGroupEntities;
+  }
+
+  public void setClusterServiceGroupEntities(Collection<ClusterServiceGroupEntity> clusterServiceGroupEntities) {
+    this.clusterServiceGroupEntities = clusterServiceGroupEntities;
+  }
+
   public ClusterStateEntity getClusterStateEntity() {
     return clusterStateEntity;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceEntity.java
index 320c1be..56f42ff 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceEntity.java
@@ -41,6 +41,12 @@ public class ClusterServiceEntity {
   @Column(name = "service_name", nullable = false, insertable = true, updatable = true)
   private String serviceName;
 
+  @Column(name = "stack_service_name", nullable = false, insertable = true, updatable = true)
+  private String stackServiceName;
+
+  @Column(name = "service_group_name", nullable = false, insertable = true, updatable = true)
+  private String serviceGroupName;
+
   @Basic
   @Column(name = "service_enabled", nullable = false, insertable = true, updatable = true, length = 10)
   private Integer serviceEnabled = 0;
@@ -71,6 +77,22 @@ public class ClusterServiceEntity {
     this.serviceName = serviceName;
   }
 
+  public String getStackServiceName() {
+    return stackServiceName;
+  }
+
+  public void setStackServiceName(String stackServiceName) {
+    this.stackServiceName = stackServiceName;
+  }
+
+  public String getServiceGroupName() {
+    return serviceGroupName;
+  }
+
+  public void setServiceGroupName(String serviceGroupName) {
+    this.serviceGroupName = serviceGroupName;
+  }
+
   public int getServiceEnabled() {
     return serviceEnabled;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceGroupEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceGroupEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceGroupEntity.java
new file mode 100644
index 0000000..8f2d97e
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceGroupEntity.java
@@ -0,0 +1,153 @@
+/**
+ * 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.state.State;
+
+import javax.persistence.*;
+import java.util.Collection;
+
+@javax.persistence.IdClass(ClusterServiceGroupEntityPK.class)
+@javax.persistence.Table(name = "clusterservicegroups")
+@NamedQueries({
+    @NamedQuery(name = "clusterServiceGroupByClusterAndServiceGroupNames", query =
+        "SELECT clusterServiceGroup " +
+            "FROM ClusterServiceGroupEntity clusterServiceGroup " +
+            "JOIN clusterServiceGroup.clusterEntity cluster " +
+            "WHERE clusterServiceGroup.serviceGroupName=:serviceGroupName AND cluster.clusterName=:clusterName")
+})
+@Entity
+public class ClusterServiceGroupEntity {
+
+  @Id
+  @Column(name = "cluster_id", nullable = false, insertable = false, updatable = false, length = 10)
+  private Long clusterId;
+
+  @Id
+  @Column(name = "service_group_name", nullable = false, insertable = true, updatable = true)
+  private String serviceGroupName;
+
+  @Column(name = "service_group_display_name", nullable = false, insertable = true, updatable = true)
+  private String serviceGroupDisplayName;
+
+  @ManyToOne
+  @JoinColumn(name = "cluster_id", referencedColumnName = "cluster_id", nullable = false)
+  private ClusterEntity clusterEntity;
+
+  @Column(name = "service_group_type", nullable = false, insertable = true, updatable = true)
+  private String serviceGroupType;
+
+  @Lob
+  @Column(name = "assembly_file", nullable = true, insertable = true, updatable = true)
+  private String assemblyFile;
+
+  @Column(name = "current_state", nullable = false, insertable = true, updatable = true)
+  @Enumerated(value = EnumType.STRING)
+  private State currentState = State.INIT;
+
+  @Column(name = "desired_state", nullable = false, insertable = true, updatable = true)
+  @Enumerated(value = EnumType.STRING)
+  private State desiredState = State.INIT;
+
+  public Long getClusterId() {
+    return clusterId;
+  }
+
+  public void setClusterId(Long clusterId) {
+    this.clusterId = clusterId;
+  }
+
+
+  public String getServiceGroupName() {
+    return serviceGroupName;
+  }
+
+  public void setServiceGroupName(String serviceGroupName) {
+    this.serviceGroupName = serviceGroupName;
+  }
+
+  public String getServiceGroupDisplayName() {
+    return serviceGroupDisplayName;
+  }
+
+  public void setServiceGroupDisplayName(String serviceGroupDisplayName) {
+    this.serviceGroupDisplayName = serviceGroupDisplayName;
+  }
+
+  public String getServiceGroupType() {
+    return serviceGroupType;
+  }
+
+  public void setServiceGroupType(String serviceGroupType) {
+    this.serviceGroupType = serviceGroupType;
+  }
+
+  public String getAssemblyFile() {
+    return assemblyFile;
+  }
+
+  public void setAssemblyFile(String assemblyFile) {
+    this.assemblyFile = assemblyFile;
+  }
+
+  public State getDesiredState() {
+    return desiredState;
+  }
+
+  public void setDesiredState(State desiredState) {
+    this.desiredState = desiredState;
+  }
+
+  public State getCurrentState() {
+    return currentState;
+  }
+
+  public void setCurrentState(State currentState) {
+    this.currentState = currentState;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    ClusterServiceGroupEntity that = (ClusterServiceGroupEntity) o;
+
+    if (clusterId != null ? !clusterId.equals(that.clusterId) : that.clusterId != null) return false;
+    if (serviceGroupName != null ? !serviceGroupName.equals(that.serviceGroupName) : that.serviceGroupName != null) return false;
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = clusterId !=null ? clusterId.intValue() : 0;
+    result = 31 * result + (serviceGroupName != null ? serviceGroupName.hashCode() : 0);
+    return result;
+  }
+
+  public ClusterEntity getClusterEntity() {
+    return clusterEntity;
+  }
+
+  public void setClusterEntity(ClusterEntity clusterEntity) {
+    this.clusterEntity = clusterEntity;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceGroupEntityPK.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceGroupEntityPK.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceGroupEntityPK.java
new file mode 100644
index 0000000..c9c565e
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterServiceGroupEntityPK.java
@@ -0,0 +1,71 @@
+/**
+ * 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.Id;
+import javax.persistence.Lob;
+import java.io.Serializable;
+
+@SuppressWarnings("serial")
+public class ClusterServiceGroupEntityPK implements Serializable {
+  private Long clusterId;
+
+  @Id
+  @Column(name = "cluster_id", nullable = false, insertable = true, updatable = true, length = 10)
+  public Long getClusterId() {
+    return clusterId;
+  }
+
+  public void setClusterId(Long clusterId) {
+    this.clusterId = clusterId;
+  }
+
+  private String serviceGroupName;
+
+  @Id
+  @Column(name = "service_group_name", nullable = false, insertable = true, updatable = true)
+  public String getServiceGroupName() {
+    return serviceGroupName;
+  }
+
+  public void setServiceGroupName(String serviceGroupName) {
+    this.serviceGroupName = serviceGroupName;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    ClusterServiceGroupEntityPK that = (ClusterServiceGroupEntityPK) o;
+
+    if (clusterId != null ? !clusterId.equals(that.clusterId) : that.clusterId != null) return false;
+    if (serviceGroupName != null ? !serviceGroupName.equals(that.serviceGroupName) : that.serviceGroupName != null) return false;
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = clusterId !=null ? clusterId.intValue() : 0;
+    result = 31 * result + (serviceGroupName != null ? serviceGroupName.hashCode() : 0);
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/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
index 99c9f2a..48ca3f8 100644
--- 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
@@ -53,7 +53,11 @@ import java.util.Collection;
   @NamedQuery(name = "remoteAmbariClusterByName", query =
     "SELECT remoteAmbariCluster " +
       "FROM RemoteAmbariClusterEntity remoteAmbariCluster " +
-      "WHERE remoteAmbariCluster.name=:clusterName")})
+      "WHERE remoteAmbariCluster.name=:clusterName"),
+  @NamedQuery(name = "remoteAmbariClusterById", query =
+    "SELECT remoteAmbariCluster " +
+      "FROM RemoteAmbariClusterEntity remoteAmbariCluster " +
+      "WHERE remoteAmbariCluster.id=:clusterId")})
 @Entity
 public class RemoteAmbariClusterEntity {
 
@@ -67,7 +71,7 @@ public class RemoteAmbariClusterEntity {
   @GeneratedValue(strategy = GenerationType.TABLE, generator = "remote_cluster_id_generator")
   private Long id;
 
-  @Column(name = "name", nullable = false, insertable = true, updatable = false)
+  @Column(name = "name", nullable = false, insertable = true, updatable = true)
   private String name;
 
   @Column(name = "url", nullable = false, insertable = true, updatable = true)

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
index 7281c01..464faf7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
@@ -84,6 +84,8 @@ public class ServiceComponentDesiredStateEntity {
   @Column(name = "recovery_enabled", nullable = false, insertable = true, updatable = true)
   private Integer recoveryEnabled = 0;
 
+  @Column(name = "desired_count", nullable = false, insertable = true, updatable = true)
+  private Integer desiredCount = 0;
   /**
    * Unidirectional one-to-one association to {@link StackEntity}
    */
@@ -169,6 +171,14 @@ public class ServiceComponentDesiredStateEntity {
     this.desiredVersion = desiredVersion;
   }
 
+  public int getDesiredCount() {
+    return desiredCount;
+  }
+
+  public void setDesiredCount(int desiredCount) {
+    this.desiredCount = desiredCount;
+  }
+
   /**
    * Adds a historical entry for the version of this service component. New
    * entries are automatically created when this entities is merged via a

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/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 98bafdd..e20db3b 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
@@ -121,7 +121,7 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
    * The associated cluster handle.
    */
   @Column(name = "cluster_handle", nullable = true)
-  private String clusterHandle;
+  private Long clusterHandle;
 
   /**
    *  Cluster Type for cluster Handle
@@ -360,7 +360,7 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
   }
 
   @Override
-  public String getClusterHandle() {
+  public Long getClusterHandle() {
     return clusterHandle;
   }
 
@@ -443,11 +443,11 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
   /**
    * Set a cluster association for this view instance with the Ambari cluster
    * identified by the given cluster handle.  For a local cluster reference,
-   * the cluster handle is simply the unique cluster name.
+   * the cluster handle is simply the unique cluster id.
    *
    * @param clusterHandle  the cluster identifier
    */
-  public void setClusterHandle(String clusterHandle) {
+  public void setClusterHandle(Long clusterHandle) {
     this.clusterHandle = clusterHandle;
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/serveraction/servicegroup/ServiceGroupServerAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/servicegroup/ServiceGroupServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/servicegroup/ServiceGroupServerAction.java
new file mode 100644
index 0000000..640a0b8
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/servicegroup/ServiceGroupServerAction.java
@@ -0,0 +1,91 @@
+/*
+ * 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.serveraction.servicegroup;
+
+import com.google.gson.Gson;
+import com.google.inject.Inject;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.agent.ExecutionCommand;
+import org.apache.ambari.server.serveraction.AbstractServerAction;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public abstract class ServiceGroupServerAction extends AbstractServerAction {
+
+  protected final static Logger LOG = LoggerFactory.getLogger(ServiceGroupServerAction.class);
+
+  /**
+   * Gson
+   */
+  @Inject
+  protected Gson gson;
+
+  /**
+   * The Cluster that this ServerAction implementation is executing on
+   */
+  @Inject
+  private Clusters clusters = null;
+
+  /**
+   * Returns the relevant Cluster object
+   *
+   * @return the relevant Cluster
+   * @throws AmbariException if the Cluster object cannot be retrieved
+   */
+  protected Cluster getCluster() throws AmbariException {
+    Cluster cluster = clusters.getCluster(getClusterName());
+
+    if (cluster == null) {
+      throw new AmbariException(String.format("Failed to retrieve cluster for %s", getClusterName()));
+    }
+
+    return cluster;
+  }
+
+  /**
+   * The Clusters object for this KerberosServerAction
+   *
+   * @return a Clusters object
+   */
+  protected Clusters getClusters() {
+    return clusters;
+  }
+
+  /**
+   * Returns the relevant cluster's name
+   * <p/>
+   * Using the data from the execution command, retrieve the relevant cluster's name.
+   *
+   * @return a String declaring the relevant cluster's name
+   * @throws AmbariException if the cluster's name is not available
+   */
+  protected String getClusterName() throws AmbariException {
+    ExecutionCommand executionCommand = getExecutionCommand();
+    String clusterName = (executionCommand == null) ? null : executionCommand.getClusterName();
+
+    if ((clusterName == null) || clusterName.isEmpty()) {
+      throw new AmbariException("Failed to retrieve the cluster name from the execution command");
+    }
+
+    return clusterName;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/serveraction/servicegroup/YarnServiceGroupServerAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/servicegroup/YarnServiceGroupServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/servicegroup/YarnServiceGroupServerAction.java
new file mode 100644
index 0000000..57b113a
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/servicegroup/YarnServiceGroupServerAction.java
@@ -0,0 +1,171 @@
+/*
+ * 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.serveraction.servicegroup;
+
+import com.google.gson.reflect.TypeToken;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.actionmanager.HostRoleStatus;
+import org.apache.ambari.server.agent.CommandReport;
+import org.apache.ambari.server.agent.ExecutionCommand;
+import org.apache.ambari.server.controller.ServiceComponentHostResponse;
+import org.apache.ambari.server.state.*;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+public class YarnServiceGroupServerAction extends ServiceGroupServerAction {
+  public static String COMMAND_PARAM_SERVICE_GROUP = "service_group";
+  public static String COMMAND_PARAM_DESIRED_STATE = "desired_state";
+  public static String COMMAND_PARAM_DASH_API_ENDPOINT = "dash_api_endpoint";
+
+  @Override
+  public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws AmbariException, InterruptedException {
+
+    Cluster cluster = getCluster();
+    if(cluster == null) {
+      throw new AmbariException("Missing cluster object");
+    }
+    Map<String, String> commandParams = getCommandParameters();
+    if(commandParams == null) {
+      throw new AmbariException("Missing command params");
+    }
+    if(!commandParams.containsKey(COMMAND_PARAM_SERVICE_GROUP)) {
+      throw new AmbariException("Missing service group");
+    }
+    if(!commandParams.containsKey(COMMAND_PARAM_DESIRED_STATE)) {
+      throw new AmbariException("Missing desired state");
+    }
+    if(!commandParams.containsKey(COMMAND_PARAM_DASH_API_ENDPOINT)) {
+      throw new AmbariException("Missing Dash API endpoint");
+    }
+
+    String serviceGroupName = commandParams.get(COMMAND_PARAM_SERVICE_GROUP);
+    State desiredState = State.valueOf(commandParams.get(COMMAND_PARAM_DESIRED_STATE));
+    String dashApiEndpoint = commandParams.get(COMMAND_PARAM_DASH_API_ENDPOINT);
+
+    ServiceGroup sg = cluster.getServiceGroup(serviceGroupName);
+    String sgType = sg.getServiceGroupType();
+
+    if(!sgType.equalsIgnoreCase("YARN")) {
+      throw new AmbariException("Invalid service group type " + sgType);
+    }
+    if(StringUtils.isBlank(sg.getAssemblyFile())) {
+      throw new AmbariException("Assembly file contents not set");
+    }
+
+
+    actionLog.writeStdOut("=================================================");
+    actionLog.writeStdOut("Service Group : " + serviceGroupName);
+    actionLog.writeStdOut("Desired State : " + desiredState);
+    actionLog.writeStdOut("Dash API Endpoint : " + dashApiEndpoint);
+    actionLog.writeStdOut("=================================================");
+
+    if(desiredState == State.INSTALLED && sg.getCurrentState() == State.INIT) {
+      // Installing Service Group
+      actionLog.writeStdOut("Installing service group " + serviceGroupName);
+      try {
+        URL url = new URL(dashApiEndpoint);
+        HttpURLConnection httpRequest = (HttpURLConnection) url.openConnection();
+        httpRequest.setDoOutput(true);
+        httpRequest.setDoInput(true);
+        httpRequest.setRequestMethod("POST");
+        httpRequest.setRequestProperty("Content-Type", "application/json");
+        httpRequest.setRequestProperty("Content-Length", String.valueOf(sg.getAssemblyFile().length()));
+        httpRequest.setRequestProperty("Accept", "application/json");
+
+        OutputStream os = httpRequest.getOutputStream();
+        os.write(sg.getAssemblyFile().getBytes());
+        int responseCode = httpRequest.getResponseCode();
+        actionLog.writeStdOut("Dash response code = " + responseCode);
+        if (responseCode != 202) {
+          String errorMessage = "Failed to install service group " + serviceGroupName + " via Dash API endpoint " + dashApiEndpoint;
+          actionLog.writeStdErr(errorMessage);
+          throw new AmbariException(errorMessage);
+        }
+      } catch (Exception e) {
+        String errorMessage = "Hit exception while installing service group " + serviceGroupName + " via Dash API endpoint " + dashApiEndpoint;
+        actionLog.writeStdErr(errorMessage);
+        actionLog.writeStdErr("Exception = " + e.getMessage());
+        throw new AmbariException(errorMessage, e);
+      }
+      sg.setCurrentState(State.INSTALLED);
+      actionLog.writeStdOut("Service group " + serviceGroupName + " successfully installed");
+    } else if(desiredState == State.STARTED && sg.getCurrentState() == State.INSTALLED) {
+      // Starting service group
+      actionLog.writeStdOut("Waiting for service group " + serviceGroupName + " to start");
+      String urlString = dashApiEndpoint + "/" + serviceGroupName.toLowerCase();
+      Boolean started = false;
+      while(!started) {
+        try {
+          URL url = new URL(urlString);
+          HttpURLConnection httpRequest = (HttpURLConnection) url.openConnection();
+          InputStream inputStream = httpRequest.getInputStream();
+          String jsonResponse = IOUtils.toString(inputStream, "UTF-8");
+          actionLog.writeStdOut("*****************************************************");
+          actionLog.writeStdOut("Get Response:");
+          actionLog.writeStdOut(jsonResponse);
+          actionLog.writeStdOut("*****************************************************");
+          Map<String, Object> responseMap = gson.fromJson(jsonResponse, new TypeToken<Map<String, Object>>() {
+          }.getType());
+
+          if (responseMap != null && responseMap.containsKey("state") && responseMap.containsKey("containers")) {
+            started = true;
+            String appState = (String) responseMap.get("state");
+            if (appState.equalsIgnoreCase("RUNNING")) {
+              for (Map<String, String> cMap : (ArrayList<Map<String, String>>) responseMap.get("containers")) {
+                String containerState = cMap.get("state");
+                if (!containerState.equalsIgnoreCase("READY")) {
+                  started = false;
+                  break;
+                }
+              }
+            } else {
+              started = false;
+            }
+          }
+        } catch (Exception e) {
+          String errorMessage = "Hit exception while waiting for service group " + serviceGroupName + " to start via Dash API endpoint " + dashApiEndpoint;
+          actionLog.writeStdErr(errorMessage);
+          actionLog.writeStdErr("Exception = " + e.getMessage());
+          actionLog.writeStdErr("Will retry!");
+        }
+        if (!started) {
+          Thread.sleep(1000);
+        }
+      }
+      sg.setCurrentState(State.STARTED);
+      actionLog.writeStdOut("Service group " + serviceGroupName + " successfully started");
+    } else if(desiredState == State.INSTALLED && sg.getCurrentState() == State.STARTED) {
+      // Stopping service group
+      actionLog.writeStdOut("Stopping service group " + serviceGroupName);
+      sg.setCurrentState(State.INSTALLED);
+      actionLog.writeStdOut("Service group " + serviceGroupName + "successfully stopped");
+    }
+    sg.persist();
+    return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index d9df8b4..5f64460 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -25,6 +25,7 @@ import java.util.Set;
 import java.util.concurrent.locks.ReadWriteLock;
 
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.ServiceGroupNotFoundException;
 import org.apache.ambari.server.controller.ClusterResponse;
 import org.apache.ambari.server.controller.ServiceConfigVersionResponse;
 import org.apache.ambari.server.events.ClusterConfigChangedEvent;
@@ -68,6 +69,15 @@ public interface Cluster {
    */
   void addService(Service service);
 
+
+  /**
+   * Add service group to the cluster
+   * @param serviceGroup
+   * @return
+   * @throws AmbariException
+   */
+  void addServiceGroup(ServiceGroup serviceGroup);
+
   /**
    * Get a service
    * @param serviceName
@@ -82,6 +92,19 @@ public interface Cluster {
   Map<String, Service> getServices();
 
   /**
+   * Get a service group
+   * @param serviceGroupName
+   * @return
+   */
+  ServiceGroup getServiceGroup(String serviceGroupName) throws ServiceGroupNotFoundException;
+
+  /**
+   * Get all service groups
+   * @return
+   */
+  Map<String, ServiceGroup> getServiceGroups() throws AmbariException;
+
+  /**
    * Get all ServiceComponentHosts on a given host
    * @param hostname
    * @return
@@ -413,6 +436,8 @@ public interface Cluster {
 
   String getServiceForConfigTypes(Collection<String> configTypes);
 
+  String getStackConfigTypeFromConfigType(String configType);
+
   /**
    * Apply specified service config version (rollback)
    * @param serviceName service name
@@ -502,6 +527,19 @@ public interface Cluster {
   void deleteService(String serviceName) throws AmbariException;
 
   /**
+   * Delete all the service groups associated with this cluster
+   * @throws AmbariException
+   */
+  void deleteAllServiceGroups() throws AmbariException;
+
+  /**
+   * Delete the named service associated with this cluster
+   * @param serviceGroupName
+   * @throws AmbariException
+   */
+  void deleteServiceGroup(String serviceGroupName) throws AmbariException;
+
+  /**
    * Gets if the cluster can be deleted
    * @return
    */
@@ -516,10 +554,12 @@ public interface Cluster {
   /**
    * Add service to the cluster
    * @param serviceName
+   * @param stackServiceName
+   * @param serviceGroupName
    * @return
    * @throws AmbariException
    */
-  Service addService(String serviceName) throws AmbariException;
+  Service addService(String serviceName, String stackServiceName, String serviceGroupName) throws AmbariException;
 
   /**
    * Get lock to control access to cluster structure

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
index e660d20..bd9de13 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
@@ -62,6 +62,14 @@ public interface Clusters {
       throws AmbariException;
 
   /**
+   * Gets the Cluster given the cluster id
+   * @param clusterId Id of the Cluster to retrieve
+   * @return  <code>Cluster</code> identified by the given id
+   */
+  Cluster getCluster(Long clusterId)
+    throws AmbariException;
+
+  /**
    * Get all clusters
    * @return <code>Map</code> of clusters with cluster name as key
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
index b35aad9..f83f550 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
@@ -38,6 +38,11 @@ public interface Config {
   public String getType();
 
   /**
+   * @return Stack Config Type
+   */
+  public String getStackType();
+
+  /**
    * @return Version Tag this config instance is mapped to
    */
   public String getTag();

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
index dbdd5a2..6bf725b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
@@ -57,6 +57,7 @@ public class ConfigImpl implements Config {
   private Cluster cluster;
   private StackId stackId;
   private String type;
+  private String stackType;
   private volatile String tag;
   private volatile Long version;
   private volatile Map<String, String> properties;
@@ -81,6 +82,10 @@ public class ConfigImpl implements Config {
       @Assisted Map<String, Map<String, String>> propertiesAttributes, Injector injector) {
     this.cluster = cluster;
     this.type = type;
+    this.stackType = cluster.getStackConfigTypeFromConfigType(type);
+    if(StringUtils.isBlank(this.stackType)) {
+      this.stackType = type;
+    }
     this.properties = properties;
     this.propertiesAttributes = propertiesAttributes;
 
@@ -97,6 +102,7 @@ public class ConfigImpl implements Config {
   public ConfigImpl(@Assisted Cluster cluster, @Assisted ClusterConfigEntity entity, Injector injector) {
     this.cluster = cluster;
     type = entity.getType();
+    stackType = entity.getStackType();
     tag = entity.getTag();
     version = entity.getVersion();
 
@@ -168,7 +174,16 @@ public class ConfigImpl implements Config {
     } finally {
       readWriteLock.readLock().unlock();
     }
+  }
 
+  @Override
+  public String getStackType() {
+    readWriteLock.readLock().lock();
+    try {
+      return stackType;
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
   }
 
   @Override
@@ -362,6 +377,7 @@ public class ConfigImpl implements Config {
           entity.setClusterEntity(clusterEntity);
           entity.setClusterId(cluster.getClusterId());
           entity.setType(getType());
+          entity.setStackType(getStackType());
           entity.setVersion(getVersion());
           entity.setTag(getTag());
           entity.setTimestamp(new Date().getTime());

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/Service.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Service.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Service.java
index 7000574..ead72ca 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Service.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Service.java
@@ -28,6 +28,10 @@ public interface Service {
 
   String getName();
 
+  String getStackServiceName();
+
+  String getServiceGroupName();
+
   long getClusterId();
 
   Cluster getCluster();

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
index 983cbdf..643587f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
@@ -44,6 +44,10 @@ public interface ServiceComponent {
 
   String getServiceName();
 
+  String getStackServiceName();
+
+  String getServiceGroupName();
+
   long getClusterId();
 
   String getClusterName();
@@ -60,6 +64,10 @@ public interface ServiceComponent {
 
   void setDesiredVersion(String version);
 
+  int getDesiredCount();
+
+  void setDesiredCount(int desiredCount);
+
   Map<String, ServiceComponentHost> getServiceComponentHosts();
 
   ServiceComponentHost getServiceComponentHost(String hostname)

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
index 4866148..915d08a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
@@ -46,6 +46,18 @@ public interface ServiceComponentHost {
   String getServiceName();
 
   /**
+   * Get the Real Service this object maps to
+   * @return Name of the Real Service
+   */
+  String getStackServiceName();
+
+  /**
+   * Get the service group name this object maps to
+   * @return Name of the service group
+   */
+  String getServiceGroupName();
+
+  /**
    * Get the ServiceComponent this object maps to
    * @return Name of the ServiceComponent
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
index 9283e38..c5ca4bf 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
@@ -18,16 +18,24 @@
 
 package org.apache.ambari.server.state;
 
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.ObjectNotFoundException;
 import org.apache.ambari.server.ServiceComponentHostNotFoundException;
+import org.apache.ambari.server.ServiceGroupNotFoundException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.controller.ServiceComponentHostResponse;
 import org.apache.ambari.server.controller.ServiceComponentResponse;
 import org.apache.ambari.server.events.ServiceComponentRecoveryChangedEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
@@ -43,6 +51,9 @@ import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
 import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
 import org.apache.ambari.server.state.cluster.ClusterImpl;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.utils.MapUtils;
+import org.apache.commons.io.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -139,7 +150,7 @@ public class ServiceComponentImpl implements ServiceComponent {
     StackId stackId = service.getDesiredStackVersion();
     try {
       ComponentInfo compInfo = ambariMetaInfo.getComponent(
-        stackId.getStackName(), stackId.getStackVersion(), service.getName(),
+        stackId.getStackName(), stackId.getStackVersion(), service.getStackServiceName(),
         componentName);
       isClientComponent = compInfo.isClient();
       isMasterComponent = compInfo.isMaster();
@@ -250,6 +261,16 @@ public class ServiceComponentImpl implements ServiceComponent {
   }
 
   @Override
+  public String getStackServiceName() {
+    return service.getStackServiceName();
+  }
+
+  @Override
+  public String getServiceGroupName() {
+    return service.getServiceGroupName();
+  }
+
+  @Override
   public long getClusterId() {
     return service.getClusterId();
   }
@@ -515,6 +536,33 @@ public class ServiceComponentImpl implements ServiceComponent {
   }
 
   @Override
+  public int getDesiredCount() {
+    readWriteLock.readLock().lock();
+    try {
+      return getDesiredStateEntity().getDesiredCount();
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  @Override
+  public void setDesiredCount(int desiredCount) {
+    readWriteLock.writeLock().lock();
+    try {
+      ServiceComponentDesiredStateEntity desiredStateEntity = getDesiredStateEntity();
+      if (desiredStateEntity != null) {
+        desiredStateEntity.setDesiredCount(desiredCount);
+        saveIfPersisted(desiredStateEntity);
+      } else {
+        LOG.warn("Setting a member on an entity object that may have been " +
+            "previously deleted, serviceName = " + (service != null ? service.getName() : ""));
+      }
+    } finally {
+      readWriteLock.writeLock().unlock();
+    }
+  }
+
+  @Override
   public ServiceComponentResponse convertToResponse() {
     readWriteLock.readLock().lock();
     try {
@@ -522,7 +570,7 @@ public class ServiceComponentImpl implements ServiceComponent {
       ServiceComponentResponse r = new ServiceComponentResponse(getClusterId(),
           cluster.getClusterName(), service.getName(), getName(),
           getDesiredStackVersion().getStackId(), getDesiredState().toString(),
-          getTotalCount(), getStartedCount(), getInstalledCount(),
+          getTotalCount(), getStartedCount(), getInstalledCount(), getDesiredCount(),
           isRecoveryEnabled(), displayName);
       return r;
     } finally {
@@ -799,10 +847,19 @@ public class ServiceComponentImpl implements ServiceComponent {
   }
 
   private int getSCHCountByState(State state) {
+    Cluster cluster = service.getCluster();
+    ServiceGroup sg = null;
+    try {
+      sg = cluster.getServiceGroup(getServiceGroupName());
+    } catch (ServiceGroupNotFoundException e) {
+      sg = null;
+    }
     int count = 0;
+    if(sg == null || sg.getServiceGroupType().equalsIgnoreCase("AMBARI")) {
     for (ServiceComponentHost sch : hostComponents.values()) {
-      if (sch.getState() == state) {
-        count++;
+        if (sch.getState() == state) {
+          count++;
+        }
       }
     }
     return count;
@@ -817,7 +874,18 @@ public class ServiceComponentImpl implements ServiceComponent {
   }
 
   private int getTotalCount() {
-    return hostComponents.size();
+    Cluster cluster = service.getCluster();
+    ServiceGroup sg = null;
+    try {
+      sg = cluster.getServiceGroup(getServiceGroupName());
+    } catch (ServiceGroupNotFoundException e) {
+      sg = null;
+    }
+    if(sg == null || sg.getServiceGroupType().equalsIgnoreCase("AMBARI")) {
+      return hostComponents.size();
+    } else {
+      return getDesiredCount();
+    }
   }
 
   // Refresh cached reference after ever setter
@@ -825,7 +893,6 @@ public class ServiceComponentImpl implements ServiceComponent {
     if (!isPersisted()) {
       return desiredStateEntity;
     }
-
     return serviceComponentDesiredStateDAO.findById(desiredStateEntity.getId());
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceFactory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceFactory.java
index a3a041b..16d0c7a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceFactory.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceFactory.java
@@ -18,11 +18,15 @@
 
 package org.apache.ambari.server.state;
 
+import com.google.inject.assistedinject.Assisted;
 import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
 
 public interface ServiceFactory {
 
-  Service createNew(Cluster cluster, String serviceName);
+  Service createNew(Cluster cluster,
+                    @Assisted("serviceName") String serviceName,
+                    @Assisted("stackServiceName") String stackServiceName,
+                    @Assisted("serviceGroupName") String serviceGroupName);
 
   Service createExisting(Cluster cluster, ClusterServiceEntity serviceEntity);
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroup.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroup.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroup.java
new file mode 100644
index 0000000..df86b50
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroup.java
@@ -0,0 +1,79 @@
+/**
+ * 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.state;
+
+import java.util.Map;
+import java.util.concurrent.locks.ReadWriteLock;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.ServiceGroupResponse;
+
+public interface ServiceGroup {
+
+  String getName();
+
+  String getServiceGroupDisplayName();
+
+  void setServiceGroupDisplayName(String displayName);
+
+  String getServiceGroupType();
+
+  String getAssemblyFile();
+
+  void setAssemblyFile(String assemblyFile);
+
+  long getClusterId();
+
+  Cluster getCluster();
+
+  State getDesiredState();
+
+  void setDesiredState(State state);
+
+  State getCurrentState();
+
+  void setCurrentState(State state);
+
+  ServiceGroupResponse convertToResponse();
+
+  void debugDump(StringBuilder sb);
+
+  boolean isPersisted();
+
+  void persist();
+
+  void refresh();
+
+  /**
+   * Find out whether the service and its components
+   * are in a state that it can be removed from a cluster
+   * @return
+   */
+  boolean canBeRemoved();
+
+  void delete() throws AmbariException;
+
+  /**
+   * Get lock to control access to cluster structure
+   * @return cluster-global lock
+   */
+  ReadWriteLock getClusterGlobalLock();
+
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroupFactory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroupFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroupFactory.java
new file mode 100644
index 0000000..5c4effa
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroupFactory.java
@@ -0,0 +1,35 @@
+/**
+ * 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.state;
+
+import com.google.inject.assistedinject.Assisted;
+import org.apache.ambari.server.orm.entities.ClusterServiceGroupEntity;
+
+public interface ServiceGroupFactory {
+
+  ServiceGroup createNew(Cluster cluster,
+      @Assisted("serviceGroupName") String serviceGroupName,
+      @Assisted("serviceGroupDisplayName") String serviceGroupDisplayName,
+      @Assisted("serviceGroupType") String serviceGroupType,
+      @Assisted("assemblyFile") String assemblyFile,
+      @Assisted("desiredState") State desiredState,
+      @Assisted("currentState") State currentState);
+
+  ServiceGroup createExisting(Cluster cluster, ClusterServiceGroupEntity serviceGroupEntity);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroupImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroupImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroupImpl.java
new file mode 100644
index 0000000..89fedb9
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceGroupImpl.java
@@ -0,0 +1,378 @@
+/**
+ * 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.state;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.ProvisionException;
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.controller.ServiceGroupResponse;
+import org.apache.ambari.server.events.MaintenanceModeEvent;
+import org.apache.ambari.server.events.ServiceGroupInstalledEvent;
+import org.apache.ambari.server.events.ServiceGroupRemovedEvent;
+import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.apache.ambari.server.orm.dao.ClusterDAO;
+import org.apache.ambari.server.orm.dao.ClusterServiceGroupDAO;
+import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
+import org.apache.ambari.server.orm.entities.ClusterConfigMappingEntity;
+import org.apache.ambari.server.orm.entities.ClusterEntity;
+import org.apache.ambari.server.orm.entities.ClusterServiceGroupEntity;
+import org.apache.ambari.server.orm.entities.ClusterServiceGroupEntityPK;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+
+public class ServiceGroupImpl implements ServiceGroup {
+  private final ReadWriteLock clusterGlobalLock;
+  private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
+  // Cached entity has only 1 getter for name
+  private ClusterServiceGroupEntity serviceGroupEntity;
+  private ClusterServiceGroupEntityPK serviceGroupEntityPK;
+
+  private static final Logger LOG = LoggerFactory.getLogger(ServiceImpl.class);
+
+  private volatile boolean persisted = false;
+  private final Cluster cluster;
+
+  @Inject
+  private ClusterServiceGroupDAO clusterServiceGroupDAO;
+  @Inject
+  private ClusterDAO clusterDAO;
+  @Inject
+  private AmbariMetaInfo ambariMetaInfo;
+
+  /**
+   * Used to publish events relating to service group CRUD operations.
+   */
+  @Inject
+  private AmbariEventPublisher eventPublisher;
+
+  private final Gson gson;
+  private Map<String, Object> responseMap = null;
+  private long lastFetchTimestamp = 0;
+
+  private void init() {
+    // TODO load from DB during restart?
+  }
+
+  @AssistedInject
+  public ServiceGroupImpl(@Assisted Cluster cluster,
+      @Assisted("serviceGroupName") String serviceGroupName,
+      @Assisted("serviceGroupDisplayName") String serviceGroupDisplayName,
+      @Assisted("serviceGroupType") String serviceGroupType,
+      @Assisted("assemblyFile") String assemblyFile,
+      @Assisted("desiredState") State desiredState,
+      @Assisted("currentState") State currentState,
+      Injector injector) throws AmbariException {
+    injector.injectMembers(this);
+    gson = injector.getInstance(Gson.class);
+
+    clusterGlobalLock = cluster.getClusterGlobalLock();
+    serviceGroupEntity = new ClusterServiceGroupEntity();
+    serviceGroupEntity.setClusterId(cluster.getClusterId());
+    serviceGroupEntity.setServiceGroupName(serviceGroupName);
+    serviceGroupEntity.setServiceGroupDisplayName(serviceGroupDisplayName);
+    serviceGroupEntity.setServiceGroupType(serviceGroupType);
+    serviceGroupEntity.setAssemblyFile(assemblyFile);
+    serviceGroupEntity.setDesiredState(desiredState);
+    serviceGroupEntity.setCurrentState(currentState);
+    this.cluster = cluster;
+    init();
+  }
+
+  private void populateResponseMap() {
+    Map<String, String> configs = ambariMetaInfo.getAmbariServerProperties();
+    String dashEndpoint = configs.containsKey(Configuration.YARN_DASH_API_ENDPOINT)?
+        configs.get(Configuration.YARN_DASH_API_ENDPOINT) : Configuration.YARN_DASH_API_ENDPOINT_DEFAULT;
+    String urlString = dashEndpoint + "/" +  getName().toLowerCase();
+    try {
+      URL url = new URL(urlString);
+      HttpURLConnection httpRequest = (HttpURLConnection) url.openConnection();
+      InputStream inputStream = httpRequest.getInputStream();
+      String jsonResponse = IOUtils.toString(inputStream, "UTF-8");
+      responseMap = gson.fromJson(jsonResponse, new TypeToken<Map<String, Object>>() {
+      }.getType());
+      lastFetchTimestamp = System.currentTimeMillis();
+    } catch (Exception e) {
+      LOG.error("Failed to get response from DASH endpoint " + dashEndpoint);
+      responseMap = null;
+    }
+  }
+
+  @AssistedInject
+  public ServiceGroupImpl(@Assisted Cluster cluster, @Assisted ClusterServiceGroupEntity
+      serviceGroupEntity, Injector injector) throws AmbariException {
+    injector.injectMembers(this);
+    gson = injector.getInstance(Gson.class);
+    clusterGlobalLock = cluster.getClusterGlobalLock();
+    this.serviceGroupEntity = serviceGroupEntity;
+    this.cluster = cluster;
+    persisted = true;
+  }
+
+  @Override
+  public ReadWriteLock getClusterGlobalLock() {
+    return clusterGlobalLock;
+  }
+
+  @Override
+  public String getName() {
+    return serviceGroupEntity.getServiceGroupName();
+  }
+
+  @Override
+  public String getServiceGroupDisplayName() {
+    return serviceGroupEntity.getServiceGroupDisplayName();
+  }
+
+  @Override
+  public void setServiceGroupDisplayName(String displayName) {
+    serviceGroupEntity.setServiceGroupDisplayName(displayName);
+  }
+
+  @Override
+  public String getServiceGroupType() {
+    return serviceGroupEntity.getServiceGroupType();
+  }
+
+  @Override
+  public String getAssemblyFile() {
+    return serviceGroupEntity.getAssemblyFile();
+  }
+
+  @Override
+  public void setAssemblyFile(String assemblyFile) {
+    serviceGroupEntity.setAssemblyFile(assemblyFile);
+  }
+
+  @Override
+  public long getClusterId() {
+    return cluster.getClusterId();
+  }
+
+  @Override
+  public State getDesiredState() {
+    return serviceGroupEntity.getDesiredState();
+  }
+
+  @Override
+  public void setDesiredState(State state) {
+    serviceGroupEntity.setDesiredState(state);
+  }
+
+  @Override
+  public State getCurrentState() {
+    return serviceGroupEntity.getCurrentState();
+  }
+
+  @Override
+  public void setCurrentState(State state) {
+    serviceGroupEntity.setCurrentState(state);
+  }
+
+  @Override
+  public ServiceGroupResponse convertToResponse() {
+    readWriteLock.readLock().lock();
+    try {
+      ServiceGroupResponse r = new ServiceGroupResponse(cluster.getClusterId(),
+          cluster.getClusterName(), getName(), getServiceGroupDisplayName(),
+          getServiceGroupType(), getAssemblyFile(), getDesiredState().toString(), getCurrentState().toString());
+      return r;
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  @Override
+  public Cluster getCluster() {
+    return cluster;
+  }
+
+  @Override
+  public void debugDump(StringBuilder sb) {
+    readWriteLock.readLock().lock();
+    try {
+      sb.append("ServiceGroup={ serviceGroupName=" + getName()  + ", clusterName="
+          + cluster.getClusterName() + ", clusterId=" + cluster.getClusterId() + "}");
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean isPersisted() {
+    // a lock around this internal state variable is not required since we
+    // have appropriate locks in the persist() method and this member is
+    // only ever false under the condition that the object is new
+    return persisted;
+  }
+
+  /**
+   * {@inheritDoc}
+   * <p/>
+   * This method uses Java locks and then delegates to internal methods which
+   * perform the JPA merges inside of a transaction. Because of this, a
+   * transaction is not necessary before this calling this method.
+   */
+  @Override
+  public void persist() {
+    clusterGlobalLock.writeLock().lock();
+    try {
+      readWriteLock.writeLock().lock();
+      try {
+        if (!persisted) {
+          persistEntities();
+          refresh();
+          // There refresh calls are no longer needed with cached references
+          // not used on getters/setters
+          // cluster.refresh();
+          persisted = true;
+
+          // publish the service installed event
+          StackId stackId = cluster.getDesiredStackVersion();
+          cluster.addServiceGroup(this);
+
+          ServiceGroupInstalledEvent event = new ServiceGroupInstalledEvent(
+              getClusterId(), getName());
+          eventPublisher.publish(event);
+        } else {
+          saveIfPersisted();
+        }
+      } finally {
+        readWriteLock.writeLock().unlock();
+      }
+    } finally {
+      clusterGlobalLock.writeLock().unlock();
+    }
+  }
+
+  @Transactional
+  protected void persistEntities() {
+    long clusterId = cluster.getClusterId();
+
+    ClusterEntity clusterEntity = clusterDAO.findById(clusterId);
+    serviceGroupEntity.setClusterEntity(clusterEntity);
+    clusterServiceGroupDAO.create(serviceGroupEntity);
+    clusterEntity.getClusterServiceGroupEntities().add(serviceGroupEntity);
+    clusterDAO.merge(clusterEntity);
+    clusterServiceGroupDAO.merge(serviceGroupEntity);
+  }
+
+  @Transactional
+  void saveIfPersisted() {
+    if (isPersisted()) {
+      clusterServiceGroupDAO.merge(serviceGroupEntity);
+    }
+  }
+
+  @Override
+  @Transactional
+  public void refresh() {
+    readWriteLock.writeLock().lock();
+    try {
+      if (isPersisted()) {
+        ClusterServiceGroupEntityPK pk = new ClusterServiceGroupEntityPK();
+        pk.setClusterId(getClusterId());
+        pk.setServiceGroupName(getName());
+        serviceGroupEntity = clusterServiceGroupDAO.findByPK(pk);
+        clusterServiceGroupDAO.refresh(serviceGroupEntity);
+      }
+    } finally {
+      readWriteLock.writeLock().unlock();
+    }
+  }
+
+  @Override
+  public boolean canBeRemoved() {
+    clusterGlobalLock.readLock().lock();
+    try {
+      readWriteLock.readLock().lock();
+      try {
+        // TODO: Add check for services in the service group
+        return true;
+      } finally {
+        readWriteLock.readLock().unlock();
+      }
+    } finally {
+      clusterGlobalLock.readLock().unlock();
+    }
+  }
+
+
+  @Override
+  @Transactional
+  public void delete() throws AmbariException {
+    clusterGlobalLock.writeLock().lock();
+    try {
+      readWriteLock.writeLock().lock();
+      try {
+
+        if (persisted) {
+          removeEntities();
+          persisted = false;
+
+          // publish the service removed event
+          ServiceGroupRemovedEvent event = new ServiceGroupRemovedEvent(getClusterId(), getName());
+
+          eventPublisher.publish(event);
+        }
+      } finally {
+        readWriteLock.writeLock().unlock();
+      }
+    } finally {
+      clusterGlobalLock.writeLock().unlock();
+    }
+
+
+  }
+
+  @Transactional
+  protected void removeEntities() throws AmbariException {
+
+    ClusterServiceGroupEntityPK pk = new ClusterServiceGroupEntityPK();
+    pk.setClusterId(getClusterId());
+    pk.setServiceGroupName(getName());
+    clusterServiceGroupDAO.removeByPK(pk);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java
index 3120b86..ce9c64c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java
@@ -110,13 +110,16 @@ public class ServiceImpl implements Service {
   }
 
   @AssistedInject
-  public ServiceImpl(@Assisted Cluster cluster, @Assisted String serviceName,
+  public ServiceImpl(@Assisted Cluster cluster, @Assisted("serviceName") String serviceName,
+      @Assisted("stackServiceName") String stackServiceName, @Assisted("serviceGroupName") String serviceGroupName,
       Injector injector) throws AmbariException {
     injector.injectMembers(this);
     clusterGlobalLock = cluster.getClusterGlobalLock();
     serviceEntity = new ClusterServiceEntity();
     serviceEntity.setClusterId(cluster.getClusterId());
     serviceEntity.setServiceName(serviceName);
+    serviceEntity.setStackServiceName(stackServiceName);
+    serviceEntity.setServiceGroupName(serviceGroupName);
     serviceDesiredStateEntity = new ServiceDesiredStateEntity();
     serviceDesiredStateEntity.setServiceName(serviceName);
     serviceDesiredStateEntity.setClusterId(cluster.getClusterId());
@@ -135,7 +138,7 @@ public class ServiceImpl implements Service {
     setDesiredStackVersion(stackId);
 
     ServiceInfo sInfo = ambariMetaInfo.getService(stackId.getStackName(),
-        stackId.getStackVersion(), serviceName);
+        stackId.getStackVersion(), stackServiceName);
     isClientOnlyService = sInfo.isClientOnlyService();
 
     init();
@@ -165,9 +168,11 @@ public class ServiceImpl implements Service {
                     serviceComponentDesiredStateEntity));
           } catch(ProvisionException ex) {
             StackId stackId = cluster.getCurrentStackVersion();
-            LOG.error(String.format("Can not get component info: stackName=%s, stackVersion=%s, serviceName=%s, componentName=%s",
+            LOG.error(String.format("Can not get component info: stackName=%s, stackVersion=%s, serviceName=%s, " +
+                "stackServiceName=%s, componentName=%s",
                 stackId.getStackName(), stackId.getStackVersion(),
-                serviceEntity.getServiceName(),serviceComponentDesiredStateEntity.getComponentName()));
+                serviceEntity.getServiceName(), serviceEntity.getStackServiceName(),
+                serviceComponentDesiredStateEntity.getComponentName()));
             ex.printStackTrace();
           }
       }
@@ -175,7 +180,7 @@ public class ServiceImpl implements Service {
 
     StackId stackId = getDesiredStackVersion();
     ServiceInfo sInfo = ambariMetaInfo.getService(stackId.getStackName(),
-        stackId.getStackVersion(), getName());
+        stackId.getStackVersion(), getStackServiceName());
     isClientOnlyService = sInfo.isClientOnlyService();
 
     persisted = true;
@@ -192,6 +197,16 @@ public class ServiceImpl implements Service {
   }
 
   @Override
+  public String getStackServiceName() {
+    return serviceEntity.getStackServiceName();
+  }
+
+  @Override
+  public String getServiceGroupName() {
+    return serviceEntity.getServiceGroupName();
+  }
+
+  @Override
   public long getClusterId() {
     return cluster.getClusterId();
   }
@@ -236,6 +251,7 @@ public class ServiceImpl implements Service {
               + ", clusterName=" + cluster.getClusterName()
               + ", clusterId=" + cluster.getClusterId()
               + ", serviceName=" + getName()
+              + ", stackServiceName=" + getStackServiceName()
               + ", serviceComponentName=" + component.getName());
         }
         if (components.containsKey(component.getName())) {
@@ -243,6 +259,7 @@ public class ServiceImpl implements Service {
               + ", clusterName=" + cluster.getClusterName()
               + ", clusterId=" + cluster.getClusterId()
               + ", serviceName=" + getName()
+              + ", stackServiceName=" + getStackServiceName()
               + ", serviceComponentName=" + component.getName());
         }
         components.put(component.getName(), component);
@@ -266,6 +283,7 @@ public class ServiceImpl implements Service {
               + ", clusterName=" + cluster.getClusterName()
               + ", clusterId=" + cluster.getClusterId()
               + ", serviceName=" + getName()
+              + ", stackServiceName=" + getStackServiceName()
               + ", serviceComponentName=" + serviceComponentName);
         }
         if (components.containsKey(serviceComponentName)) {
@@ -273,6 +291,7 @@ public class ServiceImpl implements Service {
               + ", clusterName=" + cluster.getClusterName()
               + ", clusterId=" + cluster.getClusterId()
               + ", serviceName=" + getName()
+              + ", stackServiceName=" + getStackServiceName()
               + ", serviceComponentName=" + serviceComponentName);
         }
         ServiceComponent component = serviceComponentFactory.createNew(this, serviceComponentName);
@@ -293,7 +312,7 @@ public class ServiceImpl implements Service {
     try {
       if (!components.containsKey(componentName)) {
         throw new ServiceComponentNotFoundException(cluster.getClusterName(),
-            getName(), componentName);
+            getName(), getStackServiceName(), getServiceGroupName(), componentName);
       }
       return components.get(componentName);
     } finally {
@@ -318,7 +337,7 @@ public class ServiceImpl implements Service {
       if (LOG.isDebugEnabled()) {
         LOG.debug("Setting DesiredState of Service" + ", clusterName="
             + cluster.getClusterName() + ", clusterId="
-            + cluster.getClusterId() + ", serviceName=" + getName()
+            + cluster.getClusterId() + ", serviceName=" + getName() + ", stackServiceName=" + getStackServiceName()
             + ", oldDesiredState=" + getDesiredState() + ", newDesiredState="
             + state + ", persisted = " + isPersisted());
       }
@@ -351,6 +370,7 @@ public class ServiceImpl implements Service {
         LOG.debug("Setting DesiredSecurityState of Service" + ", clusterName="
             + cluster.getClusterName() + ", clusterId="
             + cluster.getClusterId() + ", serviceName=" + getName()
+            + ", stackServiceName=" + getStackServiceName()
             + ", oldDesiredSecurityState=" + getSecurityState()
             + ", newDesiredSecurityState=" + securityState);
       }
@@ -384,6 +404,7 @@ public class ServiceImpl implements Service {
         LOG.debug("Setting DesiredStackVersion of Service" + ", clusterName="
             + cluster.getClusterName() + ", clusterId="
             + cluster.getClusterId() + ", serviceName=" + getName()
+            + ", stackServiceName=" + getStackServiceName()
             + ", oldDesiredStackVersion=" + getDesiredStackVersion()
             + ", newDesiredStackVersion=" + stack);
       }
@@ -401,7 +422,7 @@ public class ServiceImpl implements Service {
     readWriteLock.readLock().lock();
     try {
       ServiceResponse r = new ServiceResponse(cluster.getClusterId(),
-          cluster.getClusterName(), getName(),
+          cluster.getClusterName(), getName(), getStackServiceName(), getServiceGroupName(),
           getDesiredStackVersion().getStackId(), getDesiredState().toString());
 
       r.setMaintenanceState(getMaintenanceState().name());
@@ -420,7 +441,7 @@ public class ServiceImpl implements Service {
   public void debugDump(StringBuilder sb) {
     readWriteLock.readLock().lock();
     try {
-      sb.append("Service={ serviceName=" + getName() + ", clusterName="
+      sb.append("Service={ serviceName=" + getName() + "stackServiceName=" + getStackServiceName() + ", clusterName="
           + cluster.getClusterName() + ", clusterId=" + cluster.getClusterId()
           + ", desiredStackVersion=" + getDesiredStackVersion()
           + ", desiredState=" + getDesiredState().toString()
@@ -479,7 +500,7 @@ public class ServiceImpl implements Service {
 
           ServiceInstalledEvent event = new ServiceInstalledEvent(
               getClusterId(), stackId.getStackName(),
-              stackId.getStackVersion(), getName());
+              stackId.getStackVersion(), getName(), getStackServiceName(), getServiceGroupName());
 
           eventPublisher.publish(event);
         } else {
@@ -689,7 +710,7 @@ public class ServiceImpl implements Service {
           StackId stackId = cluster.getDesiredStackVersion();
 
           ServiceRemovedEvent event = new ServiceRemovedEvent(getClusterId(),
-              stackId.getStackName(), stackId.getStackVersion(), getName());
+              stackId.getStackName(), stackId.getStackVersion(), getName(), getStackServiceName(), getServiceGroupName());
 
           eventPublisher.publish(event);
         }