blob: 6a4f04afca8f3d4c916784d30ca7a157c7c1f5db [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.view;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.orm.entities.RemoteAmbariClusterEntity;
import org.apache.ambari.view.AmbariHttpException;
import org.apache.ambari.view.AmbariStreamProvider;
import org.apache.ambari.view.cluster.Cluster;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* View associated Remote cluster implementation.
*/
public class RemoteAmbariCluster implements Cluster {
public static final String AMBARI_OR_CLUSTER_ADMIN = "/api/v1/users/%s?privileges/PrivilegeInfo/permission_name=AMBARI.ADMINISTRATOR|" +
"(privileges/PrivilegeInfo/permission_name=CLUSTER.ADMINISTRATOR&privileges/PrivilegeInfo/cluster_name=%s)";
/**
* Name of the remote Ambari Cluster
*/
private String name;
/**
* StreamProvider for the remote cluster
* Base path will be http://host:port
*/
private AmbariStreamProvider streamProvider;
/**
* Path for the cluster.
* Value will be like : /api/v1/clusters/clusterName
*/
private String clusterPath;
/**
* User for the cluster
*/
private String username;
private final LoadingCache<String, JsonElement> configurationCache = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.SECONDS)
.build(new CacheLoader<String, JsonElement>() {
@Override
public JsonElement load(String url) throws Exception {
return readFromUrlJSON(url);
}
});
/**
* Constructor for Remote Ambari Cluster
*
* @param remoteAmbariClusterEntity
*/
public RemoteAmbariCluster(RemoteAmbariClusterEntity remoteAmbariClusterEntity, Configuration config) throws MalformedURLException {
this.name = getClusterName(remoteAmbariClusterEntity);
this.username = remoteAmbariClusterEntity.getUsername();
URL url = new URL(remoteAmbariClusterEntity.getUrl());
String portString = url.getPort() == -1 ? "" : ":" + url.getPort();
String baseUrl = url.getProtocol() + "://" + url.getHost() + portString;
this.clusterPath = url.getPath();
this.streamProvider = new RemoteAmbariStreamProvider(
baseUrl, remoteAmbariClusterEntity.getUsername(),
remoteAmbariClusterEntity.getPassword(), config.getRequestConnectTimeout(), config.getRequestReadTimeout());
}
private String getClusterName(RemoteAmbariClusterEntity remoteAmbariClusterEntity) {
String[] urlSplit = remoteAmbariClusterEntity.getUrl().split("/");
// remoteAmbariClusterEntity.getName() is not the actual name of Remote Cluster
// We need to extract the name from cluster url which is like. http://host:port/api/vi/clusters/${clusterName}
return urlSplit[urlSplit.length - 1];
}
/**
* Constructor for Remote Ambari Cluster
*
* @param name
* @param streamProvider
*/
public RemoteAmbariCluster(String name, String clusterPath, AmbariStreamProvider streamProvider) {
this.name = name;
this.clusterPath = clusterPath;
this.streamProvider = streamProvider;
}
@Override
public String getName() {
return this.name;
}
@Override
public String getConfigurationValue(String type, String key) {
JsonElement config = null;
try {
String desiredTag = getDesiredConfig(type);
if (desiredTag != null) {
config = configurationCache.get(String.format("%s/configurations?(type=%s&tag=%s)",this.clusterPath, type, desiredTag));
}
} catch (ExecutionException e) {
throw new RemoteAmbariConfigurationReadException("Can't retrieve configuration from Remote Ambari", e);
}
if (config == null || !config.isJsonObject()) return null;
JsonElement items = config.getAsJsonObject().get("items");
if (items == null || !items.isJsonArray()) return null;
JsonElement item = items.getAsJsonArray().get(0);
if (item == null || !item.isJsonObject()) return null;
JsonElement properties = item.getAsJsonObject().get("properties");
if (properties == null || !properties.isJsonObject()) return null;
JsonElement property = properties.getAsJsonObject().get(key);
if (property == null || !property.isJsonPrimitive()) return null;
return property.getAsJsonPrimitive().getAsString();
}
@Override
public List<String> getHostsForServiceComponent(String serviceName, String componentName) {
String url = String.format("%s/services/%s/components/%s?" +
"fields=host_components/HostRoles/host_name", this.clusterPath, serviceName, componentName);
List<String> hosts = new ArrayList<String>();
try {
JsonElement response = configurationCache.get(url);
if (response == null || !response.isJsonObject()) return hosts;
JsonElement hostComponents = response.getAsJsonObject().get("host_components");
if (hostComponents == null || !hostComponents.isJsonArray()) return hosts;
for (JsonElement element : hostComponents.getAsJsonArray()) {
JsonElement hostRoles = element.getAsJsonObject().get("HostRoles");
String hostName = hostRoles.getAsJsonObject().get("host_name").getAsString();
hosts.add(hostName);
}
} catch (ExecutionException e) {
throw new RemoteAmbariConfigurationReadException("Can't retrieve host information from Remote Ambari", e);
}
return hosts;
}
/**
* Get list of services installed on the remote cluster
*
* @return list of services Available on cluster
*/
public Set<String> getServices() throws IOException, AmbariHttpException {
Set<String> services = new HashSet<String>();
String path = this.clusterPath + "?fields=services/ServiceInfo/service_name";
JsonElement config = configurationCache.getUnchecked(path);
if (config != null && config.isJsonObject()) {
JsonElement items = config.getAsJsonObject().get("services");
if (items != null && items.isJsonArray()) {
for (JsonElement item : items.getAsJsonArray()) {
JsonElement serviceInfo = item.getAsJsonObject().get("ServiceInfo");
if (serviceInfo != null && serviceInfo.isJsonObject()) {
String serviceName = serviceInfo.getAsJsonObject().get("service_name").getAsString();
services.add(serviceName);
}
}
}
}
return services;
}
public boolean isAmbariOrClusterAdmin() throws AmbariHttpException {
if (username == null) return false;
String url = String.format(AMBARI_OR_CLUSTER_ADMIN, username, name);
JsonElement response = configurationCache.getUnchecked(url);
if (response != null && response.isJsonObject()) {
JsonElement privileges = response.getAsJsonObject().get("privileges");
if (privileges != null && privileges.isJsonArray()) {
if (privileges.getAsJsonArray().size() > 0) return true;
}
}
return false;
}
/**
* Get the current tag for the config type
*
* @param type
* @return
* @throws ExecutionException
*/
private String getDesiredConfig(String type) throws ExecutionException {
JsonElement desiredConfigResponse = configurationCache.get(this.clusterPath +"?fields=services/ServiceInfo,hosts,Clusters");
if (desiredConfigResponse == null || !desiredConfigResponse.isJsonObject()) return null;
JsonElement clusters = desiredConfigResponse.getAsJsonObject().get("Clusters");
if (clusters == null || !clusters.isJsonObject()) return null;
JsonElement desiredConfig = clusters.getAsJsonObject().get("desired_configs");
if (desiredConfig == null || !desiredConfig.isJsonObject()) return null;
JsonElement desiredConfigForType = desiredConfig.getAsJsonObject().get(type);
if (desiredConfigForType == null || !desiredConfigForType.isJsonObject()) return null;
JsonElement typeJson = desiredConfigForType.getAsJsonObject().get("tag");
if (typeJson == null || !(typeJson.isJsonPrimitive())) return null;
return typeJson.getAsJsonPrimitive().getAsString();
}
/**
* Read the content of the url from remote cluster
*
* @param url
* @return
* @throws IOException
* @throws AmbariHttpException
*/
private JsonElement readFromUrlJSON(String url) throws IOException, AmbariHttpException {
InputStream inputStream = streamProvider.readFrom(url, "GET", (String) null, null);
String response = IOUtils.toString(inputStream);
return new JsonParser().parse(response);
}
}