Script Steps After the mapper has delivered its requitition, multiple script steps can be used to customize the result. These script steps can change the requisition initially provided by the mapper. Script steps provide a requisition as their results, which allows the chaining of script steps. Every script step has access to the latest version of the requisition. The first script step reads the requisition from the mapper. The second script step reads the requisition provided by the first script step and so on. Additionally every script step can access the configuration of PRIS and the raw result of the source. The script steps are executed by the JVM following the JSR-223 specification. Therefore all JSR-223 supported languages can be used to write script steps. By default runtimes for Groovy 2.3.3 and Beanshell 2.0b5 are provided out of the box. Every script step has to provide a Requisition object as its result. For every request of a requisition each script step is reloaded. Script steps are configured in the requisition.properties for the requisition. /opt/opennms-pris └── requisitions | └── myRequisitionConfiguration | └── requisition.properties └── scriptsteps └── default | └── reverseDNS.groovy | └── requisitionRename.groovy | └── IgnoreNodeByCategory.groovy └── custom └── myScript.groovy How to add script steps ### File: requisition.properties ## source configuration part source = ... ## Run a no operation mapper mapper = echo # run script step script.file = ../../scriptsteps/default/requisitionRename.groovy, ../../scriptsteps/default/IgnoreNodeByCategory.groovy Example script step 1 /** * This script renames the requisition to the value in the newName variable. * If newBuilding is set to true, each node gets the newName set as building */ import org.opennms.pris.model.Requisition import org.opennms.pris.model.RequisitionNode logger.info("starting requisitionRename.groovy") String newName = "myNewRequisitionName" Boolean newBuilding = false // true requisition.setForeignSource(newName) if (newBuilding) { for (RequisitionNode node : requisition.getNodes()) { node.setBuilding(newName) } } logger.info("done with requisitionRename.groovy") return requisition Example script step 2 /** * This script step removes every node from the requisiton that has the "ignore" category assigned to it. * The category match ignores case. **/ import org.opennms.pris.model.Requisition import org.opennms.pris.model.RequisitionNode import org.opennms.pris.model.RequisitionCategory import org.opennms.pris.util.RequisitionUtils logger.info("starting IgnoreNodeByCategory.groovy") final String IGNORE_CATEGORY = "ignore" List <RequisitionNode> nodes = new ArrayList<>(); for (RequisitionNode node : requisition.getNodes()) { if (RequisitionUtils.hasCategory(node, IGNORE_CATEGORY, true)) { logger.debug("node '{}' has to be ignored", node.getForeignId()) } else { nodes.add(node) logger.debug("node '{}' is ok", node.getForeignId()) } } requisition.unsetNodes() requisition.withNodes(nodes) logger.info("done with IgnoreNodeByCategory.groovy") return requisition Example script step 3 /** * This script step sets the nodelabel based on a reverse dns lookup of the ip interfaces. * It reverse dns lookups all interfaces for each node until it findes a dns name for a node. * If a dns name was found it is set as nodelabel and no other interface of the nodes will be checked. * If no dns name was found the nodelabel will be changed. */ import org.opennms.pris.model.Requisition import org.opennms.pris.model.RequisitionNode import org.opennms.pris.model.RequisitionInterface logger.info("starting reverseDNS.groovy") for (RequisitionNode node : requisition.getNodes()) { for (RequisitionInterface myInterface : node.getInterfaces()) { String ipAddress = myInterface.getIpAddr() String dnsNodeLabel = InetAddress.getByName(ipAddress).getCanonicalHostName() logger.debug("For foreignID '{}' dnsNodeLabel for IP '{}' is '{}'", node.getForeignId(), ipAddress, dnsNodeLabel) if (!ipAddress.equals(dnsNodeLabel)) { logger.info("Using '{}' as NodeLabel for foreignId '{}' based on IP '{}'", dnsNodeLabel, node.getForeignId(), ipAddress) node.setNodeLabel(dnsNodeLabel) break } } } logger.info("done with reverseDNS.groovy") return requisition Example script step 4 /** * This script forces a hard failure if the requisition is empty. * This can avoid provisioning an empty requisition, * including removing the existing nodes, on a failure of a source or mapper. **/ import org.slf4j.Logger import org.opennms.pris.model.Requisition logger.info("starting failOnEmpty.groovy") logger.debug("Amount of nodes in the requisition '{}'", (requisition.getNodes().size())) if (requisition.getNodes().size() == 0) { throw new Exception("The requisition '" + requisition.getForeignSource() + "' had no nodes. The failOnEmpty.groovy script is failing the request on purpose.") } logger.info("done with failOnEmpty.groovy") return requisition Example script step 5 /** * This script provides backwards compatibility with OpenNMS 1.12 in regards to Assets. **/ import org.opennms.pris.model.Requisition import org.opennms.pris.model.RequisitionNode import org.opennms.pris.model.RequisitionAsset import org.opennms.pris.util.RequisitionUtils import org.opennms.pris.model.AssetField_1_12 import org.opennms.pris.util.AssetUtils logger.info("starting OpenNMS_Assets_1_12.groovy") List<RequisitionAsset> assetsToRemove = new ArrayList<>(); for (RequisitionNode node : requisition.getNodes()) { for (RequisitionAsset asset : node.getAssets()) { if (asset.getName().equalsIgnoreCase("managedObjectInstance") || asset.getName().equalsIgnoreCase("managedObjectType") ) { assetsToRemove.add(asset); logger.info("Remove from node '{}' the asset '{}' with the value '{}'", node.getNodeLabel(), asset.getName(), asset.getValue()); } else { for (AssetField_1_12 assetField : AssetField_1_12.values()) { if (asset.getName().equalsIgnoreCase(assetField.name())) { String assetValue_1_12 = AssetUtils.assetStringCleaner(asset.getValue(), assetField.maxLength); if (!assetValue_1_12.equals(asset.getValue())) { logger.info("For node '{}' asset '{}' was changed from '{}' to '{}'" , node.getNodeLabel(), asset.getName(), asset.getValue(), assetValue_1_12) asset.setValue(assetValue_1_12); } break; } } } } node.getAssets().removeAll(assetsToRemove); assetsToRemove = new ArrayList<>(); } logger.info("done with OpenNMS_Assets_1_12.groovy") return requisition Every script step can reference variables from the runtime of PRIS. The following script shows the provided objects: Example script step 6 /** * This sample script step demonstrates all objects provided by the pris runtime. * The objects are casted to their exact type and are logged. **/ import java.nio.file.Path import org.slf4j.Logger import org.opennms.pris.model.Requisition import org.opennms.pris.util.InterfaceUtils import org.opennms.pris.config.InstanceApacheConfiguration logger.info("starting Sample.groovy") logger.debug("script '{}'", ((Path)script)) logger.debug("data '{}'", ((Object)data)) logger.debug("requisition '{}'", ((Requisition)requisition)) logger.debug("logger '{}'", ((Logger)logger)) logger.debug("config '{}'", ((InstanceApacheConfiguration)config)) logger.debug("config '{}'", ((InterfaceUtils)interfaceUtils)) logger.info("done with Sample.groovy") return requisition The following two script steps provide a mechanism to fail the requisition on purpose if the amount of nodes has changed to drastically to the previous run. The setup contains of two scripts that have to be included as script steps in order. Example script step 7 /** * This script persists the amount of nodes delivered for the last request into a previousSize.txt file next to the requisition.properties. * To configure the script provide a script.sizeChangeAbs parameter in the requisition.properties. **/ import org.slf4j.Logger import org.opennms.pris.model.Requisition import javax.xml.bind.JAXBContext import java.io.StringWriter logger.info("starting failOnSizeChange.groovy") String fileName = "previousSize.txt" String sizeChangeAbsParam = "sizeChangeAbs" logger.info("Is '{}' set in config? '{}'", sizeChangeAbsParam ,config.containsKey(sizeChangeAbsParam)) if (config.containsKey(sizeChangeAbsParam)) { logger.info("fail on size change bigger than '{}'", config.getInt(sizeChangeAbsParam)) int sizeChangeThreshold = config.getInt(sizeChangeAbsParam) File previousSizeFile = new File("requisitions" + File.separator + requisition.getForeignSource() + File.separator + fileName) if (previousSizeFile.exists() && previousSizeFile.canRead()) { int previousSize = previousSizeFile.getText('UTF-8').toInteger() logger.info("previousSize from file is '{}'. New size is '{}'", previousSize, requisition.getNodes().size()) int sizeChange = (previousSize - requisition.getNodes().size()).abs() if (sizeChange > sizeChangeThreshold) { throw new Exception("The requisition '" + requisition.getForeignSource() + "' changed size by '" + sizeChange + "' nodes. The configured limit for size change is '" + sizeChangeThreshold + "' Failed on purpose.") } } else { logger.info("No " + fileName + " found. Deliver requisition.") } } logger.info("done with failOnSizeChange.groovy") return requisition The persistence of the previous requistion size is provided by the second script step of the setup. Example script step 8 /** * This script persists the amount of nodes delivered for the last request into a previousSize.txt file next to the requisition.properties. **/ import org.slf4j.Logger import org.opennms.pris.model.Requisition import javax.xml.bind.JAXBContext import java.io.StringWriter logger.info("starting persistRequisitionSize.groovy") JAXBContext jc = JAXBContext.newInstance(Requisition) StringWriter sw = new StringWriter() jc.createMarshaller().marshal(requisition, sw) String xml = sw.toString() File file = new File("requisitions" + File.separator + requisition.getForeignSource() + File.separator + "previousSize.txt") file.write requisition.getNodes().size() + "" logger.info("persisted a requisition size of {}", requisition.getNodes().size()) logger.info("done with persistRequisitionSize.groovy") return requisition Example script step 9 /** * This script step adds every node from the requisiton that has a specific category assigned to it. **/ import org.opennms.pris.model.Requisition import org.opennms.pris.model.RequisitionNode import org.opennms.pris.model.RequisitionCategory import org.opennms.pris.util.RequisitionUtils logger.info("starting selectNodeByCategory.groovy") final String SELECT_CATEGORY = "CATEGORY" List <RequisitionNode> nodes = new ArrayList<>(); for (RequisitionNode node : requisition.getNodes()) { if (RequisitionUtils.hasCategory(node, SELECT_CATEGORY, true)) { logger.debug("node '{}' is ok", node.getForeignId()) nodes.add(node) } else { logger.debug("node '{}' has to be ignored", node.getForeignId()) } } requisition.unsetNodes() requisition.withNodes(nodes) logger.info("done with selectNodeByCategory.groovy") return requisition Data Mapping with a Script Variable Substitution