A Code Walkthrough of Holmes Engine Management

Understanding major lines of the source code of a component surely helps one to understand how it works, but it may take tremendous effort. This page walks you through the source code of Holmes Engine Management, in the hope to

  1. Help you to understand how things work internally.

  2. Help people working with the code to grasp the main plot. 

You can check out the source code from ONAP gerritThere are two sub modules, engine-d-standalone and engine-d. engine-d-standalone is for packaging the application into a Docker image, while engine-d has all the business logics.

Setup Docker Image

Holmes Engine Management can run in a Docker container. In folder /engine-d-standalone/src/main/assembly/, Dockerfile is used to pack the Docker image. During the packing, ActiveMQ and PostgreSQL client are installed. Docker ENTRYPOINT is bin/run.sh, where conf/engine-d.yml is used to inject parameters to Dropwizard configuration, org.onap.holmes.engine.EngineDAppConfig, which is then passed to org.onap.holmes.engine.EngineDActiveApp. EngineDActiveApp, a Dropwizard application, is the entrance point to Holmes Engine Management application. Dropwizard is an open source Java framework for developing RESTful web services, like Spring Boot.

Four Things Happen in EngineDActiveApp

  1. Register Holmes Engine Management service to MSB.

  2. Setup a background task to periodically (every 30 seconds) poll DCAE policy updates.

  3. Set up a servlet filter to generate unique transaction ids for REST requests.

  4. Register a REST resource to Dropwizard environment to serve REST APIs.

The following sections will go into details on these four aspects.

Register to MSB (Microservices Bus)

It is required that env variable HOSTNAME is provided. Engine host address and port are obtained from Consul by passing in HOSTNAME (in org.onap.holmes.common.config.MicroServiceConfig.getMicroServiceIpAndPort()):
http://{CONSUL_HOST}:8500/v1/catalog/service/{HOSTNAME} ,
where CONSUL_HOST is defined in env as well.

Host address and port are then submitted to MSB, along with:
       msinfo.setServiceName("holmes-engine-mgmt");
       msinfo.setVersion("v1");
       msinfo.setUrl("/api/holmes-engine-mgmt/v1"); 

DCAE Configuration Polling

A thread is forked using JDK Executors.newSingleThreadScheduledExecutor(), and the forked task, DcaeConfigurationPolling, runs at an interval of 30 seconds.

Config Binding Service (CBS) endpoint is obtained from DCAE Consul:
http://{CONSUL_HOST}:8500/v1/catalog/service/{CONFIG_BINDING_SERVICE}

From CBS, configuration info on HOSTNAME is obtained through:
http://{CBS ServiceAddress}:{CBS ServicePort}/service_component/{HOSTNAME}
(In MicroServiceConfig.getServiceConfigInfoFromCBS())

The configuration data from CBS is in JSON format, and is parsed by DcaeConfigurationParser. The result is a DcaeConfigurations object, which contains rules, DMaaP Publisher topics, and DMaaP Subscriber topics. (In DcaeConfigurationParser.parse())

If the DcaeConfigurations has update (by checking its MD5 against the previous version), DcaeConfigurationsCache is updated with the new config, and a subscriber is created for each new DMaaP Subscriber topic. (In DcaeConfigurationPolling)

DMaaP Subscriber 

Each subscriber runs in its own thread as DMaaPAlarmPolling. (SubscriberAction.addSubscriber())

In DMaaPAlarmPolling, the subscriber reads the VES event data from DMaaP in JSON format, and converts it into a VES object, VesAlarm.  

Part of the info in VES object is then saved to PostgreSQL database table ALARM_INFO through this SQL statement: (in AlarmInfoDao.saveAlarm())
INSERT INTO ALARM_INFO  (EVENTID,EVENTNAME,STARTEPOCHMICROSEC,SOURCEID,SOURCENAME,ALARMISCLEARED,ROOTFLAG,LASTEPOCHMICROSEC) VALUES (:eventId,:eventName,:startEpochMicroSec,:sourceId,:sourceName,:alarmIsCleared,:rootFlag,:lastEpochMicroSec)

The VES object is then passed to DroolsEngine.putRaisedIntoStream(), where Drools Kie API is used to fire all rules on the event, and trigger new event if need.

Transaction Id Filter

Servlet filter TransactionIdFilter is setup to intercepts all HTTP requests. (In EngineDActiveApp)

If header “X-TransactionID" does not present in a HTTP request, the filter generates a random UUID and insert it to request header “X-TransactionID".

When sending responses to HTTP clients, response header “X-TransactionID" is set with the above value, and a new UUID is generated for another header, "X-InvocationID". (In TransactionIdFilter).

The purpose of these IDs is unclear to me at this writing. Your input is welcome.

Engine Management REST API

Like Spring Boot Controllers, Dropwizard Jersey resources are the entrance points of web services. EngineResources is such a resource that provides all engine REST APIs, and has the following services:

  1. Deploying a rule into the Drools engine
    The request should be in such format:

    PUT /api/holmes-engine-mgmt/v1/rule
    {
     "content": "string",
     "engineid": "string",(optional)
     "loopcontrolname": "string"
    }

    This request is handled by EngineResources.deployRule(), where the “content” in the HTTP body is used as a rule and is inserted into Drools rule engine using Kie API. On successfully deploying the rule, the package name for the rule is returned to the client.


  2. Testing if a rule is valid
    The request should be in such format:

    POST  /api/holmes-engine-mgmt/v1/rule
    {
    "content": "string"
    }

    The "content" in the HTTP POST body contains the rule to be validated.
    This request is handled by EngineResources.compileRule(), where the “content” in the HTTP body is used as a rule and Kie API is used to check if the rule is valid. Notice that when deploying a rule (described above), the same validation is also performed.


  3. Undeploying a rule from the Drools engine
    The request URI should be in such format:
    DELETE /api/holmes-engine-mgmt/v1/rule/{packageName}

    where {packageName} is the name of the package to be remove from the Drools engine. This service is handled by EngineResources.undeployRule().

    engine-d/src/main/resources/swagger.json is the OpenAPI Specification (OAS) file that describes all the above APIs. This wiki page has the complete spec of these REST APIs.