You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2014/10/16 22:11:28 UTC

[04/51] [partial] AMBARI-7718. Rebase branch-windows-dev against trunk. (Jayush Luniya and Florian Barca via yusaku)

http://git-wip-us.apache.org/repos/asf/ambari/blob/9213dcca/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertResourceProvider.java
new file mode 100644
index 0000000..f6b6082
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertResourceProvider.java
@@ -0,0 +1,191 @@
+/**
+ * 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 java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.AmbariManagementController;
+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.Resource;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.orm.dao.AlertsDAO;
+import org.apache.ambari.server.orm.entities.AlertCurrentEntity;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
+import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
+import org.apache.ambari.server.state.Cluster;
+
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+
+/**
+ * ResourceProvider for Alert instances
+ */
+public class AlertResourceProvider extends ReadOnlyResourceProvider {
+
+  protected static final String ALERT_CLUSTER_NAME = "Alert/cluster_name";
+  protected static final String ALERT_ID = "Alert/id";
+  protected static final String ALERT_NAME = "Alert/name";
+  protected static final String ALERT_LATEST_TIMESTAMP = "Alert/latest_timestamp";
+  protected static final String ALERT_MAINTENANCE_STATE = "Alert/maintenance_state";
+  protected static final String ALERT_ORIGINAL_TIMESTAMP = "Alert/original_timestamp";
+  protected static final String ALERT_INSTANCE = "Alert/instance";
+  protected static final String ALERT_LABEL = "Alert/label";
+  protected static final String ALERT_STATE = "Alert/state";
+  protected static final String ALERT_TEXT = "Alert/text";
+  protected static final String ALERT_COMPONENT = "Alert/component_name";
+  protected static final String ALERT_HOST = "Alert/host_name";
+  protected static final String ALERT_SERVICE = "Alert/service_name";
+  protected static final String ALERT_SCOPE = "Alert/scope";
+
+  private static Set<String> pkPropertyIds = new HashSet<String>(
+      Arrays.asList(ALERT_ID, ALERT_NAME));
+
+  private static AlertsDAO alertsDAO = null;
+
+  /**
+   * @param injector the injector
+   */
+  @Inject
+  public static void init(Injector injector) {
+    alertsDAO = injector.getInstance(AlertsDAO.class);
+  }
+
+  AlertResourceProvider(Set<String> propertyIds,
+      Map<Resource.Type, String> keyPropertyIds,
+      AmbariManagementController managementController) {
+
+    super(propertyIds, keyPropertyIds, managementController);
+  }
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return pkPropertyIds;
+  }
+
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+
+    Set<String> requestPropertyIds = getRequestPropertyIds(request, predicate);
+
+    Set<Resource> results = new HashSet<Resource>();
+
+    for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
+
+      String clusterName = (String) propertyMap.get(ALERT_CLUSTER_NAME);
+
+      if (null == clusterName || clusterName.isEmpty()) {
+        throw new IllegalArgumentException("Invalid argument, cluster name is required");
+      }
+
+      String id = (String) propertyMap.get(ALERT_ID);
+      if (null != id) {
+        AlertCurrentEntity entity = alertsDAO.findCurrentById(Long.parseLong(id));
+
+        if (null != entity) {
+          results.add(toResource(false, clusterName, entity, requestPropertyIds));
+        }
+
+      } else {
+        Cluster cluster = null;
+
+        try {
+          cluster = getManagementController().getClusters().getCluster(clusterName);
+        } catch (AmbariException e) {
+          throw new NoSuchResourceException("Parent Cluster resource doesn't exist", e);
+        }
+
+        String serviceName = (String) propertyMap.get(ALERT_SERVICE);
+        String hostName = (String) propertyMap.get(ALERT_HOST);
+
+        List<AlertCurrentEntity> entities = null;
+
+        if (null != hostName) {
+          entities = alertsDAO.findCurrentByHost(cluster.getClusterId(),
+              hostName);
+        } else if (null != serviceName) {
+          entities = alertsDAO.findCurrentByService(cluster.getClusterId(),
+              serviceName);
+        } else {
+          entities = alertsDAO.findCurrentByCluster(cluster.getClusterId());
+        }
+
+        if (null == entities)
+          entities = Collections.emptyList();
+
+        for (AlertCurrentEntity entity : entities) {
+          results.add(toResource(true, clusterName, entity, requestPropertyIds));
+        }
+      }
+    }
+
+    return results;
+  }
+
+  /**
+   * Converts an entity to a resource.
+   *
+   * @param isCollection {@code true} if the response is for a collection
+   * @param clusterName the cluster name
+   * @param entity the entity
+   * @param requestedIds the requested ids
+   * @return the resource
+   */
+  private Resource toResource(boolean isCollection, String clusterName,
+      AlertCurrentEntity entity, Set<String> requestedIds) {
+    Resource resource = new ResourceImpl(Resource.Type.Alert);
+
+    setResourceProperty(resource, ALERT_CLUSTER_NAME, clusterName, requestedIds);
+    setResourceProperty(resource, ALERT_ID, entity.getAlertId(), requestedIds);
+    setResourceProperty(resource, ALERT_LATEST_TIMESTAMP, entity.getLatestTimestamp(), requestedIds);
+    setResourceProperty(resource, ALERT_MAINTENANCE_STATE, entity.getMaintenanceState(), requestedIds);
+    setResourceProperty(resource, ALERT_ORIGINAL_TIMESTAMP, entity.getOriginalTimestamp(), requestedIds);
+    setResourceProperty(resource, ALERT_TEXT, entity.getLatestText(), requestedIds);
+
+    AlertHistoryEntity history = entity.getAlertHistory();
+    setResourceProperty(resource, ALERT_INSTANCE, history.getAlertInstance(), requestedIds);
+    setResourceProperty(resource, ALERT_LABEL, history.getAlertLabel(), requestedIds);
+    setResourceProperty(resource, ALERT_STATE, history.getAlertState(), requestedIds);
+    setResourceProperty(resource, ALERT_COMPONENT, history.getComponentName(), requestedIds);
+    setResourceProperty(resource, ALERT_HOST, history.getHostName(), requestedIds);
+    setResourceProperty(resource, ALERT_SERVICE, history.getServiceName(), requestedIds);
+
+    AlertDefinitionEntity definition = history.getAlertDefinition();
+    setResourceProperty(resource, ALERT_NAME, definition.getDefinitionName(), requestedIds);
+    setResourceProperty(resource, ALERT_SCOPE, definition.getScope(), requestedIds);
+
+    if (isCollection) {
+      // !!! want name to be populated as if it were a PK when requesting the collection
+      resource.setProperty(ALERT_NAME, definition.getDefinitionName());
+    }
+
+    return resource;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/9213dcca/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertSummaryPropertyProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertSummaryPropertyProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertSummaryPropertyProvider.java
new file mode 100644
index 0000000..2e8661a
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertSummaryPropertyProvider.java
@@ -0,0 +1,138 @@
+/**
+ * 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 java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.PropertyProvider;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.orm.dao.AlertSummaryDTO;
+import org.apache.ambari.server.orm.dao.AlertsDAO;
+import org.apache.ambari.server.state.AlertState;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Injector;
+
+/**
+ * Property provider that adds alert summary information to endpoints.
+ */
+public class AlertSummaryPropertyProvider extends BaseProvider implements PropertyProvider {
+
+  private final static Logger LOG = LoggerFactory.getLogger(AlertSummaryPropertyProvider.class);
+
+  private static Clusters s_clusters = null;
+  private static AlertsDAO s_dao = null;
+
+  private Resource.Type m_resourceType = null;
+  private String m_clusterPropertyId = null;
+  private String m_typeIdPropertyId = null;
+
+  AlertSummaryPropertyProvider(Resource.Type type,
+      String clusterPropertyId, String typeIdPropertyId) {
+    super(Collections.singleton("alerts_summary"));
+    m_resourceType = type;
+    m_clusterPropertyId = clusterPropertyId;
+    m_typeIdPropertyId = typeIdPropertyId;
+  }
+
+  public static void init(Injector injector) {
+    s_clusters = injector.getInstance(Clusters.class);
+    s_dao = injector.getInstance(AlertsDAO.class);
+  }
+
+  @Override
+  public Set<Resource> populateResources(Set<Resource> resources,
+      Request request, Predicate predicate) throws SystemException {
+
+    Set<String> propertyIds = getRequestPropertyIds(request, predicate);
+
+    try {
+      for (Resource res : resources) {
+        populateResource(res, propertyIds);
+      }
+    } catch (AmbariException e) {
+      LOG.error("Could not load built-in alerts - Executor exception ({})",
+          e.getMessage());
+    }
+
+    return resources;
+  }
+
+  private void populateResource(Resource resource, Set<String> requestedIds) throws AmbariException {
+
+    AlertSummaryDTO summary = null;
+
+    String clusterName = (String) resource.getPropertyValue(m_clusterPropertyId);
+
+    if (null == clusterName)
+      return;
+
+    String typeId = null == m_typeIdPropertyId ? null : (String) resource.getPropertyValue(m_typeIdPropertyId);
+    Cluster cluster = s_clusters.getCluster(clusterName);
+
+    switch (m_resourceType.getInternalType()) {
+      case Cluster:
+        summary = s_dao.findCurrentCounts(cluster.getClusterId(), null, null);
+        break;
+      case Service:
+        summary = s_dao.findCurrentCounts(cluster.getClusterId(), typeId, null);
+        break;
+      case Host:
+        summary = s_dao.findCurrentCounts(cluster.getClusterId(), null, typeId);
+        break;
+      default:
+        break;
+    }
+
+    if (null != summary) {
+      Map<AlertState, Integer> map = new HashMap<AlertState, Integer>();
+      map.put(AlertState.OK, Integer.valueOf(summary.getOkCount()));
+      map.put(AlertState.WARNING, Integer.valueOf(summary.getWarningCount()));
+      map.put(AlertState.CRITICAL, Integer.valueOf(summary.getCriticalCount()));
+      map.put(AlertState.UNKNOWN, Integer.valueOf(summary.getUnknownCount()));
+
+      setResourceProperty(resource, "alerts_summary", map, requestedIds);
+    }
+
+  }
+
+  @Override
+  public Set<String> checkPropertyIds(Set<String> propertyIds) {
+    Set<String> rejects = new HashSet<String>();
+
+    for (String id : propertyIds) {
+      if (!id.startsWith("alerts_summary"))
+        rejects.add(id);
+    }
+
+    return rejects;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/9213dcca/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
new file mode 100644
index 0000000..5173c1b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
@@ -0,0 +1,384 @@
+/**
+ * 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 java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.ambari.server.AmbariException;
+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.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.orm.dao.AlertDispatchDAO;
+import org.apache.ambari.server.orm.entities.AlertTargetEntity;
+import org.apache.ambari.server.state.alert.AlertTarget;
+import org.apache.commons.lang.StringUtils;
+
+import com.google.gson.Gson;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+
+/**
+ * The {@link AlertTargetResourceProvider} class deals with managing the CRUD
+ * operations for {@link AlertTarget}, including property coercion to and from
+ * {@link AlertTargetEntity}.
+ */
+public class AlertTargetResourceProvider extends
+ AbstractResourceProvider {
+
+  protected static final String ALERT_TARGET = "AlertTarget";
+  protected static final String ALERT_TARGET_ID = "AlertTarget/id";
+  protected static final String ALERT_TARGET_NAME = "AlertTarget/name";
+  protected static final String ALERT_TARGET_DESCRIPTION = "AlertTarget/description";
+  protected static final String ALERT_TARGET_NOTIFICATION_TYPE = "AlertTarget/notification_type";
+  protected static final String ALERT_TARGET_PROPERTIES = "AlertTarget/properties";
+  protected static final String ALERT_TARGET_GROUPS = "AlertTarget/groups";
+
+  private static final Set<String> PK_PROPERTY_IDS = new HashSet<String>(
+      Arrays.asList(ALERT_TARGET_ID, ALERT_TARGET_NAME));
+
+  /**
+   * The property ids for an alert target resource.
+   */
+  private static final Set<String> PROPERTY_IDS = new HashSet<String>();
+
+  /**
+   * The key property ids for an alert target resource.
+   */
+  private static final Map<Resource.Type, String> KEY_PROPERTY_IDS = new HashMap<Resource.Type, String>();
+
+  static {
+    // properties
+    PROPERTY_IDS.add(ALERT_TARGET_ID);
+    PROPERTY_IDS.add(ALERT_TARGET_NAME);
+    PROPERTY_IDS.add(ALERT_TARGET_DESCRIPTION);
+    PROPERTY_IDS.add(ALERT_TARGET_NOTIFICATION_TYPE);
+    PROPERTY_IDS.add(ALERT_TARGET_PROPERTIES);
+    PROPERTY_IDS.add(ALERT_TARGET_GROUPS);
+
+    // keys
+    KEY_PROPERTY_IDS.put(Resource.Type.AlertTarget, ALERT_TARGET_ID);
+  }
+
+  /**
+   * Target DAO
+   */
+  @Inject
+  private static AlertDispatchDAO s_dao;
+
+  /**
+   * Used for serializationa and deserialization of some fields.
+   */
+  private static final Gson s_gson = new Gson();
+
+  /**
+   * Initializes the injectable members of this class with the specified
+   * injector.
+   *
+   * @param injector
+   *          the injector (not {@code null}).
+   */
+  @Inject
+  public static void init(Injector injector) {
+    s_dao = injector.getInstance(AlertDispatchDAO.class);
+  }
+
+  /**
+   * Constructor.
+   */
+  AlertTargetResourceProvider() {
+    super(PROPERTY_IDS, KEY_PROPERTY_IDS);
+  }
+
+  @Override
+  public RequestStatus createResources(final Request request)
+      throws SystemException,
+      UnsupportedPropertyException, ResourceAlreadyExistsException,
+      NoSuchParentResourceException {
+
+    createResources(new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        createAlertTargets(request.getProperties());
+        return null;
+      }
+    });
+
+    notifyCreate(Resource.Type.AlertTarget, request);
+    return getRequestStatus(null);
+  }
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+
+    Set<Resource> results = new HashSet<Resource>();
+    Set<String> requestPropertyIds = getRequestPropertyIds(request, predicate);
+
+    if( null == predicate ){
+      List<AlertTargetEntity> entities = s_dao.findAllTargets();
+      for (AlertTargetEntity entity : entities) {
+        results.add(toResource(true, entity, requestPropertyIds));
+      }
+    } else {
+      for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
+        String id = (String) propertyMap.get(ALERT_TARGET_ID);
+        if (null == id) {
+          continue;
+        }
+
+        AlertTargetEntity entity = s_dao.findTargetById(Long.parseLong(id));
+        if (null != entity) {
+          results.add(toResource(false, entity, requestPropertyIds));
+        }
+      }
+    }
+
+    return results;
+  }
+
+  @Override
+  public RequestStatus updateResources(final Request request,
+      Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+
+    modifyResources(new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        updateAlertTargets(request.getProperties());
+        return null;
+      }
+    });
+
+    notifyUpdate(Resource.Type.AlertTarget, request, predicate);
+    return getRequestStatus(null);
+  }
+
+  @Override
+  public RequestStatus deleteResources(Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+
+    Set<Resource> resources = getResources(new RequestImpl(null, null, null,
+        null), predicate);
+
+    Set<Long> targetIds = new HashSet<Long>();
+
+    for (final Resource resource : resources) {
+      Long id = (Long) resource.getPropertyValue(ALERT_TARGET_ID);
+      targetIds.add(id);
+    }
+
+    for (Long targetId : targetIds) {
+      LOG.info("Deleting alert target {}", targetId);
+
+      final AlertTargetEntity entity = s_dao.findTargetById(targetId.longValue());
+
+      modifyResources(new Command<Void>() {
+        @Override
+        public Void invoke() throws AmbariException {
+          s_dao.remove(entity);
+          return null;
+        }
+      });
+    }
+
+    notifyDelete(Resource.Type.AlertTarget, predicate);
+    return getRequestStatus(null);
+  }
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return PK_PROPERTY_IDS;
+  }
+
+  /**
+   * Create and persist {@link AlertTargetEntity} from the map of properties.
+   *
+   * @param requestMaps
+   * @throws AmbariException
+   */
+  private void createAlertTargets(Set<Map<String, Object>> requestMaps)
+      throws AmbariException {
+    List<AlertTargetEntity> entities = new ArrayList<AlertTargetEntity>();
+    for (Map<String, Object> requestMap : requestMaps) {
+      AlertTargetEntity entity = new AlertTargetEntity();
+
+      String name = (String) requestMap.get(ALERT_TARGET_NAME);
+      String description = (String) requestMap.get(ALERT_TARGET_DESCRIPTION);
+      String notificationType = (String) requestMap.get(ALERT_TARGET_NOTIFICATION_TYPE);
+
+      if (StringUtils.isEmpty(name)) {
+        throw new IllegalArgumentException(
+            "The name of the alert target is required.");
+      }
+
+      if (StringUtils.isEmpty(notificationType)) {
+        throw new IllegalArgumentException(
+            "The type of the alert target is required.");
+      }
+
+      String properties = extractProperties(requestMap);
+      if (StringUtils.isEmpty(properties)) {
+        throw new IllegalArgumentException(
+            "Alert targets must be created with their connection properties");
+      }
+
+      entity.setDescription(description);
+      entity.setNotificationType(notificationType);
+      entity.setProperties(properties);
+      entity.setTargetName(name);
+
+      entities.add(entity);
+    }
+
+    s_dao.createTargets(entities);
+  }
+
+  /**
+   * Updates existing {@link AlertTargetEntity}s with the specified properties.
+   *
+   * @param requestMaps
+   *          a set of property maps, one map for each entity.
+   * @throws AmbariException
+   *           if the entity could not be found.
+   */
+  private void updateAlertTargets(Set<Map<String, Object>> requestMaps)
+      throws AmbariException {
+
+    for (Map<String, Object> requestMap : requestMaps) {
+      String stringId = (String) requestMap.get(ALERT_TARGET_ID);
+
+      if (StringUtils.isEmpty(stringId)) {
+        throw new IllegalArgumentException(
+            "The ID of the alert target is required when updating an existing target");
+      }
+
+      long id = Long.parseLong(stringId);
+      AlertTargetEntity entity = s_dao.findTargetById(id);
+
+      if (null == entity) {
+        String message = MessageFormat.format(
+            "The alert target with ID {0} could not be found", id);
+        throw new AmbariException(message);
+      }
+
+      String name = (String) requestMap.get(ALERT_TARGET_NAME);
+      String description = (String) requestMap.get(ALERT_TARGET_DESCRIPTION);
+      String notificationType = (String) requestMap.get(ALERT_TARGET_NOTIFICATION_TYPE);
+
+      if (!StringUtils.isBlank(name)) {
+        entity.setTargetName(name);
+      }
+
+      if (null != description) {
+        entity.setDescription(description);
+      }
+
+      if (!StringUtils.isBlank(notificationType)) {
+        entity.setNotificationType(notificationType);
+      }
+
+      String properties = extractProperties(requestMap);
+      if (!StringUtils.isEmpty(properties)) {
+        entity.setProperties(properties);
+      }
+
+      s_dao.merge(entity);
+    }
+  }
+
+  /**
+   * Convert the given {@link AlertTargetEntity} to a {@link Resource}.
+   *
+   * @param isCollection
+   *          {@code true} if the resource is part of a collection. Some
+   *          properties are not returned when a collection of targets is
+   *          requested.
+   * @param entity
+   *          the entity to convert.
+   * @param requestedIds
+   *          the properties that were requested or {@code null} for all.
+   * @return the resource representation of the entity (never {@code null}).
+   */
+  private Resource toResource(boolean isCollection, AlertTargetEntity entity,
+      Set<String> requestedIds) {
+
+    Resource resource = new ResourceImpl(Resource.Type.AlertTarget);
+    resource.setProperty(ALERT_TARGET_ID, entity.getTargetId());
+    resource.setProperty(ALERT_TARGET_NAME, entity.getTargetName());
+
+    resource.setProperty(ALERT_TARGET_DESCRIPTION, entity.getDescription());
+    resource.setProperty(ALERT_TARGET_NOTIFICATION_TYPE,
+        entity.getNotificationType());
+
+    if (!isCollection) {
+      String properties = entity.getProperties();
+      Map<String, String> map = s_gson.<Map<String, String>> fromJson(
+          properties,
+          Map.class);
+
+      resource.setProperty(ALERT_TARGET_PROPERTIES, map);
+    }
+
+    return resource;
+  }
+
+  /**
+   * Looks through the flat list of propery keys in the supplied map and builds
+   * a JSON string of key:value pairs for the {@link #ALERT_TARGET_PROPERTIES}
+   * key.
+   *
+   * @param requestMap
+   *          the map of flattened properties (not {@code null}).
+   * @return the JSON representing the key/value pairs of all properties, or
+   *         {@code null} if none.
+   */
+  private String extractProperties( Map<String, Object> requestMap ){
+    Map<String, Object> normalizedMap = new HashMap<String, Object>(
+        requestMap.size());
+
+    for (Entry<String, Object> entry : requestMap.entrySet()) {
+      String key = entry.getKey();
+      String propCat = PropertyHelper.getPropertyCategory(key);
+
+      if (propCat.equals(ALERT_TARGET_PROPERTIES)) {
+        String propKey = PropertyHelper.getPropertyName(key);
+        normalizedMap.put(propKey, entry.getValue());
+      }
+    }
+
+    return s_gson.toJson(normalizedMap);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/9213dcca/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariPrivilegeResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariPrivilegeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariPrivilegeResourceProvider.java
new file mode 100644
index 0000000..f4839e9
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariPrivilegeResourceProvider.java
@@ -0,0 +1,174 @@
+/**
+ * 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 privileges and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.controller.internal;
+
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.orm.dao.ClusterDAO;
+import org.apache.ambari.server.orm.entities.ClusterEntity;
+import org.apache.ambari.server.orm.entities.GroupEntity;
+import org.apache.ambari.server.orm.entities.PrivilegeEntity;
+import org.apache.ambari.server.orm.entities.ResourceEntity;
+import org.apache.ambari.server.orm.entities.ResourceTypeEntity;
+import org.apache.ambari.server.orm.entities.UserEntity;
+import org.apache.ambari.server.orm.entities.ViewEntity;
+import org.apache.ambari.server.orm.entities.ViewInstanceEntity;
+import org.apache.ambari.server.view.ViewRegistry;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.ambari.server.controller.internal.ClusterPrivilegeResourceProvider.PRIVILEGE_CLUSTER_NAME_PROPERTY_ID;
+import static org.apache.ambari.server.controller.internal.ViewPrivilegeResourceProvider.PRIVILEGE_INSTANCE_NAME_PROPERTY_ID;
+import static org.apache.ambari.server.controller.internal.ViewPrivilegeResourceProvider.PRIVILEGE_VIEW_NAME_PROPERTY_ID;
+import static org.apache.ambari.server.controller.internal.ViewPrivilegeResourceProvider.PRIVILEGE_VIEW_VERSION_PROPERTY_ID;
+
+/**
+ * Resource provider for Ambari privileges.
+ */
+public class AmbariPrivilegeResourceProvider extends PrivilegeResourceProvider<Object> {
+
+  public static final String PRIVILEGE_TYPE_PROPERTY_ID  = "PrivilegeInfo/type";
+
+  /**
+   * Data access object used to obtain privilege entities.
+   */
+  protected static ClusterDAO clusterDAO;
+
+  /**
+   * The property ids for an Ambari privilege resource.
+   */
+  private static Set<String> propertyIds = new HashSet<String>();
+  static {
+    propertyIds.add(PRIVILEGE_ID_PROPERTY_ID);
+    propertyIds.add(PERMISSION_NAME_PROPERTY_ID);
+    propertyIds.add(PRINCIPAL_NAME_PROPERTY_ID);
+    propertyIds.add(PRINCIPAL_TYPE_PROPERTY_ID);
+    propertyIds.add(PRIVILEGE_VIEW_NAME_PROPERTY_ID);
+    propertyIds.add(PRIVILEGE_VIEW_VERSION_PROPERTY_ID);
+    propertyIds.add(PRIVILEGE_INSTANCE_NAME_PROPERTY_ID);
+    propertyIds.add(PRIVILEGE_CLUSTER_NAME_PROPERTY_ID);
+    propertyIds.add(PRIVILEGE_TYPE_PROPERTY_ID);
+
+  }
+
+  /**
+   * The key property ids for a privilege resource.
+   */
+  private static Map<Resource.Type, String> keyPropertyIds = new HashMap<Resource.Type, String>();
+  static {
+    keyPropertyIds.put(Resource.Type.AmbariPrivilege, PRIVILEGE_ID_PROPERTY_ID);
+  }
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct an AmbariPrivilegeResourceProvider.
+   */
+  public AmbariPrivilegeResourceProvider() {
+    super(propertyIds, keyPropertyIds, Resource.Type.AmbariPrivilege);
+  }
+
+  // ----- AmbariPrivilegeResourceProvider ---------------------------------
+
+  /**
+   * Static initialization.
+   *
+   * @param clusterDao  the cluster data access object
+   */
+  public static void init(ClusterDAO clusterDao) {
+    clusterDAO  = clusterDao;
+  }
+
+  // ----- AbstractResourceProvider ------------------------------------------
+
+  @Override
+  public Map<Resource.Type, String> getKeyPropertyIds() {
+    return keyPropertyIds;
+  }
+
+
+  // ----- PrivilegeResourceProvider -----------------------------------------
+
+  @Override
+  public Map<Long, Object> getResourceEntities(Map<String, Object> properties) {
+    Map<Long, Object> resourceEntities = new HashMap<Long, Object>();
+
+    resourceEntities.put(ResourceEntity.AMBARI_RESOURCE_ID, null);
+    // add cluster entities
+    List<ClusterEntity> clusterEntities = clusterDAO.findAll();
+
+    if (clusterEntities != null) {
+      for (ClusterEntity clusterEntity : clusterEntities) {
+        resourceEntities.put(clusterEntity.getResource().getId(), clusterEntity);
+      }
+    }
+    //add view entities
+    ViewRegistry viewRegistry = ViewRegistry.getInstance();
+    for (ViewEntity viewEntity : viewRegistry.getDefinitions()) {
+      if (viewEntity.isDeployed()) {
+        for (ViewInstanceEntity viewInstanceEntity : viewEntity.getInstances()) {
+          resourceEntities.put(viewInstanceEntity.getResource().getId(), viewInstanceEntity);
+        }
+      }
+    }
+    return resourceEntities;
+  }
+
+  @Override
+  protected Resource toResource(PrivilegeEntity privilegeEntity,
+                                Map<Long, UserEntity> userEntities,
+                                Map<Long, GroupEntity> groupEntities,
+                                Map<Long, Object> resourceEntities, Set<String> requestedIds) {
+    Resource resource = super.toResource(privilegeEntity, userEntities, groupEntities, resourceEntities, requestedIds);
+    if (resource != null) {
+      ResourceEntity resourceEntity = privilegeEntity.getResource();
+      ResourceTypeEntity type = resourceEntity.getResourceType();
+      String privilegeType;
+      switch (type.getId()) {
+        case ResourceTypeEntity.CLUSTER_RESOURCE_TYPE:
+          ClusterEntity clusterEntity = (ClusterEntity) resourceEntities.get(resourceEntity.getId());
+          privilegeType = ResourceTypeEntity.CLUSTER_RESOURCE_TYPE_NAME;
+          setResourceProperty(resource, PRIVILEGE_CLUSTER_NAME_PROPERTY_ID, clusterEntity.getClusterName(), requestedIds);
+          break;
+        case ResourceTypeEntity.AMBARI_RESOURCE_TYPE:
+          privilegeType = ResourceTypeEntity.AMBARI_RESOURCE_TYPE_NAME;
+          break;
+        default:
+          privilegeType = ResourceTypeEntity.VIEW_RESOURCE_TYPE_NAME;
+          ViewInstanceEntity viewInstanceEntity = (ViewInstanceEntity) resourceEntities.get(resourceEntity.getId());
+          ViewEntity viewEntity = viewInstanceEntity.getViewEntity();
+
+          setResourceProperty(resource, PRIVILEGE_VIEW_NAME_PROPERTY_ID, viewEntity.getCommonName(), requestedIds);
+          setResourceProperty(resource, PRIVILEGE_VIEW_VERSION_PROPERTY_ID, viewEntity.getVersion(), requestedIds);
+          setResourceProperty(resource, PRIVILEGE_INSTANCE_NAME_PROPERTY_ID, viewInstanceEntity.getName(), requestedIds);
+          break;
+      }
+      setResourceProperty(resource, PRIVILEGE_TYPE_PROPERTY_ID, privilegeType, requestedIds);
+    }
+    return resource;
+  }
+  @Override
+  public Long getResourceEntityId(Predicate predicate) {
+    return ResourceEntity.AMBARI_RESOURCE_ID;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/9213dcca/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java
index 800ff3b..0cd8a9f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java
@@ -19,12 +19,14 @@
 package org.apache.ambari.server.controller.internal;
 
 import com.google.gson.Gson;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.StackAccessException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.StackConfigurationRequest;
 import org.apache.ambari.server.controller.StackConfigurationResponse;
+import org.apache.ambari.server.controller.StackLevelConfigurationRequest;
 import org.apache.ambari.server.controller.StackServiceComponentRequest;
 import org.apache.ambari.server.controller.StackServiceComponentResponse;
 import org.apache.ambari.server.controller.StackServiceRequest;
@@ -38,7 +40,9 @@ import org.apache.ambari.server.orm.entities.HostGroupComponentEntity;
 import org.apache.ambari.server.orm.entities.HostGroupConfigEntity;
 import org.apache.ambari.server.orm.entities.HostGroupEntity;
 import org.apache.ambari.server.state.AutoDeployInfo;
+import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.DependencyInfo;
+import org.apache.ambari.server.state.PropertyInfo;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -64,6 +68,8 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
    */
   protected static AmbariMetaInfo stackInfo;
 
+  protected static ConfigHelper configHelper;
+
 
   protected BaseBlueprintProcessor(Set<String> propertyIds,
                                    Map<Resource.Type, String> keyPropertyIds,
@@ -80,9 +86,9 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
    *
    * @return collection of host groups which contain the specified component
    */
-  protected Collection<HostGroup> getHostGroupsForComponent(String component, Collection<HostGroup> hostGroups) {
-    Collection<HostGroup> resultGroups = new HashSet<HostGroup>();
-    for (HostGroup group : hostGroups ) {
+  protected Collection<HostGroupImpl> getHostGroupsForComponent(String component, Collection<HostGroupImpl> hostGroups) {
+    Collection<HostGroupImpl> resultGroups = new HashSet<HostGroupImpl>();
+    for (HostGroupImpl group : hostGroups ) {
       if (group.getComponents().contains(component)) {
         resultGroups.add(group);
       }
@@ -98,11 +104,11 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
    *
    * @return map of host group name to host group
    */
-  protected Map<String, HostGroup> parseBlueprintHostGroups(BlueprintEntity blueprint, Stack stack) {
-    Map<String, HostGroup> mapHostGroups = new HashMap<String, HostGroup>();
+  protected Map<String, HostGroupImpl> parseBlueprintHostGroups(BlueprintEntity blueprint, Stack stack) {
+    Map<String, HostGroupImpl> mapHostGroups = new HashMap<String, HostGroupImpl>();
 
     for (HostGroupEntity hostGroup : blueprint.getHostGroups()) {
-      mapHostGroups.put(hostGroup.getName(), new HostGroup(hostGroup, stack));
+      mapHostGroups.put(hostGroup.getName(), new HostGroupImpl(hostGroup, stack, this));
     }
     return mapHostGroups;
   }
@@ -119,7 +125,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
   protected Stack parseStack(BlueprintEntity blueprint) throws SystemException {
     Stack stack;
     try {
-      stack = new Stack(blueprint.getStackName(), blueprint.getStackVersion());
+      stack = new Stack(blueprint.getStackName(), blueprint.getStackVersion(), getManagementController());
     } catch (StackAccessException e) {
       throw new IllegalArgumentException("Invalid stack information provided for cluster.  " +
           "stack name: " + blueprint.getStackName() +
@@ -144,15 +150,15 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
    * @throws IllegalArgumentException when validation fails
    */
   protected BlueprintEntity validateTopology(BlueprintEntity blueprint) throws AmbariException {
-    Stack stack = new Stack(blueprint.getStackName(), blueprint.getStackVersion());
-    Map<String, HostGroup> hostGroupMap = parseBlueprintHostGroups(blueprint, stack);
-    Collection<HostGroup> hostGroups = hostGroupMap.values();
+    Stack stack = new Stack(blueprint.getStackName(), blueprint.getStackVersion(), getManagementController());
+    Map<String, HostGroupImpl> hostGroupMap = parseBlueprintHostGroups(blueprint, stack);
+    Collection<HostGroupImpl> hostGroups = hostGroupMap.values();
     Map<String, Map<String, String>> clusterConfig = processBlueprintConfigurations(blueprint, null);
     Map<String, Map<String, Collection<DependencyInfo>>> missingDependencies =
         new HashMap<String, Map<String, Collection<DependencyInfo>>>();
 
     Collection<String> services = getTopologyServices(hostGroups);
-    for (HostGroup group : hostGroups) {
+    for (HostGroupImpl group : hostGroups) {
       Map<String, Collection<DependencyInfo>> missingGroupDependencies =
           group.validateTopology(hostGroups, services, clusterConfig);
       if (! missingGroupDependencies.isEmpty()) {
@@ -206,6 +212,33 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
   }
 
   /**
+   * Process cluster scoped configuration attributes contained in blueprint.
+   *
+   * @param blueprint  blueprint entity
+   *
+   * @return cluster scoped property attributes contained within in blueprint
+   */
+  protected Map<String, Map<String, Map<String, String>>> processBlueprintAttributes(BlueprintEntity blueprint) {
+
+    Map<String, Map<String, Map<String, String>>> mapAttributes =
+        new HashMap<String, Map<String, Map<String, String>>>();
+    Collection<BlueprintConfigEntity> configs = blueprint.getConfigurations();
+
+    if (configs != null) {
+      Gson gson = new Gson();
+      for (BlueprintConfigEntity config : configs) {
+        Map<String, Map<String, String>> typeAttrs =
+            gson.<Map<String, Map<String, String>>>fromJson(config.getConfigAttributes(), Map.class);
+        if (typeAttrs != null && !typeAttrs.isEmpty()) {
+          mapAttributes.put(config.getType(), typeAttrs);
+        }
+      }
+    }
+
+    return mapAttributes;
+  }
+
+  /**
    * Override existing properties or add new.
    *
    * @param existingProperties  current property values
@@ -280,9 +313,9 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
    *
    * @return collections of all services provided by topology
    */
-  protected Collection<String> getTopologyServices(Collection<HostGroup> hostGroups) {
+  protected Collection<String> getTopologyServices(Collection<HostGroupImpl> hostGroups) {
     Collection<String> services = new HashSet<String>();
-    for (HostGroup group : hostGroups) {
+    for (HostGroupImpl group : hostGroups) {
       services.addAll(group.getServices());
     }
     return services;
@@ -328,7 +361,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
    * @return collection of missing component information
    */
   private Collection<String> verifyComponentCardinalityCount(BlueprintEntity blueprint,
-                                                             Collection<HostGroup> hostGroups,
+                                                             Collection<HostGroupImpl> hostGroups,
                                                              String component,
                                                              Cardinality cardinality,
                                                              AutoDeployInfo autoDeploy,
@@ -343,11 +376,11 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
       if (! validated && autoDeploy != null && autoDeploy.isEnabled() && cardinality.supportsAutoDeploy()) {
         String coLocateName = autoDeploy.getCoLocate();
         if (coLocateName != null && ! coLocateName.isEmpty()) {
-          Collection<HostGroup> coLocateHostGroups = getHostGroupsForComponent(
+          Collection<HostGroupImpl> coLocateHostGroups = getHostGroupsForComponent(
               coLocateName.split("/")[1], hostGroups);
           if (! coLocateHostGroups.isEmpty()) {
             validated = true;
-            HostGroup group = coLocateHostGroups.iterator().next();
+            HostGroupImpl group = coLocateHostGroups.iterator().next();
             if (group.addComponent(component)) {
               addComponentToBlueprint(blueprint, group.getEntity().getName(), component);
             }
@@ -374,7 +407,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
    * @return collection of missing component information
    */
   private Collection<String> verifyComponentInAllHostGroups(BlueprintEntity blueprint,
-                                                            Collection<HostGroup> hostGroups,
+                                                            Collection<HostGroupImpl> hostGroups,
                                                             String component,
                                                             AutoDeployInfo autoDeploy) {
 
@@ -382,7 +415,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     int actualCount = getHostGroupsForComponent(component, hostGroups).size();
     if (actualCount != hostGroups.size()) {
       if (autoDeploy != null && autoDeploy.isEnabled()) {
-        for (HostGroup group : hostGroups) {
+        for (HostGroupImpl group : hostGroups) {
           if (group.addComponent(component)) {
             addComponentToBlueprint(blueprint, group.getEntity().getName(), component);
           }
@@ -424,7 +457,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
   /**
    * Encapsulates stack information.
    */
-  protected class Stack {
+  protected static class Stack {
     /**
      * Stack name
      */
@@ -480,8 +513,44 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     /**
      * Map of service to config type properties
      */
-    private Map<String, Map<String, Map<String, String>>> serviceConfigurations =
-        new HashMap<String, Map<String, Map<String, String>>>();
+    private Map<String, Map<String, Map<String, ConfigProperty>>> serviceConfigurations =
+        new HashMap<String, Map<String, Map<String, ConfigProperty>>>();
+
+
+    /**
+     * Ambari Management Controller, used to obtain Stack definitions
+     */
+    private final AmbariManagementController ambariManagementController;
+
+    /**
+     * Contains a configuration property's value and attributes.
+     */
+    private class ConfigProperty {
+
+      private ConfigProperty(String value, Map<String, String> attributes) {
+        this.value = value;
+        this.attributes = attributes;
+      }
+
+      private String value;
+      private Map<String, String> attributes;
+
+      public String getValue() {
+        return value;
+      }
+
+      public void setValue(String value) {
+        this.value = value;
+      }
+
+      public Map<String, String> getAttributes() {
+        return attributes;
+      }
+
+      public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+      }
+    }
 
     /**
      * Constructor.
@@ -492,11 +561,12 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
      * @throws AmbariException an exception occurred getting stack information
      *                         for the specified name and version
      */
-    public Stack(String name, String version) throws AmbariException {
+    public Stack(String name, String version, AmbariManagementController ambariManagementController) throws AmbariException {
       this.name = name;
       this.version = version;
+      this.ambariManagementController = ambariManagementController;
 
-      Set<StackServiceResponse> stackServices = getManagementController().getStackServices(
+      Set<StackServiceResponse> stackServices = ambariManagementController.getStackServices(
           Collections.singleton(new StackServiceRequest(name, version, null)));
 
       for (StackServiceResponse stackService : stackServices) {
@@ -525,6 +595,11 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
       return version;
     }
 
+
+    Map<DependencyInfo, String> getDependencyConditionalServiceMap() {
+      return dependencyConditionalServiceMap;
+    }
+
     /**
      * Get services contained in the stack.
      *
@@ -565,7 +640,47 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
      * @return map of property names to values for the specified service and configuration type
      */
     public Map<String, String> getConfigurationProperties(String service, String type) {
-      return serviceConfigurations.get(service).get(type);
+      Map<String, String> configMap = new HashMap<String, String>();
+      Map<String, ConfigProperty> configProperties = serviceConfigurations.get(service).get(type);
+      if (configProperties != null) {
+        for (Map.Entry<String, ConfigProperty> configProperty : configProperties.entrySet()) {
+          configMap.put(configProperty.getKey(), configProperty.getValue().getValue());
+        }
+      }
+      return configMap;
+    }
+
+    /**
+     * Get config attributes for the specified service and configuration type.
+     *
+     * @param service  service name
+     * @param type     configuration type
+     *
+     * @return  map of attribute names to map of property names to attribute values
+     *          for the specified service and configuration type
+     */
+    public Map<String, Map<String, String>> getConfigurationAttributes(String service, String type) {
+      Map<String, Map<String, String>> attributesMap = new HashMap<String, Map<String, String>>();
+      Map<String, ConfigProperty> configProperties = serviceConfigurations.get(service).get(type);
+      if (configProperties != null) {
+        for (Map.Entry<String, ConfigProperty> configProperty : configProperties.entrySet()) {
+          String propertyName = configProperty.getKey();
+          Map<String, String> propertyAttributes = configProperty.getValue().getAttributes();
+          if (propertyAttributes != null) {
+            for (Map.Entry<String, String> propertyAttribute : propertyAttributes.entrySet()) {
+              String attributeName = propertyAttribute.getKey();
+              String attributeValue = propertyAttribute.getValue();
+              Map<String, String> attributes = attributesMap.get(attributeName);
+              if (attributes == null) {
+                  attributes = new HashMap<String, String>();
+                  attributesMap.put(attributeName, attributes);
+              }
+              attributes.put(propertyName, attributeValue);
+            }
+          }
+        }
+      }
+      return attributesMap;
     }
 
     /**
@@ -603,8 +718,8 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
      * @return name of service which corresponds to the specified configuration type
      */
     public String getServiceForConfigType(String config) {
-      for (Map.Entry<String, Map<String, Map<String, String>>> entry : serviceConfigurations.entrySet()) {
-        Map<String, Map<String, String>> typeMap = entry.getValue();
+      for (Map.Entry<String, Map<String, Map<String, ConfigProperty>>> entry : serviceConfigurations.entrySet()) {
+        Map<String, Map<String, ConfigProperty>> typeMap = entry.getValue();
         if (typeMap.containsKey(config)) {
           return entry.getKey();
         }
@@ -666,7 +781,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     private void parseComponents(String service) throws AmbariException{
       Collection<String> componentSet = new HashSet<String>();
 
-      Set<StackServiceComponentResponse> components = getManagementController().getStackComponents(
+      Set<StackServiceComponentResponse> components = ambariManagementController.getStackComponents(
           Collections.singleton(new StackServiceComponentRequest(name, version, service, null)));
 
       // stack service components
@@ -702,12 +817,15 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
      * @throws AmbariException an exception occurred getting configurations from the stack definition
      */
     private void parseConfigurations(String service) throws AmbariException {
-      Map<String, Map<String, String>> mapServiceConfig = new HashMap<String, Map<String, String>>();
+      Map<String, Map<String, ConfigProperty>> mapServiceConfig = new HashMap<String, Map<String, ConfigProperty>>();
 
       serviceConfigurations.put(service, mapServiceConfig);
 
-      Set<StackConfigurationResponse> serviceConfigs = getManagementController().getStackConfigurations(
+      Set<StackConfigurationResponse> serviceConfigs = ambariManagementController.getStackConfigurations(
           Collections.singleton(new StackConfigurationRequest(name, version, service, null)));
+      Set<StackConfigurationResponse> stackLevelConfigs = ambariManagementController.getStackLevelConfigurations(
+          Collections.singleton(new StackLevelConfigurationRequest(name, version, null)));
+      serviceConfigs.addAll(stackLevelConfigs);
 
       for (StackConfigurationResponse config : serviceConfigs) {
         String type = config.getType();
@@ -715,12 +833,13 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
         if (type.endsWith(".xml")) {
           type = type.substring(0, type.length() - 4);
         }
-        Map<String, String> mapTypeConfig = mapServiceConfig.get(type);
+        Map<String, ConfigProperty> mapTypeConfig = mapServiceConfig.get(type);
         if (mapTypeConfig == null) {
-          mapTypeConfig = new HashMap<String, String>();
+          mapTypeConfig = new HashMap<String, ConfigProperty>();
           mapServiceConfig.put(type, mapTypeConfig);
         }
-        mapTypeConfig.put(config.getPropertyName(), config.getPropertyValue());
+        mapTypeConfig.put(config.getPropertyName(),
+            new ConfigProperty(config.getPropertyValue(), config.getPropertyAttributes()));
       }
     }
 
@@ -728,13 +847,19 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
      * Register conditional dependencies.
      */
     //todo: This information should be specified in the stack definition.
-    private void registerConditionalDependencies() {
+    void registerConditionalDependencies() {
       Collection<DependencyInfo> nagiosDependencies = getDependenciesForComponent("NAGIOS_SERVER");
       for (DependencyInfo dependency : nagiosDependencies) {
         if (dependency.getComponentName().equals("HCAT")) {
-          dependencyConditionalServiceMap.put(dependency, "HCATALOG");
+          dependencyConditionalServiceMap.put(dependency, "HIVE");
         } else if (dependency.getComponentName().equals("OOZIE_CLIENT")) {
           dependencyConditionalServiceMap.put(dependency, "OOZIE");
+        } else if (dependency.getComponentName().equals("YARN_CLIENT")) {
+          dependencyConditionalServiceMap.put(dependency, "YARN");
+        } else if (dependency.getComponentName().equals("TEZ_CLIENT")) {
+          dependencyConditionalServiceMap.put(dependency, "TEZ");
+        } else if (dependency.getComponentName().equals("MAPREDUCE2_CLIENT")) {
+          dependencyConditionalServiceMap.put(dependency, "MAPREDUCE2");
         }
       }
       dbDependencyInfo.put("MYSQL_SERVER", "global/hive_database");
@@ -744,7 +869,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
   /**
    * Host group representation.
    */
-  protected class HostGroup {
+  protected static class HostGroupImpl implements HostGroup {
     /**
      * Host group entity
      */
@@ -778,18 +903,39 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     private Stack stack;
 
     /**
+     * The Blueprint processor associated with this HostGroupImpl instance
+     */
+    private final BaseBlueprintProcessor blueprintProcessor;
+
+    /**
      * Constructor.
      *
      * @param hostGroup  host group
      * @param stack      stack
      */
-    public HostGroup(HostGroupEntity hostGroup, Stack stack) {
+    public HostGroupImpl(HostGroupEntity hostGroup, Stack stack, BaseBlueprintProcessor blueprintProcessor) {
       this.hostGroup = hostGroup;
       this.stack = stack;
+      this.blueprintProcessor = blueprintProcessor;
       parseComponents();
       parseConfigurations();
     }
 
+    @Override
+    public String getName() {
+      return hostGroup.getName();
+    }
+
+    @Override
+    public Collection<String> getComponents() {
+      return this.components;
+    }
+
+    @Override
+    public Collection<String> getHostInfo() {
+      return this.hosts;
+    }
+
     /**
      * Associate a host with the host group.
      *
@@ -800,15 +946,6 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     }
 
     /**
-     * Get associated host information.
-     *
-     * @return collection of hosts associated with the host group
-     */
-    public Collection<String> getHostInfo() {
-      return this.hosts;
-    }
-
-    /**
      * Get the services which are deployed to this host group.
      *
      * @return collection of services which have components in this host group
@@ -818,16 +955,6 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     }
 
     /**
-     * Get the components associated with the host group.
-     *
-     * @return  collection of component names for the host group
-     */
-    public Collection<String> getComponents() {
-      return this.components;
-    }
-
-
-    /**
      * Add a component to the host group.
      *
      * @param component  component to add
@@ -867,7 +994,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
      *
      * @return map of configuration type to a map of properties
      */
-    public Map<String, Map<String, String>> getConfigurations() {
+    public Map<String, Map<String, String>> getConfigurationProperties() {
       return configurations;
     }
 
@@ -889,7 +1016,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
      *
      * @return map of component to missing dependencies
      */
-    public Map<String, Collection<DependencyInfo>> validateTopology(Collection<HostGroup> hostGroups,
+    public Map<String, Collection<DependencyInfo>> validateTopology(Collection<HostGroupImpl> hostGroups,
                                                                     Collection<String> services,
                                                                     Map<String, Map<String, String>> clusterConfig) {
 
@@ -911,14 +1038,14 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
           boolean           resolved        = false;
 
           if (dependencyScope.equals("cluster")) {
-            Collection<String> missingDependencyInfo = verifyComponentCardinalityCount(entity, hostGroups,
+            Collection<String> missingDependencyInfo = blueprintProcessor.verifyComponentCardinalityCount(entity, hostGroups,
                 componentName, new Cardinality("1+"), autoDeployInfo, stack, clusterConfig);
             resolved = missingDependencyInfo.isEmpty();
           } else if (dependencyScope.equals("host")) {
             if (components.contains(component) || (autoDeployInfo != null && autoDeployInfo.isEnabled())) {
               resolved = true;
               if (addComponent(componentName)) {
-                addComponentToBlueprint(hostGroup.getBlueprintEntity(), getEntity().getName(), componentName);
+                blueprintProcessor.addComponentToBlueprint(hostGroup.getBlueprintEntity(), getEntity().getName(), componentName);
               }
             }
           }
@@ -957,8 +1084,12 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
           typeProperties = new HashMap<String, String>();
           configurations.put(type, typeProperties);
         }
-        configurations.put(type, jsonSerializer.<Map<String, String>>fromJson(
-            configEntity.getConfigData(), Map.class));
+        Map<String, String> propertyMap =  jsonSerializer.<Map<String, String>>fromJson(
+            configEntity.getConfigData(), Map.class);
+
+        if (propertyMap != null) {
+          typeProperties.putAll(propertyMap);
+        }
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/9213dcca/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseProvider.java
index f31ed7a..b4d582a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseProvider.java
@@ -18,6 +18,16 @@
 
 package org.apache.ambari.server.controller.internal;
 
+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.regex.Matcher;
+import java.util.regex.Pattern;
+
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.Request;
 import org.apache.ambari.server.controller.spi.Resource;
@@ -26,10 +36,6 @@ import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 /**
  * Base provider implementation for both property and resource providers.
  */
@@ -61,6 +67,19 @@ public abstract class BaseProvider {
   protected final static Logger LOG =
       LoggerFactory.getLogger(BaseProvider.class);
 
+  /**
+   * The regex pattern that will match on all $1, $2.method(0), and
+   * $3.method(\"/\",\"///\") and properly substitute. For example, it can turn
+   * <p/>
+   * {@code
+   * metrics/yarn/Queue/$1.replaceAll(\"([.])\",\"/\")/AppsCompleted/$2.substring(0)/foo/$3/bar/$4
+   * }
+   * <p/>
+   * into
+   * <p/>
+   * {@code metrics/yarn/Queue/-/AppsCompleted/-/foo/-/bar/-}
+   */
+  private static final Pattern METRIC_ARGUMENT_METHOD_REPLACEMENT = Pattern.compile("\\$\\d+(\\.\\w+(\\(\\\\\"\\S+\\\\\"\\)|\\(\\d+\\)))*");
 
   // ----- Constructors ------------------------------------------------------
 
@@ -71,13 +90,17 @@ public abstract class BaseProvider {
    */
   public BaseProvider(Set<String> propertyIds) {
     this.propertyIds = new HashSet<String>(propertyIds);
-    this.categoryIds = PropertyHelper.getCategories(propertyIds);
-    this.combinedIds = new HashSet<String>(propertyIds);
-    this.combinedIds.addAll(this.categoryIds);
-    this.patterns = new HashMap<String, Pattern>();
-    for (String id : this.combinedIds) {
+    categoryIds = PropertyHelper.getCategories(propertyIds);
+    combinedIds = new HashSet<String>(propertyIds);
+    combinedIds.addAll(categoryIds);
+    patterns = new HashMap<String, Pattern>();
+
+    // convert the argumented metric as it's defined in the JSON file to regex
+    for (String id : combinedIds) {
       if (containsArguments(id)) {
-        String pattern = id.replaceAll("\\$\\d+(\\.\\S+\\(\\S+\\))*", "(\\\\S+)");
+        String pattern = METRIC_ARGUMENT_METHOD_REPLACEMENT.matcher(id).replaceAll(
+            "(\\\\S*)");
+
         patterns.put(id, Pattern.compile(pattern));
       }
     }
@@ -93,15 +116,17 @@ public abstract class BaseProvider {
    */
   protected Set<String> checkConfigPropertyIds(Set<String> base, String configCategory) {
 
-    if (0 == base.size())
+    if (0 == base.size()) {
       return base;
+    }
 
     Set<String> unsupported = new HashSet<String>();
 
     for (String propertyId : base)
     {
-      if (!propertyId.startsWith(configCategory + "/desired_config"))
+      if (!propertyId.startsWith(configCategory + "/desired_config")) {
         unsupported.add(propertyId);
+      }
     }
 
     return unsupported;
@@ -110,7 +135,7 @@ public abstract class BaseProvider {
   public Set<String> checkPropertyIds(Set<String> propertyIds) {
     if (!this.propertyIds.containsAll(propertyIds)) {
       Set<String> unsupportedPropertyIds = new HashSet<String>(propertyIds);
-      unsupportedPropertyIds.removeAll(this.combinedIds);
+      unsupportedPropertyIds.removeAll(combinedIds);
 
       // If the property id is not in the set of known property ids we may still allow it if
       // its parent category is a known property. This allows for Map type properties where
@@ -150,17 +175,17 @@ public abstract class BaseProvider {
       propertyIds.addAll(PredicateHelper.getPropertyIds(predicate));
     }
 
-    if (!this.combinedIds.containsAll(propertyIds)) {
+    if (!combinedIds.containsAll(propertyIds)) {
       Set<String> keepers = new HashSet<String>();
       Set<String> unsupportedPropertyIds = new HashSet<String>(propertyIds);
-      unsupportedPropertyIds.removeAll(this.combinedIds);
+      unsupportedPropertyIds.removeAll(combinedIds);
 
       for (String unsupportedPropertyId : unsupportedPropertyIds) {
         if (checkCategory(unsupportedPropertyId) || checkRegExp(unsupportedPropertyId)) {
           keepers.add(unsupportedPropertyId);
         }
       }
-      propertyIds.retainAll(this.combinedIds);
+      propertyIds.retainAll(combinedIds);
       propertyIds.addAll(keepers);
     }
     return propertyIds;
@@ -173,7 +198,7 @@ public abstract class BaseProvider {
   protected boolean checkCategory(String unsupportedPropertyId) {
     String category = PropertyHelper.getPropertyCategory(unsupportedPropertyId);
     while (category != null) {
-      if(this.propertyIds.contains(category)) {
+      if(propertyIds.contains(category)) {
         return true;
       }
       category = PropertyHelper.getPropertyCategory(category);
@@ -191,20 +216,28 @@ public abstract class BaseProvider {
     return false;
   }
 
-  protected String getRegExpKey(String id) {
-    String regExpKey = null;
+  /**
+   * Gets the key/value mapping between a metric string in the JSON files and
+   * the Java regular expression pattern that matches it.
+   *
+   * @param id
+   * @return the entry, or {@code null} if none match the ID.
+   */
+  protected Map.Entry<String, Pattern> getRegexEntry(String id) {
+    Map.Entry<String, Pattern> regexEntry = null;
+
     for (Map.Entry<String, Pattern> entry : patterns.entrySet()) {
       Pattern pattern = entry.getValue();
       Matcher matcher = pattern.matcher(id);
 
       if (matcher.matches()) {
         String key = entry.getKey();
-        if (regExpKey == null || key.startsWith(regExpKey)) {
-          regExpKey = entry.getKey();
+        if (regexEntry == null || key.startsWith(regexEntry.getKey())) {
+          regexEntry = entry;
         }
       }
     }
-    return regExpKey;
+    return regexEntry;
   }
 
   /**