= Scriptd
:description: Overview and examples of scriptd in {page-component-title}, which transforms and enhances data from events that other daemons generate.

Scriptd can transform and enhance {page-component-title} events generated by other daemons.
It can generate new events based on incoming data, enrich events with new data, and forward events as SNMP traps to external systems amongst other uses.
This feature enables better integration with external systems, based on data from sources internal and external to {page-component-title}.

The `$\{OPENNMS_HOME}/etc/scriptd-configuration.xml` file contains elements and attributes that define how scriptd processes events and executes scripts.
Beanshell scripts executed by `scriptd` should be placed in `$\{OPENNMS_HOME}/etc/scripts/`

When an event is published on the event bus that Scriptd is configured to listen for, it executes the beanscript associated with that event.
A common (though not _only_, as you can see in the examples) usage pattern is to compose a script that defines one or more a callable functions that accept an immutable event `IEvent` as a argument, which is sourced when scriptd starts, in `start-script` block.
This makes the function available within the context of `scriptd` so that the script function can be called from an `event-script` block.
Beanshell script can also be embedded directly in the `start-script` or `event-script` blocks, using xml `CDATA` tags.

== Examples

[[ga-scriptd-rdns-parm]]
=== Example 1: Enriching an event

The following example performs a reverse DNS lookup on the content of a specified event parameter, and emits a new event with a distinct UEI and a new parameter containing the rDNS name.

.`$\{OPENNMS_HOME}/etc/scriptd-configuration.xml`
[source, xml]
----
<scriptd-configuration xmlns="http://xmlns.opennms.org/xsd/config/scriptd" transactional="false">
   <engine language="beanshell" className="bsh.util.BeanShellBSFEngine" extensions="bsh"/>

   <start-script language="beanshell">
     log = bsf.lookupBean("log"); <1>
     source("/opt/opennms/etc/scripts/scriptd-parm-rdns.bsh"); <2>
   </start-script>

   <stop-script language="beanshell">
     log.debug("executing a stop script");
   </stop-script>

   <event-script language="beanshell">
     <uei name="uei.opennms.org/corporate_networks/traps/OSPF-State"/> <3>
     <uei name="uei.opennms.org/corporate_networks/traps/OSPFNbrState"/>
       appendUei = "WithLookup";                <!-- the resulting event appends this to the UEI --> <4>
       positions = new int[]{ 1, 2, 4 };        <!-- Do the work on parms parm[2] and parm[4] -->
       event = bsf.lookupBean("event"); <5>
       mangleEvent(event, appendUei, positions); <6>

   </event-script>
</scriptd-configuration>
----
<1> Boilerplate to define the log object, used for logging within your script.
<2> The script is sourced on startup to define the functions it contains.
<3> The `uei` for which to listen that will execute this `event-script` block.
<4> This value is appended to the resulting event `uei`, making the emitted event uei `uei.opennms.org/corporate_networks/traps/OSPF-StateWithLookup`.
<5> Fetch the incoming event. 
<6> This function is defined in `scriptd-parm-rdns.bsh`, sourced by the `start-script` block.

.`$\{OPENNMS_HOME}/etc/scripts/scriptd-parm-rdns.bsh`
[source, beanshell]
----
import java.net.InetAddress; <1>
import org.opennms.core.spring.BeanUtils;
import org.opennms.netmgt.events.api.model.IEvent; <2>
import org.opennms.netmgt.events.api.model.IParm;
import org.opennms.netmgt.events.api.EventForwarder;
import org.opennms.netmgt.model.events.EventBuilder; <3>
import org.opennms.netmgt.xml.event.Event; <4>
import org.opennms.netmgt.xml.event.Parm;

eventForwarder = BeanUtils.getBean("daemonContext", "eventForwarder", EventForwarder.class);

void mangleEvent(IEvent event, String appendUei, int[] positions) { <5>

    log.debug("Mangling an event: {}", event.uei); <6>

    iparms  = event.getParmCollection();
    if (iparms == null) {
        log.debug("Parameters null");
    }
    else {
        log.debug("Parameters NOT null");
        newEvent = new EventBuilder(event.uei.concat(appendUei), "Scriptd", new Date()) <6>
        .setNodeid(event.nodeid)
        .setInterface(event.interfaceAddress)
        .setService(event.service)
        .getEvent();
        for(p : iparms) {
            parm = new Parm();
            newEvent.addParm(parm.copyFrom(p)); //eventForwarder won't forward an event built with immutableParms
        }
        for( i : positions ) {
            lookMeUp = lookupParm(iparms.get(i-1));
            //log.debug("parm #{}", i); <7>
            //log.debug("is appended as Name: {}", lookMeUp.getParmName());
            //log.debug(" with Value: {}", lookMeUp.getValue().getContent());
            newEvent.addParm(lookMeUp); <8>
        }

    }
    log.debug("New event {}", newEvent.toString());
    eventForwarder.sendNow(newEvent); <9>
}

Parm lookupParm(IParm parm) { <10>

    value = parm.getValue().getContent();
    try {
       InetAddress ip = InetAddress.getByName(value); <11>
       hostname = ip.getCanonicalHostName();
    } catch ( UnknownHostException e ) {
       log.debug("Reverse DNS lookup failed for {}", value);
       hostname = value; //if the lookup fails, keep the original parm value
    }
    newParm = new Parm(parm.getParmName()+"-hostname", hostname); <12>

    return newParm;
}
----
<1> `java.net.InetAddress` is used to perform the reverse DNS lookup.
<2> Incoming events are immutable `IEvent` objects, and event paramters are immutable `IParm` objects.
<3> The `EventBuilder` is used to create new events.
<4> The `EventForwarder` requires mutable `Event` and `Parm` objects.
<5> This defines the `mangleEvent` function, called from the `event-script` block in `scripts-configuration.xml`.
<6> Create a new event using the `EventBuilder` using the new, appended UEI and "Scriptd" as the source.
<7> Additional debug logging can be added using the `log` object for troubleshooting; beanshell uses Java-style comments.
<8> This adds the event parameter containing the rDNS hostname to the new event being created.
<9> Using the `EventForwarder`, send the new event out on the event bus.
<10> This function is called by `mangleEvent` to perform the rDNS lookup on the original event parameter.
<11> Perform the rDNS loookup using `InetAddress.getByName()` from `java.net.InetAddress`.
<12> Create a new event `Parm` object using the original `Parameter` name appended with `'-hostname'`, using the value returned by the `getByName()`

[[ga-scriptd-forward-snmpv3]]
=== Example 2: Forward events to an external system using SNMPv3

This example uses the `snmpv3TrapHelper` to forward events to an external system.
The `snmpv3TrapHelper` will not forward an immutable `IEvent` or an event containing immutable `IParms`, and so we create a new, mutable `Event` object and copy the parameters to new, mutable `Parm` objects.

.`$\{OPENNMS_HOME}/etc/scriptd-configuration.xml`
[source,xml]
----
<scriptd-configuration xmlns="http://xmlns.opennms.org/xsd/config/scriptd" transactional="false">
   <engine language="beanshell" className="bsh.util.BeanShellBSFEngine" extensions="bsh"/>

   <start-script language="beanshell"><![CDATA[ <1>
    log = bsf.lookupBean("log"); <2>

    snmpTrapHelper = new org.opennms.netmgt.scriptd.helper.SnmpTrapHelper(); <3>
    snmpv3TrapHelper = new org.opennms.netmgt.scriptd.helper.SnmpV3TrapEventForwarder( <4>
          "192.168.1.202", /* destination ip */
          162,             /* destination port */
          3,               /* security level: noAuth,noPriv=1, auth,noPriv=2, auth,priv=3 */
          "opennmsUser",   /* security name */
          "0p3nNMSv3",     /* auth pass phrase */
          "MD5",           /* auth protocol: MD5, SHA */
          "0p3nNmsv3",     /* priv pass phrase */
          "DES",           /* priv protocol: DES, AES, AES192, AES256 */
          snmpTrapHelper   /* helper object */
    );
    // Use the default policy rule that forwards all events - we can manage the filtering ourselves in this script
    snmpv3TrapHelper.setEventPolicyRule(new org.opennms.netmgt.scriptd.helper.EventPolicyRuleDefaultImpl());
   ]]>
   </start-script>

   <stop-script language="beanshell">
    snmpTrapHelper.stop(); <5>
   </stop-script>

   <event-script language="beanshell"><![CDATA[
    event = bsf.lookupBean("event");

    if (event != null && !event.getSource().equals("trapd")) { <6>
        iparms  = event.getParmCollection(); <7>
        newEvent = new org.opennms.netmgt.model.events.EventBuilder(event.uei, "Scriptd", new Date()) <8>
           .setNodeid(event.nodeid)
           .setInterface(event.interfaceAddress)
           .setService(event.service)
           .getEvent();
        for(p : iparms) {
            parm = new org.opennms.netmgt.xml.event.Parm(); <9>
            newEvent.addParm(parm.copyFrom(p)); //eventForwarder won't forward an event built with immutableParms
        }
        //log.debug("Forwarding SNMPv3 trap with: {}", snmpv3TrapHelper); <10>
        snmpv3TrapHelper.flushEvent(newEvent); <11>
    }
   ]]>
   </event-script>
</scriptd-configuration>
----
<1> Here we wrap the script content in CDATA tags directly in the `start-script`
<2> Boilerplate to define the log object, used for logging within the script.
<3> Create a new `snmpTrapHelper` object.
<4> Create a new `snmpv3TrapHelper` with the desired parameters.
<5> The `stop-script` block can be used to clean up on scriptd restart.
<6> This checks if the incoming event originated from a daemon other than `Trapd` and is not `null`.
<7> Fetch all immutable `IParm` event parameters from the incoming event; incoming events are always immutable.
<8> Create a new `Event` using the `EventBuilder` and set the desired event attributes; the `snmpv3TrapHelper` will not forward immutable `IEvent` events.
<9> Create a new, mutable event parameter `Parm` object; the `snmpv3TrapHelper` will not forward events with immutable `IParm` parameters.
<10> Populate the new `Parm` object with the content of the `IParm` with `copyFrom()`.
<11> Use the `snmpv3TrapHelper` to send the new, mutable event out to its destination.  

[[ga-scriptd-other]]
=== Other examples

Other scriptd examples are available in `$\{OPENNMS_HOME}/etc/examples/` directory of your {page-component-title} installation.
