/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.operation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.SimpleTrigger; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.operation.ResourceOperationHistory; import org.rhq.core.domain.operation.bean.ResourceOperationSchedule; import org.rhq.core.domain.resource.InventoryStatus; import org.rhq.core.domain.resource.Resource; import org.rhq.core.util.exception.ThrowableUtil; import org.rhq.enterprise.server.agentclient.AgentClient; import org.rhq.enterprise.server.auth.SubjectManagerLocal; import org.rhq.enterprise.server.core.AgentManagerLocal; import org.rhq.enterprise.server.resource.ResourceManagerLocal; import org.rhq.enterprise.server.resource.ResourceNotFoundException; import org.rhq.enterprise.server.util.LookupUtil; /** * A job that invokes an operation on a single resource. * * @author John Mazzitelli */ public class ResourceOperationJob extends OperationJob { public static final String DATAMAP_INT_RESOURCE_ID = "resourceId"; private static final Log log = LogFactory.getLog(ResourceOperationJob.class); /** * Prefix for all job names and job groups names of resource operations. */ private static final String RESOURCE_JOB_NAME_PREFIX = "rhq-resource-"; public static String createUniqueJobName(Resource resource, String operationName) { return RESOURCE_JOB_NAME_PREFIX + resource.getId() + "-" + operationName.hashCode() + "-" + System.currentTimeMillis(); } public static String createJobGroupName(Resource resource) { return RESOURCE_JOB_NAME_PREFIX + resource.getId(); } /** * This is invoked every time the operation needs to be invoked. Therefore, for each call to this method, a new * history item will be created. * * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) */ public void execute(JobExecutionContext context) throws JobExecutionException { ResourceOperationSchedule schedule = null; Subject loggedInSubject = null; try { JobDetail jobDetail = context.getJobDetail(); int triggerTimes = 1; if (context.getTrigger() instanceof SimpleTrigger) { SimpleTrigger trigger = (SimpleTrigger) context.getTrigger(); triggerTimes = trigger.getTimesTriggered(); } else { // Cron Trigger } OperationManagerLocal operationManager = LookupUtil.getOperationManager(); updateOperationScheduleEntity(jobDetail, context.getNextFireTime(), operationManager); // retrieve the stored schedule using the overlord so it succeeds no matter what schedule = operationManager.getResourceOperationSchedule(getOverlord(), jobDetail); if (schedule == null) { throw new CancelJobException("Resource Schedule no longer exists, canceling job"); } // Login the schedule's subject so its assigned a session, so our security tests pass. // Create a new session even if user is logged in elsewhere, we don't want to attach to that user's session loggedInSubject = getUserWithSession(schedule.getSubject(), false); schedule.setSubject(loggedInSubject); // for the security check, can the user who scheduled the operation in the first // place still have the authority to execute it against the resource in question operationManager.getResourceOperationSchedule(schedule.getSubject(), jobDetail); ResourceOperationHistory resourceHistory = null; if (triggerTimes==1) { // On 1st fire use the already provided history. resourceHistory = findOperationHistory(jobDetail.getName(),jobDetail.getGroup(),operationManager, schedule); if (resourceHistory.getStartedTime()>0) { resourceHistory=null; } } if (resourceHistory==null) { resourceHistory = createOperationHistory(jobDetail.getName(), jobDetail.getGroup(), schedule, null, operationManager); } invokeOperationOnResource(schedule, resourceHistory, operationManager); } catch (Exception e) { if (e instanceof CancelJobException) { // if a cancel job exception was thrown we do not need to do anything else. // we can just rethrow the exception. throw (CancelJobException) e; } String error = "Failed to execute scheduled operation [" + schedule + "]"; log.error(error, e); if (isResourceUncommitted(context.getJobDetail())) { int resourceId = getResourceId(context.getJobDetail()); String msg = "The resource with id " + resourceId + " is not committed in inventory. It may have " + "been deleted from inventory. Canceling job."; log.warn(msg); throw new CancelJobException(msg, e); } throw new JobExecutionException(error, e, false); } finally { // clean up our temporary session by logging out of it try { if (loggedInSubject != null) { SubjectManagerLocal subjectMgr = LookupUtil.getSubjectManager(); subjectMgr.logout(loggedInSubject); } } catch (Exception e) { log.debug("Failed to log out of temporary resource operation session - will be cleaned up during session purge later: " + ThrowableUtil.getAllMessages(e)); } } } private Subject getOverlord() { SubjectManagerLocal subjectMgr = LookupUtil.getSubjectManager(); return subjectMgr.getOverlord(); } private int getResourceId(JobDetail jobDetail) { JobDataMap jobDataMap = jobDetail.getJobDataMap(); return jobDataMap.getIntFromString(DATAMAP_INT_RESOURCE_ID); } private boolean isResourceUncommitted(JobDetail jobDetail) { ResourceManagerLocal resourceMgr = LookupUtil.getResourceManager(); int resourceId = getResourceId(jobDetail); try { Resource resource = resourceMgr.getResource(getOverlord(), resourceId); return isResourceUncommitted(resource); } catch (ResourceNotFoundException e) { return true; } } private boolean isResourceUncommitted(Resource resource) { return resource == null || resource.getInventoryStatus() != InventoryStatus.COMMITTED; } /** * Actually invokes the operation by sending the command to the agent. This is package-scoped so the group job can * call this for each member resource in the group. If groupHistory is not <code>null</code>, it means this resource * job is being executed as part of a group execution. * * @param schedule * @param resourceHistory * @param operationManager * * @return the history item that you can use to track progress * * @throws Exception */ void invokeOperationOnResource(ResourceOperationSchedule schedule, ResourceOperationHistory resourceHistory, OperationManagerLocal operationManager) throws Exception { // make sure we have a valid session Subject s = getUserWithSession(schedule.getSubject(), true); schedule.setSubject(s); resourceHistory.setStartedTime(); resourceHistory = (ResourceOperationHistory) operationManager.updateOperationHistory(s, resourceHistory); // now tell the agent to invoke it! try { Resource resource = schedule.getResource(); if (isResourceUncommitted(resource)) { String msg = "The resource with id " + resource.getId() + " is not committed in inventory. It may " + "have been deleted from inventory. Canceling job."; log.warn(msg); throw new CancelJobException(msg); } AgentManagerLocal agentManager = LookupUtil.getAgentManager(); AgentClient agentClient = agentManager.getAgentClient(getOverlord(), resource.getId()); agentClient.getOperationAgentService().invokeOperation(resourceHistory.getJobId().toString(), resource.getId(), schedule.getOperationName(), schedule.getParameters()); } catch (Exception e) { // failed to even send to the agent, immediately mark the job as failed resourceHistory.setErrorMessage(ThrowableUtil.getStackAsString(e)); operationManager.updateOperationHistory(s, resourceHistory); operationManager.checkForCompletedGroupOperation(resourceHistory.getId()); throw e; } return; } }