/** * * 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.airavata.cloud.aurora.client; import java.util.HashSet; import java.util.Set; import org.apache.airavata.cloud.aurora.client.bean.GetJobsResponseBean; import org.apache.airavata.cloud.aurora.client.bean.JobConfigBean; import org.apache.airavata.cloud.aurora.client.bean.JobDetailsResponseBean; import org.apache.airavata.cloud.aurora.client.bean.JobKeyBean; import org.apache.airavata.cloud.aurora.client.bean.PendingJobReasonBean; import org.apache.airavata.cloud.aurora.client.bean.ResponseBean; import org.apache.airavata.cloud.aurora.client.sdk.AuroraSchedulerManager; import org.apache.airavata.cloud.aurora.client.sdk.JobConfiguration; import org.apache.airavata.cloud.aurora.client.sdk.JobKey; import org.apache.airavata.cloud.aurora.client.sdk.ReadOnlyScheduler; import org.apache.airavata.cloud.aurora.client.sdk.Response; import org.apache.airavata.cloud.aurora.client.sdk.TaskQuery; import org.apache.airavata.cloud.aurora.util.AuroraThriftClientUtil; import org.apache.airavata.cloud.aurora.util.Constants; import org.apache.airavata.cloud.aurora.util.ResponseResultType; import org.apache.airavata.common.utils.ServerSettings; import org.apache.thrift.transport.TTransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The Class AuroraThriftClient. */ public class AuroraThriftClient { /** The Constant logger. */ private final static Logger logger = LoggerFactory.getLogger(AuroraThriftClient.class); /** The read only scheduler client. */ private ReadOnlyScheduler.Client readOnlySchedulerClient = null; /** The aurora scheduler manager client. */ private AuroraSchedulerManager.Client auroraSchedulerManagerClient = null; /** The thrift client. */ private static AuroraThriftClient thriftClient = null; /** * Instantiates a new aurora thrift client. */ private AuroraThriftClient() {} /** * Gets the aurora thrift client. * * @return the aurora thrift client * @throws Exception the exception */ public static AuroraThriftClient getAuroraThriftClient() throws Exception { try { if(thriftClient == null) { synchronized(AuroraThriftClient.class) { if(thriftClient == null) { thriftClient = new AuroraThriftClient(); // construct connection url for scheduler String auroraHosts = ServerSettings.getAuroraSchedulerHosts(); Integer connectTimeout = ServerSettings.getAuroraSchedulerTimeout(); // check reachable scheduler host if(auroraHosts != null && !auroraHosts.trim().isEmpty()) { auroraHosts = auroraHosts.trim(); for(String auroraHost : auroraHosts.split(",")) { // malformed host string, should be of form <host:port> if(auroraHost.split(":").length != 2) { throw new Exception("Scheduler Host String: " + auroraHost + ", is malformed. Should be of form <hostname:port>!"); } // read hostname, port & construct connection-url String hostname = auroraHost.split(":")[0]; String port = auroraHost.split(":")[1]; String connectionUrl = String.format(Constants.AURORA_SCHEDULER_CONNECTION_URL, hostname, port); // verify if connection succeeds if(AuroraThriftClientUtil.isSchedulerHostReachable(connectionUrl, connectTimeout)) { thriftClient.readOnlySchedulerClient = AuroraSchedulerClientFactory.createReadOnlySchedulerClient(connectionUrl, connectTimeout); thriftClient.auroraSchedulerManagerClient = AuroraSchedulerClientFactory.createSchedulerManagerClient(connectionUrl, connectTimeout); break; } } // check if scheduler connection successful if(thriftClient.auroraSchedulerManagerClient == null || thriftClient.readOnlySchedulerClient == null) { throw new Exception("None of the Aurora scheduler hosts : <" + auroraHosts + "> were reachable, hence connection not established!"); } } else { // aurora hosts not defined in the properties file throw new Exception("Aurora hosts not specified in airavata-server.properties file."); } } } } } catch(Exception ex) { logger.error("Couldn't initialize Aurora thrift client", ex); throw ex; } return thriftClient; } /** * Reconnect with aurora scheduler. * * @return true, if successful */ private boolean reconnectWithAuroraScheduler() { boolean connectionSuccess = false; try { // construct connection url for scheduler String auroraHosts = ServerSettings.getAuroraSchedulerHosts(); Integer connectTimeout = ServerSettings.getAuroraSchedulerTimeout(); // check reachable scheduler host if(auroraHosts != null) { for(String auroraHost : auroraHosts.split(",")) { // malformed host string, should be of form <host:port> if(auroraHost.split(":").length != 2) { throw new Exception("Scheduler Host String: " + auroraHost + ", is malformed. Should be of form <hostname:port>!"); } // read hostname, port & construct connection-url String hostname = auroraHost.split(":")[0]; String port = auroraHost.split(":")[1]; String connectionUrl = String.format(Constants.AURORA_SCHEDULER_CONNECTION_URL, hostname, port); // verify if connection succeeds if(AuroraThriftClientUtil.isSchedulerHostReachable(connectionUrl, connectTimeout)) { thriftClient.readOnlySchedulerClient = AuroraSchedulerClientFactory.createReadOnlySchedulerClient(connectionUrl, connectTimeout); thriftClient.auroraSchedulerManagerClient = AuroraSchedulerClientFactory.createSchedulerManagerClient(connectionUrl, connectTimeout); // set connection-success flag connectionSuccess = true; } } } else { // aurora hosts not defined in the properties file throw new Exception("Aurora hosts not specified in airavata-server.properties file."); } } catch(Exception ex) { logger.error(ex.getMessage(), ex); } return connectionSuccess; } /** * Creates the job. * * @param jobConfigBean the job config bean * @return the response bean * @throws Exception the exception */ public ResponseBean createJob(JobConfigBean jobConfigBean) throws Exception { ResponseBean response = null; // try till we get response or scheduler connection not found while(response == null) { try { if(jobConfigBean != null) { JobConfiguration jobConfig = AuroraThriftClientUtil.getAuroraJobConfig(jobConfigBean); Response createJobResponse = this.auroraSchedulerManagerClient.createJob(jobConfig); response = AuroraThriftClientUtil.getResponseBean(createJobResponse, ResponseResultType.CREATE_JOB); } } catch(Exception ex) { if (ex instanceof TTransportException) { // if re-connection success, retry command if (this.reconnectWithAuroraScheduler()) { continue; } } logger.error(ex.getMessage(), ex); throw ex; } } return response; } /** * Kill tasks. * * @param jobKeyBean the job key bean * @param instances the instances * @return the response bean * @throws Exception the exception */ public ResponseBean killTasks(JobKeyBean jobKeyBean, Set<Integer> instances) throws Exception { ResponseBean response = null; // try till we get response or scheduler connection not found while(response == null) { try { if(jobKeyBean != null) { JobKey jobKey = AuroraThriftClientUtil.getAuroraJobKey(jobKeyBean); Response killTaskResponse = this.auroraSchedulerManagerClient.killTasks(jobKey, instances); response = AuroraThriftClientUtil.getResponseBean(killTaskResponse, ResponseResultType.KILL_TASKS); } } catch(Exception ex) { if (ex instanceof TTransportException) { // if re-connection success, retry command if (this.reconnectWithAuroraScheduler()) { continue; } } logger.error(ex.getMessage(), ex); throw ex; } } return response; } /** * Gets the job list. * * @param ownerRole the owner role * @return the job list * @throws Exception the exception */ public GetJobsResponseBean getJobList(String ownerRole) throws Exception { GetJobsResponseBean response = null; // try till we get response or scheduler connection not found while(response == null) { try { Response jobListResponse = this.readOnlySchedulerClient.getJobs(ownerRole); response = (GetJobsResponseBean) AuroraThriftClientUtil.getResponseBean(jobListResponse, ResponseResultType.GET_JOBS); } catch(Exception ex) { if (ex instanceof TTransportException) { // if re-connection success, retry command if (this.reconnectWithAuroraScheduler()) { continue; } } logger.error(ex.getMessage(), ex); throw ex; } } return response; } /** * Gets the pending reason for job. * * @param jobKeyBean the job key bean * @return the pending reason for job * @throws Exception the exception */ public PendingJobReasonBean getPendingReasonForJob(JobKeyBean jobKeyBean) throws Exception { PendingJobReasonBean response = null; // try till we get response or scheduler connection not found while(response == null) { try { JobKey jobKey = AuroraThriftClientUtil.getAuroraJobKey(jobKeyBean); Set<JobKey> jobKeySet = new HashSet<>(); jobKeySet.add(jobKey); TaskQuery query = new TaskQuery(); query.setJobKeys(jobKeySet); Response pendingReasonResponse = this.readOnlySchedulerClient.getPendingReason(query); response = (PendingJobReasonBean) AuroraThriftClientUtil.getResponseBean(pendingReasonResponse, ResponseResultType.GET_PENDING_JOB_REASON); } catch(Exception ex) { if (ex instanceof TTransportException) { // if re-connection success, retry command if (this.reconnectWithAuroraScheduler()) { continue; } } logger.error(ex.getMessage(), ex); throw ex; } } return response; } /** * Gets the job details. * * @param jobKeyBean the job key bean * @return the job details * @throws Exception the exception */ public JobDetailsResponseBean getJobDetails(JobKeyBean jobKeyBean) throws Exception { JobDetailsResponseBean response = null; // try till we get response or scheduler connection not found while(response == null) { try { if(jobKeyBean != null) { JobKey jobKey = AuroraThriftClientUtil.getAuroraJobKey(jobKeyBean); Set<JobKey> jobKeySet = new HashSet<>(); jobKeySet.add(jobKey); TaskQuery query = new TaskQuery(); query.setJobKeys(jobKeySet); Response jobDetailsResponse = this.readOnlySchedulerClient.getTasksStatus(query); response = (JobDetailsResponseBean) AuroraThriftClientUtil.getResponseBean(jobDetailsResponse, ResponseResultType.GET_JOB_DETAILS); } } catch(Exception ex) { if (ex instanceof TTransportException) { // if re-connection success, retry command if (this.reconnectWithAuroraScheduler()) { continue; } } logger.error(ex.getMessage(), ex); throw ex; } } return response; } }