/**
* 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.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
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 org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.StackAccessException;
import org.apache.ambari.server.StaticallyInject;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
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.controller.utilities.PropertyHelper;
import org.apache.ambari.server.state.ServiceInfo;
import org.apache.ambari.server.state.StackInfo;
import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosDescriptorFactory;
import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptorFactory;
import org.apache.ambari.server.state.stack.Metric;
import org.apache.ambari.server.state.stack.MetricDefinition;
import org.apache.ambari.server.state.stack.WidgetLayout;
import org.apache.commons.lang.StringUtils;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
/**
* Provider for stack and stack service artifacts.
* Artifacts contain an artifact name as the PK and artifact data in the form of
* a map which is the content of the artifact.
* <p>
* An example of an artifact is a kerberos descriptor.
* <p>
* Stack artifacts are part of the stack definition and therefore can't
* be created, updated or deleted.
*/
@StaticallyInject
public class StackArtifactResourceProvider extends AbstractControllerResourceProvider {
/**
* stack name
*/
public static final String STACK_NAME_PROPERTY_ID =
PropertyHelper.getPropertyId("Artifacts", "stack_name");
/**
* stack version
*/
public static final String STACK_VERSION_PROPERTY_ID =
PropertyHelper.getPropertyId("Artifacts", "stack_version");
/**
* stack service name
*/
public static final String STACK_SERVICE_NAME_PROPERTY_ID =
PropertyHelper.getPropertyId("Artifacts", "service_name");
/**
* stack service name
*/
public static final String STACK_COMPONENT_NAME_PROPERTY_ID =
PropertyHelper.getPropertyId("Artifacts", "component_name");
/**
* artifact name
*/
public static final String ARTIFACT_NAME_PROPERTY_ID =
PropertyHelper.getPropertyId("Artifacts", "artifact_name");
/**
* artifact data
*/
public static final String ARTIFACT_DATA_PROPERTY_ID = "artifact_data";
/**
* primary key fields
*/
public static Set<String> pkPropertyIds = new HashSet<>();
/**
* map of resource type to fk field
*/
public static Map<Resource.Type, String> keyPropertyIds =
new HashMap<>();
/**
* resource properties
*/
public static Set<String> propertyIds = new HashSet<>();
/**
* name of the kerberos descriptor artifact.
*/
public static final String KERBEROS_DESCRIPTOR_NAME = "kerberos_descriptor";
/**
* name of the metrics descriptor artifact.
*/
public static final String METRICS_DESCRIPTOR_NAME = "metrics_descriptor";
/**
* name of the widgets descriptor artifact.
*/
public static final String WIDGETS_DESCRIPTOR_NAME = "widgets_descriptor";
/**
* KerberosDescriptorFactory used to create KerberosDescriptor instances
*/
@Inject
private static KerberosDescriptorFactory kerberosDescriptorFactory;
/**
* KerberosServiceDescriptorFactory used to create KerberosServiceDescriptor instances
*/
@Inject
private static KerberosServiceDescriptorFactory kerberosServiceDescriptorFactory;
Type widgetLayoutType = new TypeToken<Map<String, List<WidgetLayout>>>(){}.getType();
Gson gson = new Gson();
/**
* set resource properties, pk and fk's
*/
static {
// resource properties
propertyIds.add(STACK_NAME_PROPERTY_ID);
propertyIds.add(STACK_VERSION_PROPERTY_ID);
propertyIds.add(STACK_SERVICE_NAME_PROPERTY_ID);
propertyIds.add(ARTIFACT_NAME_PROPERTY_ID);
propertyIds.add(ARTIFACT_DATA_PROPERTY_ID);
// pk property
pkPropertyIds.add(ARTIFACT_NAME_PROPERTY_ID);
// fk properties
keyPropertyIds.put(Resource.Type.StackArtifact, ARTIFACT_NAME_PROPERTY_ID);
keyPropertyIds.put(Resource.Type.Stack, STACK_NAME_PROPERTY_ID);
keyPropertyIds.put(Resource.Type.StackVersion, STACK_VERSION_PROPERTY_ID);
keyPropertyIds.put(Resource.Type.StackService, STACK_SERVICE_NAME_PROPERTY_ID);
}
/**
* Constructor.
*
* @param managementController ambari controller
*/
protected StackArtifactResourceProvider(AmbariManagementController managementController) {
super(propertyIds, keyPropertyIds, managementController);
}
@Override
public Set<Resource> getResources(Request request, Predicate predicate)
throws SystemException,
UnsupportedPropertyException,
NoSuchResourceException,
NoSuchParentResourceException {
Set<Resource> resources = new HashSet<>();
resources.addAll(getKerberosDescriptors(request, predicate));
resources.addAll(getMetricsDescriptors(request, predicate));
resources.addAll(getWidgetsDescriptors(request, predicate));
// add other artifacts types here
if (resources.isEmpty()) {
throw new NoSuchResourceException(
"The requested resource doesn't exist: Artifact not found, " + predicate);
}
return resources;
}
@Override
protected Set<String> getPKPropertyIds() {
return pkPropertyIds;
}
@Override
public RequestStatus createResources(Request request)
throws SystemException,
UnsupportedPropertyException,
ResourceAlreadyExistsException,
NoSuchParentResourceException {
throw new UnsupportedOperationException("Creating stack artifacts is not supported");
}
@Override
public RequestStatus updateResources(Request request, Predicate predicate)
throws SystemException,
UnsupportedPropertyException,
NoSuchResourceException,
NoSuchParentResourceException {
throw new UnsupportedOperationException("Updating of stack artifacts is not supported");
}
@Override
public RequestStatus deleteResources(Request request, Predicate predicate)
throws SystemException,
UnsupportedPropertyException,
NoSuchResourceException,
NoSuchParentResourceException {
throw new UnsupportedOperationException("Deletion of stack artifacts is not supported");
}
/**
* Get all stack and stack service kerberos descriptor resources.
*
* @param request user request
* @param predicate request predicate
*
* @return set of all stack related kerberos descriptor resources; will not return null
*
* @throws SystemException if an unexpected exception occurs
* @throws UnsupportedPropertyException if an unsupported property was requested
* @throws NoSuchParentResourceException if a specified parent resource doesn't exist
* @throws NoSuchResourceException if the requested resource doesn't exist
*/
private Set<Resource> getKerberosDescriptors(Request request, Predicate predicate)
throws SystemException,
UnsupportedPropertyException,
NoSuchParentResourceException,
NoSuchResourceException {
Set<Resource> resources = new HashSet<>();
for (Map<String, Object> properties : getPropertyMaps(predicate)) {
String artifactName = (String) properties.get(ARTIFACT_NAME_PROPERTY_ID);
if (artifactName == null || artifactName.equals(KERBEROS_DESCRIPTOR_NAME)) {
String stackName = (String) properties.get(STACK_NAME_PROPERTY_ID);
String stackVersion = (String) properties.get(STACK_VERSION_PROPERTY_ID);
String stackService = (String) properties.get(STACK_SERVICE_NAME_PROPERTY_ID);
Map<String, Object> descriptor;
try {
descriptor = getKerberosDescriptor(stackName, stackVersion, stackService);
} catch (IOException e) {
LOG.error("Unable to process Kerberos Descriptor. Properties: " + properties, e);
throw new SystemException("An internal exception occurred while attempting to build a Kerberos Descriptor " +
"artifact. See ambari server logs for more information", e);
}
if (descriptor != null) {
Resource resource = new ResourceImpl(Resource.Type.StackArtifact);
Set<String> requestedIds = getRequestPropertyIds(request, predicate);
setResourceProperty(resource, ARTIFACT_NAME_PROPERTY_ID, KERBEROS_DESCRIPTOR_NAME, requestedIds);
setResourceProperty(resource, ARTIFACT_DATA_PROPERTY_ID, descriptor, requestedIds);
setResourceProperty(resource, STACK_NAME_PROPERTY_ID, stackName, requestedIds);
setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, stackVersion, requestedIds);
if (stackService != null) {
setResourceProperty(resource, STACK_SERVICE_NAME_PROPERTY_ID, stackService, requestedIds);
}
resources.add(resource);
}
}
}
return resources;
}
/**
* Get all stack and stack service metrics descriptor resources.
*
* @param request user request
* @param predicate request predicate
*
* @return set of all stack related kerberos descriptor resources; will not return null
*
* @throws SystemException if an unexpected exception occurs
* @throws UnsupportedPropertyException if an unsupported property was requested
* @throws NoSuchParentResourceException if a specified parent resource doesn't exist
* @throws NoSuchResourceException if the requested resource doesn't exist
*/
private Set<Resource> getMetricsDescriptors(Request request, Predicate predicate)
throws SystemException, UnsupportedPropertyException,
NoSuchParentResourceException, NoSuchResourceException {
Set<Resource> resources = new HashSet<>();
for (Map<String, Object> properties : getPropertyMaps(predicate)) {
String artifactName = (String) properties.get(ARTIFACT_NAME_PROPERTY_ID);
if (artifactName == null || artifactName.equals(METRICS_DESCRIPTOR_NAME)) {
String stackName = (String) properties.get(STACK_NAME_PROPERTY_ID);
String stackVersion = (String) properties.get(STACK_VERSION_PROPERTY_ID);
String stackService = (String) properties.get(STACK_SERVICE_NAME_PROPERTY_ID);
String componentName = (String) properties.get(STACK_COMPONENT_NAME_PROPERTY_ID);
Map<String, Object> descriptor;
AmbariMetaInfo metaInfo = getManagementController().getAmbariMetaInfo();
try {
List<MetricDefinition> componentMetrics;
Map<String, Map<String, List<MetricDefinition>>> serviceMetrics;
if (stackService != null) {
if (componentName == null) {
// Service
serviceMetrics = removeAggregateFunctions(metaInfo.getServiceMetrics(stackName,
stackVersion, stackService));
descriptor = Collections.singletonMap(stackService, (Object) serviceMetrics);
} else {
// Component
componentMetrics = removeAggregateFunctions(metaInfo.getMetrics(stackName,
stackVersion, stackService, componentName, Resource.Type.Component.name()));
descriptor = Collections.singletonMap(componentName, (Object) componentMetrics);
}
} else {
// Cluster
Map<String, Map<String, PropertyInfo>> clusterMetrics =
PropertyHelper.getMetricPropertyIds(Resource.Type.Cluster);
// Host
Map<String, Map<String, PropertyInfo>> hostMetrics =
PropertyHelper.getMetricPropertyIds(Resource.Type.Host);
descriptor = new HashMap<>();
descriptor.put(Resource.Type.Cluster.name(), clusterMetrics);
descriptor.put(Resource.Type.Host.name(), hostMetrics);
}
} catch (IOException e) {
LOG.error("Unable to process Metrics Descriptor. Properties: " + properties, e);
throw new SystemException("An internal exception occurred while attempting to build a Metrics Descriptor " +
"artifact. See ambari server logs for more information", e);
}
Resource resource = new ResourceImpl(Resource.Type.StackArtifact);
Set<String> requestedIds = getRequestPropertyIds(request, predicate);
setResourceProperty(resource, ARTIFACT_NAME_PROPERTY_ID, METRICS_DESCRIPTOR_NAME, requestedIds);
setResourceProperty(resource, ARTIFACT_DATA_PROPERTY_ID, descriptor, requestedIds);
setResourceProperty(resource, STACK_NAME_PROPERTY_ID, stackName, requestedIds);
setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, stackVersion, requestedIds);
if (stackService != null) {
setResourceProperty(resource, STACK_SERVICE_NAME_PROPERTY_ID, stackService, requestedIds);
}
resources.add(resource);
}
}
return resources;
}
private Set<Resource> getWidgetsDescriptors(Request request, Predicate predicate)
throws SystemException, UnsupportedPropertyException,
NoSuchParentResourceException, NoSuchResourceException {
Set<Resource> resources = new HashSet<>();
for (Map<String, Object> properties : getPropertyMaps(predicate)) {
String artifactName = (String) properties.get(ARTIFACT_NAME_PROPERTY_ID);
if (artifactName == null || artifactName.equals(WIDGETS_DESCRIPTOR_NAME)) {
String stackName = (String) properties.get(STACK_NAME_PROPERTY_ID);
String stackVersion = (String) properties.get(STACK_VERSION_PROPERTY_ID);
String stackService = (String) properties.get(STACK_SERVICE_NAME_PROPERTY_ID);
Map<String, Object> descriptor;
try {
descriptor = getWidgetsDescriptor(stackName, stackVersion, stackService);
} catch (IOException e) {
LOG.error("Unable to process Widgets Descriptor. Properties: " + properties, e);
throw new SystemException("An internal exception occurred while attempting to build a Widgets Descriptor " +
"artifact. See ambari server logs for more information", e);
}
if (descriptor != null) {
Resource resource = new ResourceImpl(Resource.Type.StackArtifact);
Set<String> requestedIds = getRequestPropertyIds(request, predicate);
setResourceProperty(resource, ARTIFACT_NAME_PROPERTY_ID, WIDGETS_DESCRIPTOR_NAME, requestedIds);
setResourceProperty(resource, ARTIFACT_DATA_PROPERTY_ID, descriptor, requestedIds);
setResourceProperty(resource, STACK_NAME_PROPERTY_ID, stackName, requestedIds);
setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, stackVersion, requestedIds);
if (stackService != null) {
setResourceProperty(resource, STACK_SERVICE_NAME_PROPERTY_ID, stackService, requestedIds);
}
resources.add(resource);
}
}
}
return resources;
}
private Map<String, Object> getWidgetsDescriptor(String stackName,
String stackVersion, String serviceName)
throws NoSuchParentResourceException, IOException {
AmbariManagementController controller = getManagementController();
StackInfo stackInfo;
try {
stackInfo = controller.getAmbariMetaInfo().getStack(stackName, stackVersion);
} catch (StackAccessException e) {
throw new NoSuchParentResourceException(String.format(
"Parent stack resource doesn't exist: stackName='%s', stackVersion='%s'", stackName, stackVersion));
}
if (StringUtils.isEmpty(serviceName)) {
return getWidgetsDescriptorForCluster(stackInfo);
} else {
return getWidgetsDescriptorForService(stackInfo, serviceName);
}
}
public Map<String, Object> getWidgetsDescriptorForService(StackInfo stackInfo, String serviceName)
throws NoSuchParentResourceException, IOException {
Map<String, Object> widgetDescriptor = null;
ServiceInfo serviceInfo = stackInfo.getService(serviceName);
if (serviceInfo == null) {
throw new NoSuchParentResourceException("Service not found. serviceName" + " = " + serviceName);
}
File widgetDescriptorFile = serviceInfo.getWidgetsDescriptorFile();
if (widgetDescriptorFile != null && widgetDescriptorFile.exists()) {
widgetDescriptor = gson.fromJson(new FileReader(widgetDescriptorFile), widgetLayoutType);
}
return widgetDescriptor;
}
public Map<String, Object> getWidgetsDescriptorForCluster(StackInfo stackInfo)
throws NoSuchParentResourceException, IOException {
Map<String, Object> widgetDescriptor = null;
String widgetDescriptorFileLocation = stackInfo.getWidgetsDescriptorFileLocation();
if (widgetDescriptorFileLocation != null) {
File widgetDescriptorFile = new File(widgetDescriptorFileLocation);
if (widgetDescriptorFile.exists()) {
widgetDescriptor = gson.fromJson(new FileReader(widgetDescriptorFile), widgetLayoutType);
}
}
return widgetDescriptor;
}
/**
* Get a kerberos descriptor.
*
* @param stackName stack name
* @param stackVersion stack version
* @param serviceName service name
*
* @return map of kerberos descriptor data or null if no descriptor exists
*
* @throws IOException if unable to parse the associated kerberos descriptor file
* @throws NoSuchParentResourceException if the parent stack or stack service doesn't exist
*/
private Map<String, Object> getKerberosDescriptor(String stackName, String stackVersion, String serviceName)
throws NoSuchParentResourceException, IOException {
return serviceName == null ?
buildStackDescriptor(stackName, stackVersion) :
getServiceDescriptor(stackName, stackVersion, serviceName);
}
/**
* Build a kerberos descriptor for the specified stack. This descriptor is for the entire stack version
* and will contain both the stack descriptor as well as all service descriptors.
*
* @return map of kerberos descriptor data or null if no descriptor exists
*
* @throws IOException if unable to read the kerberos descriptor file
* @throws AmbariException if unable to parse the kerberos descriptor file json
* @throws NoSuchParentResourceException if the parent stack doesn't exist
*/
private Map<String, Object> buildStackDescriptor(String stackName, String stackVersion)
throws NoSuchParentResourceException, IOException {
KerberosDescriptor kerberosDescriptor = null;
AmbariManagementController controller = getManagementController();
StackInfo stackInfo;
try {
stackInfo = controller.getAmbariMetaInfo().getStack(stackName, stackVersion);
} catch (StackAccessException e) {
throw new NoSuchParentResourceException(String.format(
"Parent stack resource doesn't exist: stackName='%s', stackVersion='%s'", stackName, stackVersion));
}
Collection<KerberosServiceDescriptor> serviceDescriptors = getServiceDescriptors(stackInfo);
String kerberosFileLocation = stackInfo.getKerberosDescriptorFileLocation();
if (kerberosFileLocation != null) {
kerberosDescriptor = kerberosDescriptorFactory.createInstance(new File(kerberosFileLocation));
} else if (! serviceDescriptors.isEmpty()) {
// service descriptors present with no stack descriptor,
// create an empty stack descriptor to hold services
kerberosDescriptor = new KerberosDescriptor();
}
if (kerberosDescriptor != null) {
for (KerberosServiceDescriptor descriptor : serviceDescriptors) {
kerberosDescriptor.putService(descriptor);
}
return kerberosDescriptor.toMap();
} else {
return null;
}
}
/**
* Get the kerberos descriptor for the specified stack service.
*
* @param stackName stack name
* @param stackVersion stack version
* @param serviceName service name
*
* @return map of kerberos descriptor data or null if no descriptor exists
*
* @throws IOException if unable to read or parse the kerberos descriptor file
* @throws NoSuchParentResourceException if the parent stack or stack service doesn't exist
*/
private Map<String, Object> getServiceDescriptor(
String stackName, String stackVersion, String serviceName) throws NoSuchParentResourceException, IOException {
AmbariManagementController controller = getManagementController();
ServiceInfo serviceInfo;
try {
serviceInfo = controller.getAmbariMetaInfo().getService(stackName, stackVersion, serviceName);
} catch (StackAccessException e) {
throw new NoSuchParentResourceException(String.format(
"Parent stack/service resource doesn't exist: stackName='%s', stackVersion='%s', serviceName='%s'",
stackName, stackVersion, serviceName));
}
File kerberosFile = serviceInfo.getKerberosDescriptorFile();
if (kerberosFile != null) {
KerberosServiceDescriptor serviceDescriptor =
kerberosServiceDescriptorFactory.createInstance(kerberosFile, serviceName);
if (serviceDescriptor != null) {
return serviceDescriptor.toMap();
}
}
return null;
}
/**
* Get a collection of all service descriptors for the specified stack.
*
* @param stack stack name
*
* @return collection of all service descriptors for the stack; will not return null
*
* @throws IOException if unable to read or parse a descriptor file
*/
private Collection<KerberosServiceDescriptor> getServiceDescriptors(StackInfo stack) throws IOException {
Collection<KerberosServiceDescriptor> serviceDescriptors = new ArrayList<>();
for (ServiceInfo service : stack.getServices()) {
File descriptorFile = service.getKerberosDescriptorFile();
if (descriptorFile != null) {
KerberosServiceDescriptor descriptor =
kerberosServiceDescriptorFactory.createInstance(descriptorFile, service.getName());
if (descriptor != null) {
serviceDescriptors.add(descriptor);
}
}
}
return serviceDescriptors;
}
private static Map<String, Map<String, List<MetricDefinition>>> removeAggregateFunctions(
Map<String, Map<String, List<MetricDefinition>>> serviceMetrics ) {
Map<String, Map<String, List<MetricDefinition>>> filteredServiceMetrics = null;
if (serviceMetrics != null) {
filteredServiceMetrics = new HashMap<>();
// For every Component
for (String component : serviceMetrics.keySet()) {
Map<String, List<MetricDefinition>> componentMetricsCopy = new HashMap<>();
Map<String, List<MetricDefinition>> componentMetrics = serviceMetrics.get(component);
// For every Component / HostComponent category retrieve metrics definitions
for (String category : componentMetrics.keySet()) {
componentMetricsCopy.put(category,
removeAggregateFunctions(componentMetrics.get(category)));
}
filteredServiceMetrics.put(component, componentMetricsCopy);
}
}
return filteredServiceMetrics;
}
private static List<MetricDefinition> removeAggregateFunctions(List<MetricDefinition> metricsDefinitions) {
List<MetricDefinition> filteredComponentMetrics = null;
if (metricsDefinitions != null) {
filteredComponentMetrics = new ArrayList<>();
// For every metric definition
for (MetricDefinition metricDefinition : metricsDefinitions) {
Map<String, Map<String, Metric>> categorizedMetricsCopy = new HashMap<>();
Map<String, Map<String, Metric>> categorizedMetrics = metricDefinition.getMetricsByCategory();
for (String category : categorizedMetrics.keySet()) {
Map<String, Metric> namedMetricsCopy = new HashMap<>();
Map<String, Metric> namedMetrics = categorizedMetrics.get(category);
for (String metricName : namedMetrics.keySet()) {
// Metrics System metrics only
if (!(metricDefinition.getType().equals("ganglia") && PropertyHelper.hasAggregateFunctionSuffix(metricName))) {
namedMetricsCopy.put(metricName, namedMetrics.get(metricName));
}
}
categorizedMetricsCopy.put(category, namedMetricsCopy);
}
filteredComponentMetrics.add(
new MetricDefinition(metricDefinition.getType(), metricDefinition.getProperties(), categorizedMetricsCopy));
}
}
return filteredComponentMetrics;
}
}