Generic Device with xMod (GIG part 9)

For this post, I’m going to change course a little and show an integration idea I had, and I’m going to talk through the process to explain all the different pieces.

The problem I was looking into is the concept of a “Generic Device”. If you’re familiar with our on premise product, you may have seen documentation for a Generic Device that allows for firing a command line call as a device. I’ve seen this used to post messages to Facebook, to an electronic bulletin board and chat programs. Unfortunately, because of the many security headaches associated with this, we couldn’t add it to the On Demand product. (No, we’re not going to let you run OS level commands in our data center, so don’t even ask.)

This means we can’t (yet!!!) deliver messages to a chat service such as Slack…. or so everyone thought. It turns out this is possible with mind bending hackery, duct tape and kludge a good deal of creativity. Cruise along down the rabbit hole to see where it goes.
Er, I might have to put a disclaimer on this one. This is a valid proof of concept, but I’m not sure it would be a good idea, and I present it here as a teaching tool. There are bigger and better things in the works to make this happen natively.

[Scroll to the bottom to skip the babbling]

The key to this whole deal is the integration agent. As we’ve seen in other articles here, the integration agent is a powerful tool for building integrations because it provides a javascript environment in which to package libraries and run code. We’re going to couple this with the event status callback. Remember back in part 5 we introduced callbacks and one such callback was the status callback? This fires on event level stuff, such as event creation and termination. Sweet, we’ll hijack this callback and point it at an integration agent. Then, regardless of where the event came from it will run some code of our choosing. Since the callbacks can be given some (or all) of the properties (with the Include in Callbacks checkbox), we’ll have all the event details in our integration agent code!

Ok, let’s start picking this apart. When this event status reaches the integration, this is the payload:

{
  "status": "active",
  "apia_priority": "normal",
  "eventidentifier": "1016010",
  "username": "tdepuy",
  "date": "15-04-16 17:31:26.226",
  "agent_application_id": "ip-999-999-999-999/999.999.999.999:8081",
  "apia_source": "alarmpoint: ",
  "apia_process_group": "apia_default_group",
  "agent_client_id": "applications|genericDevice",
  "additionalTokens": {
    "Fruits": "Cherymoya",
    "Flavor": "Coffee Tart",
    "Baker's Notes": "Frosty with a chance of meatballs",
    "Nuts?": "false"
  },
  "incident_id": "INCIDENT_ID-1016010",
  "xmatters_callback_type": "status"
}

In the additionalTokens section we can see the list of all our properties. Ok, cool. We’ll just craft an HTTP POST to our chat program and send over those event properties and we’re done! Hooray! We’ve saved the day… er… well, all we got was the data… what about who should be notified? Do you see any recipient details in there? doh. Not good.

“Wait!”, you object, “Isn’t there a GET events API call we can pull more details on the event from?” Yes! There is. Since we have the event id in that callback, we could make a request back into xMatters and retrieve the event recipients. Well, the GET events API doesn’t return any details of the event, just a list of hrefs that aren’t allowed (give a 405). Bummer. However, there is hope. The GET notifications API call is what we need. It returns all the details of the actual notification part of the event. Oh yea, I forgot to mention this doesn’t retrieve the actual address of the device (tdepuy@xmatters.com for example), so we’ll just have to assume it is the same as the user name.

For example, it might return:

{
   "total" : "2",
   "nextRecordsUrl" : "",
   "notifications" : [{
         "identifier" : 1307021,
         "user" : {
            "lastName" : "Depuy",
            "targetName" : "tdepuy",
            "firstName" : "Travis"
         },
         "incident" : "INCIDENT_ID-1013008",
         "deviceName" : "SlackMail",
         "event" : 1013008,
         "status" : "Delivered",
         "domain" : "applications",
         "created" : "2015-04-14T16:47:52+00:00",
         "delivered" : "2015-04-14T16:47:54+00:00",
         "responded" : "",
         "response" : "",
         "sender" : "tdepuy",
         "deviceType" : "Email",
         "protocol" : "SMTP",
         "protocolProvider" : "(x)Matters SMTP Email PP",
         "subscription" : "",
         "subscriptionName" : ""
      }, {
         "identifier" : 1307017,
         "user" : {
            "lastName" : "Depuy",
            "targetName" : "tdepuy",
            "firstName" : "Travis"
         },
         "incident" : "INCIDENT_ID-1013007",
         "deviceName" : "Work Email",
         "event" : 1013007,
         "status" : "Delivered",
         "domain" : "applications",
         "created" : "2015-04-14T16:36:42+00:00",
         "delivered" : "2015-04-14T16:36:44+00:00",
         "responded" : "",
         "response" : "",
         "sender" : "tdepuy",
         "deviceType" : "Email",
         "protocol" : "SMTP",
         "protocolProvider" : "(x)Matters SMTP Email PP",
         "subscription" : "",
         "subscriptionName" : ""
      }
   ]
}

Ironically, this API call doesn’t allow for requesting a specific event. Ugh. Fortunately, without any parameters, it will retrieve the notifications from the last 15 minutes. This is close enough for our uses, since it returns a JSON object, we can easily parse through and filter out just our event. The other thing we need to filter out is the other devices. We want to “try” and honor the device preferences of the user, so maybe this Slack device will be before their Email address. So, let’s just create a new Device Type to handle this new protocol.

Ok, now we have everything we need:

  1. All the event data (from the callback)
  2. The list of recipients (from the GET notifications call)
  3. An environment to make these calls (in the integration agent)

This is the apia_callback function in all: (there is a helper.js with the helper functions)

function apia_callback(msg)
{
   // Log some info so we can sort out what is happening
   var str = "Received message from xMatters:\n";
   str += JSON.stringify( msg );
   IALOG.info(str);

   // Extract the eventID for reference
   eventID = msg.eventidentifier;

   // Build the url for making the GET notifications call
   url = XM_BASE_URL + '/reapi/2015-01-01/notifications';

   // Make the call and store it in a JSON object
   notes = JSON.parse( httpget( url, XM_USER, XM_PASS ) );

   // Extract out the recipients
   recipients = getRecipients( notes );
   IALOG.debug( 'Recipients: ' + recipients );

   // Build the payload to slack
   // This could be pretti-fied way better
   slackMessage = {
     "username": "xMatters",
     "text": '@' + recipients.join( ', @' ) + '. Event ' + eventID + ' created. msg: \n' + JSON.stringify( msg.additionalTokens, null, 1, 1 ),
     "icon_emoji": ":japanese_ogre:"
   }

   // If we're talking to a channel throw that in there too
   if( SLACK_CHANNEL )
     slackMessage.channel = SLACK_CHANNEL;

   // Send off to slack
   slackResp = httppost( SLACK_URL, slackMessage, '', '' );

   IALOG.debug( 'slackResp: ' + slackResp );
}

Man, that is quite a mess. This has a number of limitations, primarily that it doesn’t really honor the device escalation. And it makes two http calls back to xM per event. Each of these could potentially have very large payloads that could impact performance. And it just isn’t pretty.

If you want to play with this in your environment, here are some installation steps:

  1. Add a new Email Device called “SlackMail”. Details here. Update the GENERIC_DEVICE_NAME parameter in the genericDevice/configuration.js file with this value.
  2. Unzip the attached genericDevice.zip into IAHOME/integrationservices and update IAHOME/IAConfig.xml with this new service.
  3. In the xMod UI > Developer tab > Event Domains > applications. Create a new Integration Service called genericDevice. When your integration agent is restarted, this integration service should show active.
  4. Make sure to check the “Include in Callbacks” for each property in the layout of the Comm Plan form.
  5. For any Comm Plan forms that should use this, you’ll need to hijack the event status callback to have:
   "callbacks": [{
         "url": "ia://genericDevice",
         "type": "status"
       }]

Or in the UI:

 

Oh yea, almost forgot, screen shot or it didn't happen:

 

 

Click here for the next installment of the GIG!

Have more questions? Submit a request

0 Comments

Please sign in to leave a comment.
Powered by Zendesk