/*
* 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.Collection;
import java.util.HashMap;
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.StaticallyInject;
import org.apache.ambari.server.controller.AlertDefinitionResponse;
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.RequestStatus;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
import org.apache.ambari.server.controller.spi.SystemException;
import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
import org.apache.ambari.server.orm.dao.AlertDispatchDAO;
import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
import org.apache.ambari.server.orm.entities.AlertGroupEntity;
import org.apache.ambari.server.orm.entities.AlertTargetEntity;
import org.apache.ambari.server.security.authorization.AuthorizationException;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.alert.AlertTarget;
import org.apache.commons.lang.StringUtils;
import com.google.inject.Inject;
/**
* The {@link AlertGroupResourceProvider} class deals with managing the CRUD
* operations alert groups, including property coercion to and from
* {@link AlertGroupEntity}.
*/
@StaticallyInject
public class AlertGroupResourceProvider extends
AbstractControllerResourceProvider {
public static final String ALERT_GROUP = "AlertGroup";
public static final String ALERT_GROUP_ID = "AlertGroup/id";
public static final String ALERT_GROUP_CLUSTER_NAME = "AlertGroup/cluster_name";
public static final String ALERT_GROUP_NAME = "AlertGroup/name";
public static final String ALERT_GROUP_DEFAULT = "AlertGroup/default";
public static final String ALERT_GROUP_DEFINITIONS = "AlertGroup/definitions";
public static final String ALERT_GROUP_TARGETS = "AlertGroup/targets";
private static final Set<String> PK_PROPERTY_IDS = new HashSet<>(
Arrays.asList(ALERT_GROUP_ID, ALERT_GROUP_CLUSTER_NAME));
/**
* The property ids for an alert group resource.
*/
private static final Set<String> PROPERTY_IDS = new HashSet<>();
/**
* The key property ids for an alert group resource.
*/
private static final Map<Resource.Type, String> KEY_PROPERTY_IDS = new HashMap<>();
static {
// properties
PROPERTY_IDS.add(ALERT_GROUP_ID);
PROPERTY_IDS.add(ALERT_GROUP_CLUSTER_NAME);
PROPERTY_IDS.add(ALERT_GROUP_NAME);
PROPERTY_IDS.add(ALERT_GROUP_DEFAULT);
PROPERTY_IDS.add(ALERT_GROUP_DEFINITIONS);
PROPERTY_IDS.add(ALERT_GROUP_TARGETS);
// keys
KEY_PROPERTY_IDS.put(Resource.Type.AlertGroup, ALERT_GROUP_ID);
KEY_PROPERTY_IDS.put(Resource.Type.Cluster, ALERT_GROUP_CLUSTER_NAME);
}
/**
* Group/Target DAO
*/
@Inject
private static AlertDispatchDAO s_dao;
/**
* Definitions DAO
*/
@Inject
private static AlertDefinitionDAO s_definitionDao;
/**
* Constructor.
*
* @param controller
*/
AlertGroupResourceProvider(AmbariManagementController controller) {
super(PROPERTY_IDS, KEY_PROPERTY_IDS, controller);
}
@Override
public RequestStatus createResources(final Request request)
throws SystemException,
UnsupportedPropertyException, ResourceAlreadyExistsException,
NoSuchParentResourceException {
createResources(new Command<Void>() {
@Override
public Void invoke() throws AmbariException, AuthorizationException {
createAlertGroups(request.getProperties());
return null;
}
});
notifyCreate(Resource.Type.AlertGroup, request);
return getRequestStatus(null);
}
@Override
public Set<Resource> getResources(Request request, Predicate predicate)
throws SystemException, UnsupportedPropertyException,
NoSuchResourceException, NoSuchParentResourceException {
Set<Resource> results = new HashSet<>();
Set<String> requestPropertyIds = getRequestPropertyIds(request, predicate);
for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
String clusterName = (String) propertyMap.get(ALERT_GROUP_CLUSTER_NAME);
if (null == clusterName || clusterName.isEmpty()) {
throw new IllegalArgumentException("The cluster name is required when retrieving alert groups");
}
String id = (String) propertyMap.get(ALERT_GROUP_ID);
if (null != id) {
AlertGroupEntity entity = s_dao.findGroupById(Long.parseLong(id));
if (null != entity) {
try {
AlertResourceProviderUtils.verifyViewAuthorization(entity, getClusterResourceId(entity.getClusterId()));
} catch (AmbariException e) {
throw new SystemException(e.getMessage(), e);
}
results.add(toResource(clusterName, entity, requestPropertyIds));
}
} else {
Cluster cluster = null;
try {
cluster = getManagementController().getClusters().getCluster(clusterName);
} catch (AmbariException ae) {
throw new NoSuchResourceException("Parent Cluster resource doesn't exist", ae);
}
List<AlertGroupEntity> entities = s_dao.findAllGroups(cluster.getClusterId());
for (AlertGroupEntity entity : entities) {
try {
if (AlertResourceProviderUtils.hasViewAuthorization(entity, getClusterResourceId(entity.getClusterId()))) {
results.add(toResource(clusterName, entity, requestPropertyIds));
}
} catch (AmbariException e) {
throw new SystemException(e.getMessage(), e);
}
}
}
}
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, AuthorizationException {
updateAlertGroups(request.getProperties());
return null;
}
});
notifyUpdate(Resource.Type.AlertGroup, request, predicate);
return getRequestStatus(null);
}
@Override
public RequestStatus deleteResources(Request request, Predicate predicate)
throws SystemException, UnsupportedPropertyException,
NoSuchResourceException, NoSuchParentResourceException {
Set<Resource> resources = getResources(new RequestImpl(null, null, null,
null), predicate);
Map<Long, AlertGroupEntity> entities = new HashMap<>();
for (final Resource resource : resources) {
Long id = (Long) resource.getPropertyValue(ALERT_GROUP_ID);
if (!entities.containsKey(id)) {
AlertGroupEntity entity = s_dao.findGroupById(id);
try {
AlertResourceProviderUtils.verifyManageAuthorization(entity, getClusterResourceId(entity.getClusterId()));
entities.put(id, entity);
} catch (AmbariException e) {
LOG.warn("The default alert group for {} cannot be removed", entity.getServiceName(), e);
}
}
}
for (final AlertGroupEntity entity : entities.values()) {
LOG.info("Deleting alert group {}", entity.getGroupId());
if (entity.isDefault()) {
// default groups cannot be removed
LOG.warn("The default alert group for {} cannot be removed",
entity.getServiceName());
continue;
}
modifyResources(new Command<Void>() {
@Override
public Void invoke() throws AmbariException {
s_dao.remove(entity);
return null;
}
});
}
notifyDelete(Resource.Type.AlertGroup, 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
*/
@SuppressWarnings("unchecked")
private void createAlertGroups(Set<Map<String, Object>> requestMaps)
throws AmbariException, AuthorizationException {
List<AlertGroupEntity> entities = new ArrayList<>();
for (Map<String, Object> requestMap : requestMaps) {
AlertGroupEntity entity = new AlertGroupEntity();
String name = (String) requestMap.get(ALERT_GROUP_NAME);
String clusterName = (String) requestMap.get(ALERT_GROUP_CLUSTER_NAME);
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException(
"The name of the alert group is required.");
}
if (StringUtils.isEmpty(clusterName)) {
throw new IllegalArgumentException(
"The name of the cluster is required when creating an alert group.");
}
Cluster cluster = getManagementController().getClusters().getCluster(
clusterName);
entity.setClusterId(cluster.getClusterId());
entity.setGroupName(name);
// groups created through the provider are not default service groups
entity.setDefault(false);
// targets are not required on creation
if (requestMap.containsKey(ALERT_GROUP_TARGETS)) {
List<Long> targetIds = (List<Long>) requestMap.get(ALERT_GROUP_TARGETS);
Set<AlertTargetEntity> targets = new HashSet<>();
targets.addAll(s_dao.findTargetsById(targetIds));
entity.setAlertTargets(targets);
}
// definitions are not required on creation
if (requestMap.containsKey(ALERT_GROUP_DEFINITIONS)) {
List<Long> definitionIds = (List<Long>) requestMap.get(ALERT_GROUP_DEFINITIONS);
Set<AlertDefinitionEntity> definitions = new HashSet<>();
definitions.addAll(s_definitionDao.findByIds(definitionIds));
entity.setAlertDefinitions(definitions);
}
AlertResourceProviderUtils.verifyManageAuthorization(entity, cluster.getResourceId());
entities.add(entity);
}
s_dao.createGroups(entities);
}
/**
* Updates existing {@link AlertGroupEntity}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.
*/
@SuppressWarnings("unchecked")
private void updateAlertGroups(Set<Map<String, Object>> requestMaps)
throws AmbariException, AuthorizationException {
for (Map<String, Object> requestMap : requestMaps) {
String stringId = (String) requestMap.get(ALERT_GROUP_ID);
if( StringUtils.isEmpty(stringId)){
throw new IllegalArgumentException("The ID of the alert group is required when updating an existing group");
}
long id = Long.parseLong(stringId);
AlertGroupEntity entity = s_dao.findGroupById(id);
if( null == entity ){
String message = MessageFormat.format("The alert group with ID {0} could not be found", id);
throw new AmbariException(message);
}
AlertResourceProviderUtils.verifyManageAuthorization(entity, getClusterResourceId(entity.getClusterId()));
String name = (String) requestMap.get(ALERT_GROUP_NAME);
// empty arrays are deserialized as HashSet while populated arrays
// are deserialized as ArrayList; use Collection for safety
Collection<Long> targetIds = (Collection<Long>) requestMap.get(ALERT_GROUP_TARGETS);
Collection<Long> definitionIds = (Collection<Long>) requestMap.get(ALERT_GROUP_DEFINITIONS);
// if targets were supplied, replace existing
if (null != targetIds) {
Set<AlertTargetEntity> targets = new HashSet<>();
List<Long> ids = new ArrayList<>(targetIds.size());
ids.addAll(targetIds);
if (ids.size() > 0) {
targets.addAll(s_dao.findTargetsById(ids));
}
entity.setAlertTargets(targets);
}
// only the targets should be updatable on default groups; everything
// else is valid only on regular groups
if (!entity.isDefault()) {
// set the name if supplied
if (!StringUtils.isBlank(name)) {
entity.setGroupName(name);
}
// if definitions were supplied, replace existing
Set<AlertDefinitionEntity> definitions = new HashSet<>();
if (null != definitionIds && definitionIds.size() > 0) {
List<Long> ids = new ArrayList<>(definitionIds.size());
ids.addAll(definitionIds);
definitions.addAll(s_definitionDao.findByIds(ids));
entity.setAlertDefinitions(definitions);
} else {
// empty array supplied, clear out existing definitions
entity.setAlertDefinitions(definitions);
}
}
s_dao.merge(entity);
}
}
/**
* Convert the given {@link AlertGroupEntity} to a {@link Resource}.
*
* @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(String clusterName, AlertGroupEntity entity,
Set<String> requestedIds) {
Resource resource = new ResourceImpl(Resource.Type.AlertGroup);
resource.setProperty(ALERT_GROUP_ID, entity.getGroupId());
resource.setProperty(ALERT_GROUP_NAME, entity.getGroupName());
resource.setProperty(ALERT_GROUP_CLUSTER_NAME, clusterName);
setResourceProperty(resource, ALERT_GROUP_DEFAULT, entity.isDefault(),
requestedIds);
// only set the definitions if requested
if (BaseProvider.isPropertyRequested(ALERT_GROUP_DEFINITIONS, requestedIds)) {
Set<AlertDefinitionEntity> definitions = entity.getAlertDefinitions();
List<AlertDefinitionResponse> definitionList = new ArrayList<>(
definitions.size());
for (AlertDefinitionEntity definition : definitions) {
AlertDefinitionResponse response = AlertDefinitionResponse.coerce(definition);
definitionList.add(response);
}
resource.setProperty(ALERT_GROUP_DEFINITIONS, definitionList);
}
if (BaseProvider.isPropertyRequested(ALERT_GROUP_TARGETS, requestedIds)) {
Set<AlertTargetEntity> targetEntities = entity.getAlertTargets();
List<AlertTarget> targets = new ArrayList<>(
targetEntities.size());
for (AlertTargetEntity targetEntity : targetEntities) {
AlertTarget target = AlertTarget.coerce(targetEntity);
targets.add(target);
}
resource.setProperty(ALERT_GROUP_TARGETS, targets);
}
return resource;
}
}