I had a bunch of questions from today's SolarWidns Lab episode, "BREAKING UP WITH BAD HABITS: Monitoring, Security, and Orion -- DON'TS". I wrapped up with a 10 minute SolarWinds SDK programming class, integrating the Orion Platform with an Amazon Alexa custom skill. It's Nodejs, AWS Lambda, Alexa and of course SolarWinds SWIS, SQL and SDK.
Below I've added the JavaScript I put together to query Orion from from AWS Lambda. Check out the lab video for the tutorial and let me know if this was helpful.
Lambda Nodejs file that backends the skill we create in the episode:
'use strict'; const https = require('https'); const swisPath = '/SolarWinds/InformationService/v3/Json/Query?query='; const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; function zeroPad(number) { return (number < 10) ? '0' + number : number; } function fixDate(date) { var local = new Date(date.setHours(date.getHours() - 6)); var hours = local.getHours(); if (hours === 0) hours = 12; else if (hours > 12) hours = hours - 12; var formatted = weekdays[local.getDay()] + ', <say-as interpret-as="date">????' + zeroPad(local.getMonth() + 1) + zeroPad(local.getDate()) + '</say-as> at <say-as interpret-as="time">' + hours + ':' + zeroPad(local.getMinutes()) + ((local.getHours() < 12) ? ' A' : ' P') + 'M</say-as>'; console.log('formattted: ', formatted) return formatted; } function swisGetRESTrequest(swisQuery) { return new Promise(function(resolve, reject) { https.get({ host: process.env.SWIS_HOST, port: 17778, rejectUnauthorized: false, path: swisPath + swisQuery.replace(/ /g, '+'), // escape the spaces headers: { 'Authorization': 'Basic ' + new Buffer(process.env.SWIS_USER + ':' + process.env.SWIS_PASS).toString('base64'), 'Accept': '*/*' } }, function(response) { // Continuously update stream with data var body = ''; response.on('data', function(d) { body += d; }); response.on('end', function() { // Data reception is done, do whatever with it! //console.log('httpsOnEnd body:' + body); var jsonReply = JSON.parse(body); // test for results element == if (!jsonReply.hasOwnProperty('results')) { console.log('"results" not found in: ' + jsonReply); reject(jsonReply); } resolve(jsonReply); }); }).on('error', function(err) { reject(err); }); }); } function getOrionAccounts(callback) { var query = 'SELECT AccountID, Enabled, AllowNodeManagement, AllowMapManagement, AllowAdmin, CanClearEvents, AllowReportManagement, AllowAlertManagement,' + ' AllowCustomize, AllowUnmanage, AllowDisableAction, AllowDisableAlert, AllowDisableAllActions, AlertSound, MenuName, HomePageViewID,' + ' DefaultNetObjectID, DisableSessionTimeout, ReportFolder, AlertCategory, Expires, LastLogin, LimitationID1, LimitationID2, LimitationID3,' + ' AccountSID, AccountType, AllowViewCopCheck FROM Orion.Accounts' + ' ORDER BY LastLogin DESC' + ' WITH ROWS 1 TO 10 WITH TOTALROWS'; swisGetRESTrequest(query).then(function(response) { console.log('getSWISdata response:', response); var accountTotal = response.totalRows; var textOut = '<p>There are ' + accountTotal + ' accounts on the Orion server.</p>'; for(var i = 0; i < response.results.length; i++) { var result = response.results[i]; //console.log('result: ', result); var date = new Date(result.LastLogin); textOut = textOut + '<p>' + result.AccountID + ', is ' + ((result.Enabled == 'Y') ? 'enabled' : 'disabled') + ((date.getFullYear() > 1900) ? '. Last log in was ' + fixDate(date) : ', and has never logged in') + '.</p>'; } if (accountTotal > 0) { console.log('textOut: ' + textOut); sendAlexaReply(textOut, callback, true); } else { sendAlexaReply('Hmm. SWIS is talking, but I can\'t understand what it\'s saying.', callback, true); } }, function(error) { console.error('getSWISdata failure', error); sendAlexaReply('Oops, The SWISS call failed.', callback, true); }); } function sendAlexaReply(text, callback, endSesson) { var reply = { "version": "1.0", "response": { "outputSpeech": { "type": text.indexOf('</') == -1 ? "PlainText" : "SSML", }, "shouldEndSession": endSesson } }; if (text.indexOf('</') == -1) reply.response.outputSpeech.text = text; else reply.response.outputSpeech.ssml = "<speak>" + text + "</speak>"; callback(null, reply); } exports.handler = (event, context, callback) => { //const alexa = Alexa.handler(event, context); console.log('event: ' + JSON.stringify(event)); console.log('context: ' + JSON.stringify(context)); switch (event.request.type) { case 'IntentRequest': switch(event.request.intent.name) { case 'GetOrionAccounts': getOrionAccounts(callback); break; case 'SayOk': sendAlexaReply('No problem.', callback, true); break; case 'GetStatus': sendAlexaReply('Patrick. You have 126 active alerts.', callback, true); break; case 'AMAZON.HelpIntent': sendAlexaReply('You can ask me to help with status, geek tasks, or, you can say exit... ' + 'What can I help you with?', callback, false); break; case 'AMAZON.CancelIntent': case 'AMAZON.StopIntent': case 'SessionEndedRequest': sendAlexaReply('OK.', callback, true); break; default: console.log('action: unknown'); } break; case 'LaunchRequest': sendAlexaReply('You can ask me to help with status, geek tasks, or, you can say exit... ' + 'What can I help you with?', callback, false); break; default: sendAlexaReply('Sorry, I don\'t know what to do with request type "' + event.request.type + '".', callback, true); } };