/** * 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.actionmanager; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.agent.CommandReport; import org.apache.ambari.server.controller.ExecuteActionRequest; import org.apache.ambari.server.topology.TopologyManager; import org.apache.ambari.server.utils.CommandUtils; import org.apache.ambari.server.utils.StageUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; import com.google.inject.Singleton; /** * This class acts as the interface for action manager with other components. */ @Singleton public class ActionManager { private static Logger LOG = LoggerFactory.getLogger(ActionManager.class); private final ActionScheduler scheduler; private final ActionDBAccessor db; private final AtomicLong requestCounter; private final RequestFactory requestFactory; private static TopologyManager topologyManager; /** * Guice-injected Constructor. * * @param db * @param requestFactory * @param scheduler */ @Inject public ActionManager(ActionDBAccessor db, RequestFactory requestFactory, ActionScheduler scheduler) { this.db = db; this.requestFactory = requestFactory; this.scheduler = scheduler; requestCounter = new AtomicLong(db.getLastPersistedRequestIdWhenInitialized()); } public void start() { LOG.info("Starting scheduler thread"); scheduler.start(); } public void shutdown() { scheduler.stop(); } public void sendActions(List<Stage> stages, String clusterHostInfo, ExecuteActionRequest actionRequest) throws AmbariException { Request request = requestFactory.createNewFromStages(stages, clusterHostInfo, actionRequest); sendActions(request, actionRequest); } public void sendActions(Request request, ExecuteActionRequest executeActionRequest) throws AmbariException { if (LOG.isDebugEnabled()) { LOG.debug(String.format("Persisting Request into DB: %s", request)); if (executeActionRequest != null) { LOG.debug("In response to request: " + request.toString()); } } db.persistActions(request); scheduler.awake(); } public List<Request> getRequests(Collection<Long> requestIds) { List<Request> requests = db.getRequests(requestIds); requests.addAll(topologyManager.getRequests(requestIds)); return requests; } public List<Stage> getRequestStatus(long requestId) { return db.getAllStages(requestId); } public Stage getAction(long requestId, long stageId) { return db.getStage(StageUtils.getActionId(requestId, stageId)); } /** * Get all actions(stages) for a request. * * @param requestId the request id * @return list of all stages associated with the given request id */ public List<Stage> getActions(long requestId) { return db.getAllStages(requestId); } public HostRoleCommand getTaskById(long taskId) { return db.getTask(taskId); } /** * Persists command reports into the db * @param reports command reports * @param commands a list of commands that correspond to reports list (it should be * a 1 to 1 matching). We use this list to avoid fetching commands from the DB * twice */ public void processTaskResponse(String hostname, List<CommandReport> reports, Map<Long, HostRoleCommand> commands) { if (reports == null) { return; } Collections.sort(reports, new Comparator<CommandReport>() { @Override public int compare(CommandReport o1, CommandReport o2) { return (int) (o1.getTaskId()-o2.getTaskId()); } }); List<CommandReport> reportsToProcess = new ArrayList<>(); //persist the action response into the db. for (CommandReport report : reports) { HostRoleCommand command = commands.get(report.getTaskId()); if (LOG.isDebugEnabled()) { LOG.debug("Processing command report : " + report.toString()); } if (command == null) { LOG.warn("The task " + report.getTaskId() + " is invalid"); continue; } if (! command.getStatus().equals(HostRoleStatus.IN_PROGRESS) && ! command.getStatus().equals(HostRoleStatus.QUEUED) && ! command.getStatus().equals(HostRoleStatus.ABORTED)) { LOG.warn("The task " + command.getTaskId() + " is not in progress, ignoring update"); continue; } reportsToProcess.add(report); } db.updateHostRoleStates(reportsToProcess); } /** * Find if the command report is for an in progress command * @param report * @return */ public boolean isInProgressCommand(CommandReport report) { HostRoleCommand command = db.getTask(report.getTaskId()); if (command == null) { LOG.warn("The task " + report.getTaskId() + " is invalid"); return false; } return command.getStatus().equals(HostRoleStatus.IN_PROGRESS) || command.getStatus().equals(HostRoleStatus.QUEUED); } public void handleLostHost(String host) { //Do nothing, the task will timeout anyway. //The actions can be failed faster as an optimization //if action timeout happens to be much larger than //heartbeat timeout. } public long getNextRequestId() { return requestCounter.incrementAndGet(); } public List<HostRoleCommand> getRequestTasks(long requestId) { return db.getRequestTasks(requestId); } public List<HostRoleCommand> getAllTasksByRequestIds(Collection<Long> requestIds) { return db.getAllTasksByRequestIds(requestIds); } public Collection<HostRoleCommand> getTasks(Collection<Long> taskIds) { return db.getTasks(taskIds); } public Map<Long, HostRoleCommand> getTasksMap(Collection<Long> taskIds) { return CommandUtils.convertToTaskIdCommandMap(getTasks(taskIds)); } /** * Get first or last maxResults requests that are in the specified status * * @param status * Desired request status * @param maxResults * maximal number of returned id's * @param ascOrder * defines sorting order for database query result * @return First or last maxResults request id's if ascOrder is true or false, * respectively */ public List<Long> getRequestsByStatus(RequestStatus status, int maxResults, boolean ascOrder) { List<Long> requests = db.getRequestsByStatus(status, maxResults, ascOrder); for (Request logicalRequest : topologyManager.getRequests(Collections.<Long>emptySet())) { //todo: Request.getStatus() returns HostRoleStatus and we are comparing to RequestStatus //todo: for now just compare the names as RequestStatus names are a subset of HostRoleStatus names HostRoleStatus logicalRequestStatus = logicalRequest.getStatus(); if (status == null || (logicalRequestStatus != null && logicalRequest.getStatus().name().equals(status.name()))) { requests.add(logicalRequest.getRequestId()); } } return requests; } public Map<Long, String> getRequestContext(List<Long> requestIds) { return db.getRequestContext(requestIds); } public String getRequestContext(long requestId) { return db.getRequestContext(requestId); } public void cancelRequest(long requestId, String reason) { scheduler.scheduleCancellingRequest(requestId, reason); scheduler.awake(); } //todo: proper static injection public static void setTopologyManager(TopologyManager topologyManager) { ActionManager.topologyManager = topologyManager; } public void resubmitTasks(List<Long> taskIds) { db.resubmitTasks(taskIds); } }