Notification Plugin
Notification Plugin
About
Notifications are actions that are performed when a Job starts or finishes.
Currently, there are five conditions that can trigger notifications:
onstart
- the Job startedonsuccess
- the Job completed without erroronfailure
- the Job failed or was abortedonavgduration
- The Execution exceed the average duration of the Jobonretryablefailure
- the Job failed but will be retried
Rundeck has two built-in notification types that can be configured for Jobs:
- Send an email to a list of addresses
- POST XML to a list of URLs
This chapter discusses how to create your own notification plugins.
Plugin execution
When a notification is defined for a Job, and the associated trigger occurs, your plugin will be executed
and passed in two sets of Map data:
- Configuration data - the user-supplied configuration for the plugin
- Execution data - information about the Job and Execution for the notification
Configuration data
The Configuration data is fully custom depending on your plugin, and is described in the Plugin configuration properties section.
Notifications thread feature
Notifications can be run on their own thread by setting the following properties in the rundeck-config.properties file:
rundeck.feature.notificationsOwnThread.enabled
: ('true','false') When set to 'true' it will trigger the notifications on a separate thread.rundeck.notification.threadTimeOut
: (numeric, in milliseconds, defaults to 120000ms) When you set the notifications to be triggered on a separate thread, you can also set a time out for the notification thread so that it must be sent before a certain time.
Property References
The specific data values of the Configuration section are allowed to have
embedded Property References as described in the
Jobs - Context Variables section.
For example, when a user configures your plugin, they could embed an option value using: ${option.myoption}
. This value will be replaced with the runtime option value before being passed to your plugin.
When defining Configuration properties that use custom Validation, keep in mind
that the value set by a user may have such an embedded property reference and
therefore may not pass the validation rules you have defined. If you want to
allow these property references for a Configuration property, it must be a String type property, and any custom validation code should allow the embedded
property references, for example by looking for a ${
sequence and allowing
the value.
Execution data
The execution data is included as a Map called execution
containing the following keys and values:
Note: The context variables that will be available for users to use on inputs on the GUI will depend on the specific notification plugin.
execution.id
: ID of the execution
execution.href
: URL to the execution output view
execution.status
: Execution state ('running','failed','aborted','succeeded')
execution.user
: User who started the job
execution.dateStarted
: Start time (java.util.Date)
execution.dateStartedUnixtime
: Start time as milliseconds since epoch (long)
execution.dateStartedW3c
: Start time as a W3C formatted String
execution.description
: Summary string for the execution
execution.argstring
: Argument string for any job options
execution.project
: Project name
execution.loglevel
: Loglevel string ('ERROR','WARN','INFO','VERBOSE','DEBUG')
The following values may be available after the job is finished (not available for onstart
trigger):
execution.failedNodeListString
: Comma-separated list of any nodes that failed, if present
execution.failedNodeList
: Java List of any node names that failed, if present
execution.succeededNodeListString
: Comma-separated list of any nodes that succeeded, if present
execution.succeededNodeList
: Java List of any node names that succeeded, if present
execution.nodestatus
: Java Map containing summary counts of node success/failure/total, in the form: [succeeded: int, failed: int, total: int]
execution.dateEnded
: End time (java.util.Date)
execution.dateEndedUnixtime
: End time as milliseconds since epoch (long)
execution.dateEndedW3c
: End time as W3C formatted string
execution.abortedby
: User who aborted the execution
job
information is in a job
entry and contains another Map:
job.id
: Job ID
job.href
: URL to Job view page
job.name
: Job name
job.group
: Job group
job.project
: Project name
job.description
: Job Description
job.averageDuration
: Average job duration in Milliseconds, if available
execution.context
- this is a map containing all of the context variables available to the execution when it ran or will run, such as Jobs - Context Variables. The contents of this Map are the specific context namespaces and variables.
execution.context.option
: a Map containing all Job Option keys/values.
execution.context.secureOption
: a Map containing only the Job Option keys/values whose value is set from secure storage.
job
: a Map containing the Job context data, as provided to executions. This map will contain some duplicate information as the execution.job
map previously described.
In Groovy, you can simply reference any values in the Execution data maps using
Groovy Gpath, e.g.:
println execution.context.option.myoption
Plugin configuration properties
Each plugin can define a set of "configuration" properties which allow users to specify input that the plugin can
use when it operates.
Notification plugins support scoped properties, allowing some of the configuration to be defined, or defaulted, on a per-project or per-Rundeck instance basis.
Plugin types
Rundeck supports two types of Notification plugins:
- Java-based development deployed as a Jar file.
- Groovy-based deployed as a single
.groovy
script.
Currently "script-based" plugins (shell scripts, that is) are not supported.
Example code
See the source directory examples/example-groovy-notification-plugins
for
examples of Notification plugins written in Groovy.
- On github: example-groovy-notification-plugins
See the source directory examples/example-java-notification-plugin
for
Java examples.
- On github: example-java-notification-plugin
Java Plugin Type
Java-based plugins can be developed just as any other Rundeck plugin, as described in the chapter Plugin Development - Java Plugin Development.
These plugin classes should implement the interface
NotificationPlugin:
public interface NotificationPlugin {
/**
* Post a notification for the given trigger, dataset, and configuration
* @param trigger event type causing notification
* @param executionData execution data
* @param config notification configuration
*/
public boolean postNotification(String trigger,Map executionData,Map config);
}
To define configuration properties for your plugin, you use the same mechanisms as for Workflow Steps, described under the chapter Plugin Development - Plugin Descriptions.
The simplest way to do this is to use Plugin Annotations. Here is an example class annotated to describe it to the Rundeck GUI:
@Plugin(service="Notification", name="example")
@PluginDescription(title="Example Plugin", description="An example Plugin for Rundeck Notifications.")
public class ExampleNotificationPlugin implements NotificationPlugin{
@PluginProperty(name = "test" ,title = "Test String", description = "a description")
private String test;
public boolean postNotification(String trigger, Map executionData, Map config) {
System.err.printf("Trigger %s fired for %s, configuration: %s\n",trigger,executionData,config);
System.err.printf("Local field test is: %s\n",test);
return true;
}
}
Groovy Plugin Type
Notification supports the Groovy Plugin Type.
To define metadata about your plugin, and configuration properties, see the Plugin Development - Groovy Plugin Development chapter.
To create a Groovy based plugin, create a file named MyNotificationPlugin.groovy
in the plugins directory for Rundeck.
To make the plugin available, as well as after any plugin code modifications, you must restart Rundeck.
Groovy DSL
Within the Groovy script, you define your plugin by calling the rundeckPlugin
method, and pass it both the Class of the type of plugin, and a Closure used to build the plugin object.
import com.dtolabs.rundeck.plugins.notification.NotificationPlugin
rundeckPlugin(NotificationPlugin){
//plugin definition goes here...
}
In this case, we use the same NotificationPlugin
interface used for Java plugins.
Notification handlers
For a NotificationPlugin
, you can define custom handlers for each of the notification triggers (onsuccess
, onfailure
, onstart
, onavgduration
, and onretryablefailure
).
Simply define a closure with the given trigger name, and return a true value if your action was successful:
onstart{ Map execution, Map configuration ->
//perform an action using the execution and configuration
println "Job ${execution.job.name} has been started by ${execution.user}..."
return true
}
onsuccess{ Map execution, Map configuration ->
//perform an action using the execution and configuration
println "Success! Job ${execution.job.name} worked fine."
return true
}
onfailure{ Map execution, Map configuration ->
//perform an action using the execution and configuration
println "Oh No! Job ${execution.job.name} didn't work out."
return true
}
onavgduration{ Map execution, Map configuration ->
//perform an action using the execution and configuration
println "Job ${execution.job.name} exceeded Average Duration!"
return true
}
onretryablefailure{ Map execution, Map configuration ->
//perform an action using the execution and configuration
println "Job ${execution.job.name} failed but will be retried."
return true
}
If your closure returns a false
value, then Rundeck will log an error in the server log.
Example
Here is a minimal example:
MinimalNotificationPlugin.groovy:
import com.dtolabs.rundeck.plugins.notification.NotificationPlugin;
rundeckPlugin(NotificationPlugin){
onstart {
println("job start: data ${execution}")
true
}
onfailure {
println("failure: data ${execution}")
true
}
onsuccess {
println("success: data ${execution}")
true
}
onavgduration{
println("exceeded average duration: data ${execution}")
true
}
onretryablefailure{
println("retryable failure: data ${execution}")
true
}
}
Here is a full example showing plugin GUI metadata, configuration properties, and
alternate closure parameter lists:
MyNotificationPlugin.groovy:
import com.dtolabs.rundeck.plugins.notification.NotificationPlugin;
rundeckPlugin(NotificationPlugin) {
title="Example Plugin"
description="An example"
configuration{
test1 title:"Test1", description:"Simple string"
//Validation can be added with a closure
test2(title:'Test2',description:"Matches a regex"){
it=~/^\d+$/
}
//required select value, becomes a Select type
test3 values: ["a","b","c"], required:true
//if not required, becomes a FreeSelect
test4 values: ["a","b","c"]
//If type is not specified, the defaultValue will be used to guess
test5 defaultValue: 3 //becomes Integer type
test6 defaultValue:true //becomes Boolean type
//these properties are assigned default values and automatically typed
test7=123
test8="abc"
test9=true
test10=false
test11=["x","y","z"] //becomes a FreeSelect
//redefining the same property will modify it
test11 title:"My Select Field", description:"Free Select field", defaultValue:"y", required:true
//the scope indicates the property will not show up in the GUI when configuring the Notification, but must be defined in the project.properties or framework.properties at runtime
test11 required:true, scope: 'Project'
}
onstart { Map executionData,Map config ->
println("script, start: data ${executionData}, config: ${config}")
true
}
onfailure { Map executionData ->
//Single argument, the configuration properties are available automatically
println("script, failure: data ${executionData}, test1: ${test1}, test2: ${test2} test3: ${test3}")
true
}
onsuccess {
//with no args, there is a "configuration" and an "execution" variable in the context
println("script, success: data ${execution}, test1: ${configuration.test1}, test2: ${configuration.test2} test3: ${configuration.test3}")
true
}
onavgduration { Map executionData,Map config ->
println("script, exceeded average duration: data ${executionData}, config: ${config}")
true
}
onretryablefailure { Map executionData,Map config ->
println("script, retryable failure: data ${executionData}, config: ${config}")
true
}
}