blob: 8972ca220fc914e178cae4b54c921a79f8441b24 [file] [log] [blame]
/**
* 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.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.configuration.ComponentSSLConfiguration;
import org.apache.ambari.server.controller.jmx.JMXHostProvider;
import org.apache.ambari.server.controller.jmx.JMXPropertyProvider;
import org.apache.ambari.server.controller.metrics.MetricHostProvider;
import org.apache.ambari.server.controller.metrics.MetricPropertyProviderFactory;
import org.apache.ambari.server.controller.metrics.MetricsPropertyProvider;
import org.apache.ambari.server.controller.metrics.MetricsServiceProvider;
import org.apache.ambari.server.controller.metrics.RestMetricsPropertyProvider;
import org.apache.ambari.server.controller.metrics.timeline.cache.TimelineMetricCacheProvider;
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.StreamProvider;
import org.apache.ambari.server.security.authorization.AuthorizationException;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.StackId;
import org.apache.ambari.server.state.stack.Metric;
import org.apache.ambari.server.state.stack.MetricDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.google.inject.Injector;
/**
* This class analyzes a service's metrics to determine if additional
* metrics should be fetched. It's okay to maintain state here since these
* are done per-request.
*/
public class StackDefinedPropertyProvider implements PropertyProvider {
private static final Logger LOG = LoggerFactory.getLogger(StackDefinedPropertyProvider.class);
@Inject
private static Clusters clusters = null;
@Inject
private static AmbariMetaInfo metaInfo = null;
@Inject
private static Injector injector = null;
/**
* A factory used to retrieve Guice-injected instances of a metric
* {@link PropertyProvider}.
*/
@Inject
private static MetricPropertyProviderFactory metricPropertyProviderFactory;
private Resource.Type type = null;
private String clusterNamePropertyId = null;
private String hostNamePropertyId = null;
private String componentNamePropertyId = null;
private String resourceStatePropertyId = null;
private ComponentSSLConfiguration sslConfig = null;
private URLStreamProvider streamProvider = null;
private JMXHostProvider jmxHostProvider;
private PropertyProvider defaultJmx = null;
private PropertyProvider defaultGanglia = null;
private final MetricHostProvider metricHostProvider;
private final MetricsServiceProvider metricsServiceProvider;
private TimelineMetricCacheProvider cacheProvider;
/**
* PropertyHelper/AbstractPropertyProvider expect map of maps,
* that's why we wrap metrics into map
*/
public static final String WRAPPED_METRICS_KEY = "WRAPPED_METRICS_KEY";
@Inject
public static void init(Injector injector) {
clusters = injector.getInstance(Clusters.class);
metaInfo = injector.getInstance(AmbariMetaInfo.class);
metricPropertyProviderFactory = injector.getInstance(MetricPropertyProviderFactory.class);
StackDefinedPropertyProvider.injector = injector;
}
public StackDefinedPropertyProvider(Resource.Type type,
JMXHostProvider jmxHostProvider,
MetricHostProvider metricHostProvider,
MetricsServiceProvider serviceProvider,
URLStreamProvider streamProvider,
String clusterPropertyId,
String hostPropertyId,
String componentPropertyId,
String resourceStatePropertyId,
PropertyProvider defaultJmxPropertyProvider,
PropertyProvider defaultGangliaPropertyProvider) {
this.metricHostProvider = metricHostProvider;
metricsServiceProvider = serviceProvider;
if (null == clusterPropertyId) {
throw new NullPointerException("Cluster name property id cannot be null");
}
if (null == componentPropertyId) {
throw new NullPointerException("Component name property id cannot be null");
}
this.type = type;
clusterNamePropertyId = clusterPropertyId;
hostNamePropertyId = hostPropertyId;
componentNamePropertyId = componentPropertyId;
this.resourceStatePropertyId = resourceStatePropertyId;
this.jmxHostProvider = jmxHostProvider;
sslConfig = ComponentSSLConfiguration.instance();
this.streamProvider = streamProvider;
defaultJmx = defaultJmxPropertyProvider;
defaultGanglia = defaultGangliaPropertyProvider;
cacheProvider = injector.getInstance(TimelineMetricCacheProvider.class);
}
@Override
public Set<Resource> populateResources(Set<Resource> resources,
Request request, Predicate predicate) throws SystemException {
// only arrange for one instance of Ganglia and JMX instantiation
Map<String, Map<String, PropertyInfo>> gangliaMap = new HashMap<>();
Map<String, Map<String, PropertyInfo>> jmxMap = new HashMap<>();
List<PropertyProvider> additional = new ArrayList<>();
try {
for (Resource r : resources) {
String clusterName = r.getPropertyValue(clusterNamePropertyId).toString();
String componentName = r.getPropertyValue(componentNamePropertyId).toString();
Cluster cluster = clusters.getCluster(clusterName);
StackId stack = cluster.getDesiredStackVersion();
String svc = metaInfo.getComponentToService(stack.getStackName(),
stack.getStackVersion(), componentName);
List<MetricDefinition> defs = metaInfo.getMetrics(
stack.getStackName(), stack.getStackVersion(), svc, componentName, type.name());
if (null == defs || 0 == defs.size()) {
continue;
}
for (MetricDefinition m : defs) {
if (m.getType().equals("ganglia")) {
gangliaMap.put(componentName, getPropertyInfo(m));
} else if (m.getType().equals("jmx")) {
jmxMap.put(componentName, getPropertyInfo(m));
} else {
PropertyProvider pp = getDelegate(m,
streamProvider, metricHostProvider,
clusterNamePropertyId, hostNamePropertyId,
componentNamePropertyId, resourceStatePropertyId,
componentName);
if (pp == null) {
pp = getDelegate(m);
}
if (pp != null) {
additional.add(pp);
}
}
}
}
if (gangliaMap.size() > 0) {
PropertyProvider propertyProvider =
MetricsPropertyProvider.createInstance(type, gangliaMap,
streamProvider, sslConfig,
cacheProvider,
metricHostProvider,
metricsServiceProvider, clusterNamePropertyId,
hostNamePropertyId, componentNamePropertyId);
propertyProvider.populateResources(resources, request, predicate);
} else {
defaultGanglia.populateResources(resources, request, predicate);
}
if (jmxMap.size() > 0) {
JMXPropertyProvider jpp = metricPropertyProviderFactory.createJMXPropertyProvider(jmxMap,
streamProvider,
jmxHostProvider, metricHostProvider,
clusterNamePropertyId, hostNamePropertyId,
componentNamePropertyId, resourceStatePropertyId);
jpp.populateResources(resources, request, predicate);
} else {
defaultJmx.populateResources(resources, request, predicate);
}
for (PropertyProvider pp : additional) {
pp.populateResources(resources, request, predicate);
}
} catch (AuthorizationException e) {
// Need to rethrow the catched 'AuthorizationException'.
throw e;
} catch (Exception e) {
LOG.error("Error loading deferred resources", e);
throw new SystemException("Error loading deferred resources", e);
}
return resources;
}
@Override
public Set<String> checkPropertyIds(Set<String> propertyIds) {
return Collections.emptySet();
}
/**
* @param def the metric definition
* @return the converted Map required for JMX or Ganglia execution.
* Format: <metric name, property info>
*/
public static Map<String, PropertyInfo> getPropertyInfo(MetricDefinition def) {
Map<String, PropertyInfo> defs = new HashMap<>();
for (Entry<String, Metric> entry : def.getMetrics().entrySet()) {
Metric metric = entry.getValue();
if (metric.getName() != null) {
PropertyInfo propertyInfo = new PropertyInfo(metric.getName(),
metric.isTemporal(), metric.isPointInTime());
propertyInfo.setAmsHostMetric(metric.isAmsHostMetric());
propertyInfo.setUnit(metric.getUnit());
defs.put(entry.getKey(), propertyInfo);
}
}
return defs;
}
/**
* @param definition metric definition for a component and resource type combination
* @return the custom property provider
*/
private PropertyProvider getDelegate(MetricDefinition definition) {
try {
Class<?> clz = Class.forName(definition.getType());
// singleton/factory
try {
Method m = clz.getMethod("getInstance", Map.class, Map.class);
Object o = m.invoke(null, definition.getProperties(), definition.getMetrics());
return PropertyProvider.class.cast(o);
} catch (Exception e) {
LOG.info("Could not load singleton or factory method for type '" +
definition.getType());
}
// try maps constructor
try {
Constructor<?> ct = clz.getConstructor(Map.class, Map.class);
Object o = ct.newInstance(definition.getProperties(), definition.getMetrics());
return PropertyProvider.class.cast(o);
} catch (Exception e) {
LOG.info("Could not find contructor for type '" +
definition.getType());
}
// just new instance
return PropertyProvider.class.cast(clz.newInstance());
} catch (Exception e) {
LOG.error("Could not load class " + definition.getType());
return null;
}
}
/**
*
* @param definition the metric definition for a component
* @param streamProvider the stream provider
* @param metricsHostProvider the metrics host provider
* @param clusterNamePropertyId the cluster name property id
* @param hostNamePropertyId the host name property id
* @param componentNamePropertyId the component name property id
* @param statePropertyId the state property id
* @return the custom property provider
*/
private PropertyProvider getDelegate(MetricDefinition definition,
StreamProvider streamProvider,
MetricHostProvider metricsHostProvider,
String clusterNamePropertyId,
String hostNamePropertyId,
String componentNamePropertyId,
String statePropertyId,
String componentName) {
Map<String, PropertyInfo> metrics = getPropertyInfo(definition);
HashMap<String, Map<String, PropertyInfo>> componentMetrics =
new HashMap<>();
componentMetrics.put(WRAPPED_METRICS_KEY, metrics);
try {
Class<?> clz = Class.forName(definition.getType());
// use a Factory for the REST provider
if (clz.equals(RestMetricsPropertyProvider.class)) {
return metricPropertyProviderFactory.createRESTMetricsPropertyProvider(
definition.getProperties(), componentMetrics, streamProvider, metricsHostProvider,
clusterNamePropertyId, hostNamePropertyId, componentNamePropertyId, statePropertyId,
componentName);
}
try {
/*
* Warning: this branch is already used, that's why please adjust
* all implementations when modifying constructor interface
*/
Constructor<?> ct = clz.getConstructor(Map.class,
Map.class, StreamProvider.class, MetricHostProvider.class,
String.class, String.class, String.class, String.class, String.class);
Object o = ct.newInstance(
injector,
definition.getProperties(), componentMetrics,
streamProvider, metricsHostProvider,
clusterNamePropertyId, hostNamePropertyId,
componentNamePropertyId, statePropertyId, componentName);
return PropertyProvider.class.cast(o);
} catch (Exception e) {
LOG.info("Could not find contructor for type '" +
definition.getType());
}
// just new instance
return PropertyProvider.class.cast(clz.newInstance());
} catch (Exception e) {
LOG.error("Could not load class " + definition.getType());
return null;
}
}
}