| /** |
| * 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); |
| } |
| |
| } |