Skip to content

Commit

Permalink
Update event logging to record node success rate instead of name when…
Browse files Browse the repository at this point in the history
… multiple nodes are targetted, and re-enable node column view
  • Loading branch information
gschueler committed Dec 16, 2010
1 parent ca749a1 commit 6451ebb
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 32 deletions.
12 changes: 11 additions & 1 deletion rundeckapp/grails-app/i18n/messages.properties
Expand Up @@ -166,6 +166,7 @@ jobquery.title.maprefUriFilter=Resource URI
jobquery.title.reportIdFilter=Report ID
jobquery.title.tagsFilter=Tags
jobquery.title.nodeFilter=Node
jobquery.title.nodeFilter.plural=Nodes
jobquery.title.messageFilter=Message
jobquery.title.reportKindFilter=Report Type
jobquery.title.recentFilter=Within
Expand Down Expand Up @@ -224,4 +225,13 @@ resource.metadata.entity.deployment-basedir=Basedir
resource.metadata.entity.deployment-startup-rank=Startup Rank
resource.metadata.entity.tags=Tags

user.authorizations.title=Authorizations
user.authorizations.title=Authorizations

# nodes summary text, when some nodes failed: {0} is success count, {1} is failed count, {2} is total count
event.nodes.failed.summary={0}/{1}
# nodes summary text, when no nodes failed: {0} is success count, {1} is failed count, {2} is total count
event.nodes.success.summary={0}/{1}
# nodes summary text, only a single node: {0} is total node count, {1} is name of single node
event.nodes.success.single.summary={1}
# nodes summary text, empty nodeset
event.nodes.empty.summary=(none)
31 changes: 20 additions & 11 deletions rundeckapp/grails-app/jobs/ExecutionJob.groovy
Expand Up @@ -9,6 +9,7 @@ import org.quartz.InterruptableJob
import com.dtolabs.rundeck.core.execution.ExecutionServiceThread
import com.dtolabs.rundeck.core.NodesetFailureException
import org.apache.tools.ant.BuildException
import com.dtolabs.rundeck.execution.NodeRecorder

class ExecutionJob implements InterruptableJob {

Expand Down Expand Up @@ -50,7 +51,7 @@ class ExecutionJob implements InterruptableJob {
}
try{
saveState(initMap.executionService, initMap.execution ? initMap.execution : (Execution) null, success,
_interrupted, initMap.isTemp, initMap.scheduledExecutionId ? initMap.scheduledExecutionId : -1L,extractFailedNodes(result?.thread))
_interrupted, initMap.isTemp, initMap.scheduledExecutionId ? initMap.scheduledExecutionId : -1L,result?.execmap)
}catch(Throwable t){
log.error("Unable to save Job execution state: ${t.message?t.message:'no message'}",t)
}
Expand All @@ -61,9 +62,14 @@ class ExecutionJob implements InterruptableJob {
* an {@link ExecutionServiceThread}.
* @return the list of failed node names, or null
*/
private static Collection<String> extractFailedNodes(Thread thread) {
if (null != thread && thread instanceof ExecutionServiceThread) {
Exception e = ((ExecutionServiceThread) thread).getException()
private static Collection<String> extractFailedNodes(Map execmap=null) {
if(null==execmap){
return null;
}
if(null!=execmap.noderecorder && execmap.noderecorder instanceof NodeRecorder){
return ((NodeRecorder)execmap.noderecorder).getFailedNodes()
} else if (null != execmap.thread && execmap.thread instanceof ExecutionServiceThread) {
Exception e = ((ExecutionServiceThread) execmap.thread).getException()
if (null != e && e instanceof NodesetFailureException) {
return ((NodesetFailureException) e).getNodeset();
} else if (null != e && e instanceof BuildException) {
Expand Down Expand Up @@ -176,16 +182,17 @@ class ExecutionJob implements InterruptableJob {


try {
success = executionService.executeAsyncFinish(execmap.thread,execmap.loghandler)
success = executionService.executeAsyncFinish(execmap)
} catch (Exception exc) {
throw new RuntimeException("Execution failed: "+exc.getMessage(), exc)
}
log.info("ExecutionJob: execution successful? " + success +", interrupted? "+_interrupted)
return [success:success,thread:execmap.thread]
return [success:success,execmap:execmap]

}

def saveState(ExecutionService executionService,Execution execution, boolean success, boolean _interrupted, boolean isTemp, long scheduledExecutionId=-1,Collection<String> failedNodes=null) {
def saveState(ExecutionService executionService,Execution execution, boolean success, boolean _interrupted, boolean isTemp, long scheduledExecutionId=-1, Map execmap=null) {
Collection<String> failedNodes=extractFailedNodes(execmap)
if(isTemp){
executionService.saveExecutionState(
null,
Expand All @@ -194,8 +201,9 @@ class ExecutionJob implements InterruptableJob {
status:String.valueOf(success),
dateCompleted:new Date(),
cancelled:_interrupted,
failedNodes:failedNodes
]
failedNodes:failedNodes,
],
execmap
)

}else{
Expand All @@ -206,8 +214,9 @@ class ExecutionJob implements InterruptableJob {
status:String.valueOf(success),
dateCompleted:new Date(),
cancelled:_interrupted,
failedNodes:failedNodes
]
failedNodes:failedNodes,
],
execmap
)

}
Expand Down
99 changes: 86 additions & 13 deletions rundeckapp/grails-app/services/ExecutionService.groovy
Expand Up @@ -37,6 +37,11 @@ import org.apache.tools.ant.BuildEvent
import com.dtolabs.rundeck.execution.JobExecutionItem
import com.dtolabs.rundeck.core.execution.BaseExecutionResult
import com.dtolabs.rundeck.core.execution.ExecutionServiceThread
import com.dtolabs.rundeck.execution.NodeRecorder
import org.springframework.context.MessageSource
import javax.servlet.http.HttpSession
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.servlet.support.RequestContextUtils as RCU;

/**
* Coordinates Command executions via Ant Project objects
Expand Down Expand Up @@ -396,23 +401,26 @@ class ExecutionService implements ApplicationContextAware, Executor{
}


public static synchronized logExecution(uri,project,user,issuccess,framework,execId,Date startDate=null, jobExecId=null, jobSummary=null,iscancelled=false){
public static synchronized logExecution(uri,project,user,issuccess,framework,execId,Date startDate=null, jobExecId=null, jobSummary=null,iscancelled=false, nodesummary=null){

def internalLog = org.apache.log4j.Logger.getLogger("ExecutionService")
if(null==project || null==user ){
//invalid
internalLog.error("could not send execution report: some required values were null: (project:${project},user:${user})")
return
}
String msg=""

if(execId){
org.apache.log4j.MDC.put('rundeckExecId',execId)
msg+="["+execId+"] "
}
if(startDate){
org.apache.log4j.MDC.put('epochDateStarted',startDate.getTime().toString())
}
if(jobExecId){
org.apache.log4j.MDC.put('rundeckJobId',jobExecId)
msg+="["+jobExecId+"] "
}
if(jobSummary){
org.apache.log4j.MDC.put('rundeckJobName',jobSummary)
Expand All @@ -424,21 +432,15 @@ class ExecutionService implements ApplicationContextAware, Executor{
org.apache.log4j.MDC.put(LogConstants.MDC_MAPREF_KEY,uri)
}
org.apache.log4j.MDC.put(LogConstants.MDC_AUTHOR_KEY,user)
// org.apache.log4j.MDC.put(LogConstants.MDC_ENT_NAME_KEY,null!=name?name:'')
// org.apache.log4j.MDC.put(LogConstants.MDC_ENT_TYPE_KEY,null!=type?type:'')
// org.apache.log4j.MDC.put(LogConstants.MDC_CMD_NAME_KEY,null!=command?command:'')
org.apache.log4j.MDC.put(LogConstants.MDC_ACTION_KEY, jobSummary?jobSummary:"rundeck Job Execution")
org.apache.log4j.MDC.put(LogConstants.MDC_ACTION_TYPE_KEY, issuccess ? LogConstants.ActionType.SUCCEED.toString():iscancelled?LogConstants.ActionType.CANCEL.toString():LogConstants.ActionType.FAIL.toString())
org.apache.log4j.MDC.put(LogConstants.MDC_NODENAME_KEY, framework.getFrameworkNodeName())
org.apache.log4j.MDC.put(LogConstants.MDC_NODENAME_KEY, null!=nodesummary?nodesummary: framework.getFrameworkNodeName())
logger.info(issuccess?'Job completed successfully':iscancelled?'Job killed':'Job failed')

org.apache.log4j.MDC.remove(LogConstants.MDC_ITEM_TYPE_KEY)
org.apache.log4j.MDC.remove(LogConstants.MDC_PROJECT_KEY)
org.apache.log4j.MDC.remove(LogConstants.MDC_MAPREF_KEY)
org.apache.log4j.MDC.remove(LogConstants.MDC_AUTHOR_KEY)
// org.apache.log4j.MDC.remove(LogConstants.MDC_ENT_NAME_KEY)
// org.apache.log4j.MDC.remove(LogConstants.MDC_ENT_TYPE_KEY)
// org.apache.log4j.MDC.remove(LogConstants.MDC_CMD_NAME_KEY)
org.apache.log4j.MDC.remove(LogConstants.MDC_CONTROLLER_KEY)
org.apache.log4j.MDC.remove(LogConstants.MDC_ACTION_KEY)
org.apache.log4j.MDC.remove(LogConstants.MDC_ACTION_TYPE_KEY)
Expand Down Expand Up @@ -485,15 +487,17 @@ class ExecutionService implements ApplicationContextAware, Executor{

ExecutionItem item = createExecutionItemForExecutionContext(execution, framework, execution.user,jobcontext)

NodeRecorder recorder = new NodeRecorder();//TODO: use workflow-aware listener for nodes

//create listener to handle log messages and Ant build events
ExecutionListener executionListener = new CLIExecutionListener(loghandler, null, loghandler);
ExecutionListener executionListener = new CLIExecutionListener(loghandler, recorder, loghandler);

//create service object for the framework and listener
com.dtolabs.rundeck.core.execution.ExecutionService service = ExecutionServiceFactory.instance().createExecutionService(framework, executionListener);

ExecutionServiceThread thread = new ExecutionServiceThread(service,item)
thread.start()
return [thread:thread, loghandler:loghandler]
return [thread:thread, loghandler:loghandler, noderecorder:recorder]
}catch(Exception e){
loghandler.publish(new LogRecord(Level.SEVERE, 'Failed to start execution: ' + e.getClass().getName() + ": " + e.message))
sysThreadBoundOut.removeThreadStream()
Expand Down Expand Up @@ -634,7 +638,9 @@ class ExecutionService implements ApplicationContextAware, Executor{
* @param framework the framework
* @execMap map contains 'thread' and 'loghandler' keys, for Thread and LogHandler objects
*/
def boolean executeAsyncFinish(ExecutionServiceThread thread, LogHandler loghandler){
def boolean executeAsyncFinish(Map execMap){
def ExecutionServiceThread thread=execMap.thread
def LogHandler loghandler=execMap.loghandler
if(!thread.isSuccessful() && thread.getException()){
Exception exc = thread.getException()
def errmsgs = []
Expand Down Expand Up @@ -999,7 +1005,7 @@ class ExecutionService implements ApplicationContextAware, Executor{
return new HtTableLogger(namespace, new File(filepath), level,dometadatalogging,defaultData)
}

def saveExecutionState( schedId, exId, Map props){
def saveExecutionState( schedId, exId, Map props, Map execmap){
def ScheduledExecution scheduledExecution
def Execution execution = Execution.get(exId)
execution.properties=props
Expand Down Expand Up @@ -1039,8 +1045,28 @@ class ExecutionService implements ApplicationContextAware, Executor{
}
}
if(execSaved) {
//summarize node success
String node=null
if (execmap.noderecorder && execmap.noderecorder instanceof NodeRecorder) {
NodeRecorder rec = (NodeRecorder) execmap.noderecorder
final HashSet<String> success = rec.getSuccessfulNodes()
final HashSet<String> failed = rec.getFailedNodes()
final HashSet<String> matched = rec.getMatchedNodes()
if(failed.size()>0){
node = lookupMessage("event.nodes.failed.summary",[success.size(),failed.size(),matched.size()] as Object[])
}else if(success.size()>1){
node = lookupMessage("event.nodes.success.summary",[success.size(),failed.size(),matched.size()] as Object[])
}else if(success.size()>0){
node = lookupMessage("event.nodes.success.single.summary",[matched.size(),success.iterator().next()] as Object[])
}else{
node = lookupMessage("event.nodes.empty.summary",null)
if(null==node){
node="(none)"
}
}
}
def Framework fw = frameworkService.getFramework()
logExecution(null, execution.project, execution.user, "true" == execution.status, fw, exId, execution.dateStarted, jobid, summarizeJob(scheduledExecution,execution), props.cancelled)
logExecution(null, execution.project, execution.user, "true" == execution.status, fw, exId, execution.dateStarted, jobid, summarizeJob(scheduledExecution, execution), props.cancelled, node)
notificationService.triggerJobNotification(props.status == 'true' ? 'success' : 'failure', schedId, [execution: execution])
}
}
Expand Down Expand Up @@ -1264,6 +1290,53 @@ class ExecutionService implements ApplicationContextAware, Executor{
throw new ExecutionException("Unsupported item type: " + executionItem.getClass().getName());
}
}


///////////////
//for loading i18n messages
//////////////

/**
* @parameter key
* @returns corresponding value from messages.properties
*/
def lookupMessage(String theKey, Object[] data) {
def locale = getLocale()
def theValue = null
MessageSource messageSource = applicationContext.getBean("messageSource")
try {
theValue = messageSource.getMessage(theKey,data,locale )
} catch (org.springframework.context.NoSuchMessageException e){
log.error "Missing message ${theKey}"
} catch (java.lang.NullPointerException e) {
log.error "Expression does not exist."
}
return theValue
}


/**
* Get the locale
* @return locale
* */
def getLocale() {
def Locale locale = null
try {
locale = RCU.getLocale(getSession().request)
}
catch(java.lang.IllegalStateException e){
//log.debug "Running in console?"
}
//log.debug "locale: ${locale}"
return locale
}
/**
* Get the HTTP Session
* @return session
**/
private HttpSession getSession() {
return RequestContextHolder.currentRequestAttributes().getSession()
}
}

/**
Expand Down
14 changes: 7 additions & 7 deletions rundeckapp/grails-app/views/reports/_baseReport.gsp
Expand Up @@ -23,7 +23,7 @@
<col style="width:18px;"/>
<col />
<col style="width:60px;"/>
<col style="width:80px;"/>
<col style="width:100px;"/>
<col style="width:10ex;"/>
<thead>

Expand All @@ -37,7 +37,7 @@
</g:if>
<th><g:message code="jobquery.title.projFilter"/></th>
<th><g:message code="jobquery.title.userFilter"/></th>
%{--<th><g:message code="jobquery.title.nodeFilter"/></th>--}%
<th><g:message code="jobquery.title.nodeFilter.plural"/></th>
<th><g:message code="jobquery.title.endFilter"/></th>
</tr>
</thead>
Expand Down Expand Up @@ -100,11 +100,11 @@
${it?.author.encodeAsHTML()}
</td>

%{--<td>--}%
%{--<g:if test="${it instanceof ExecReport}">--}%
%{--${it?.node.encodeAsHTML()}--}%
%{--</g:if>--}%
%{--</td>--}%
<td>
<g:if test="${it instanceof ExecReport}">
${it?.node.trim().encodeAsHTML()}
</g:if>
</td>

<td style="white-space:nowrap" class="right">
<g:if test="${it.dateCompleted}">
Expand Down

0 comments on commit 6451ebb

Please sign in to comment.