SMTP connection status alerts

Darth Jed
Tera Guru

I have a problem that I'm trying to solve and I'll be surprised if no one else has experienced this problem. We have emails from our instance being routed through a relay so they appear come from a mailbox on our domain. When something changes on our network that inadvertently causes the SMTP connection to fail, ServiceNow quietly stops sending mail and the only indicator of trouble is the lack of email. Yes, I know there is a gauge that displays connection status, but that is an on demand check, not any sort of real time monitoring of the connection that will alert the admins wherever we happen to be in the instance.

 

I would like to incorporate some sort of popup alert message that will notify any admins (that are logged in) about the SMTP connection being down. I've been reading and building prototypes for a few days now and have parts of the desired result working, but I can't tie everything together due to the limitations of the system. I feel like I'm missing something and I'd like to call on all of you scripting gurus (Mark Stanger (Crossfuze) Travis Toulson Geoff Cox Jim Coyne (CompuCom) Jim Pisello (Crossfuze) out there to see what you might have for solutions.

 

My goals:

1. The alert needs to reach an admin wherever they are in the system.

2. The alert should not put a load on the system for the rest of the users.

3. The alert should be triggered when the glide.smtp.status property displays 'Cannot connect' in the value. I have a scheduled job that detects the property value easily enough.

4. I like the idea of the alert being able to be acknowledged so it doesn't harass the admins until the connection is restored.

 

My attempts so far have used a variety of solutions others have provided on the community or the ServiceNowGuru site.

 

Does anyone have a solution they have developed for this problem or have an idea of how I could pull this together?

1 ACCEPTED SOLUTION

Jim Coyne
Kilo Patron

Here's my attempt.   It's actually fairly simple and heavily based on Mark Stanger (Crossfuze)'s "Outlook-style Module Counts" ServiceNowGuru article - http://www.servicenowguru.com/system-ui/outlook-style-module-counts/.   In fact, I used it to generate the Module part of the solution.


SMTP_00.png


SMTP_01.png



There are 3 parts to it:



1. UI Script:


Name: Custom - SMTP Server Problem Warning


Active: Checked


Global: Checked


Script:


if (window.name == "") addLoadEvent(u_smtpServerProblemWarning);



function u_smtpServerProblemWarning(message){


  if (message != undefined) {


  //add a warning about the SMTP server


  var options = {}; //create a new object to store all of the message attributes


  options.type = "error";


  options.text = "<span style='font-weight:bold;color:red;'>" + message + "</span>";


  options.sticky = true;


  new NotificationMessage(options);


  }


}



This will display the UI Notification message.   I was going to make it a little more generic, maybe another day...



2. Script Include:


Name: u_RecordCount


Active: Checked


Client callable: Checked


Script:


var u_RecordCount = Class.create();


u_RecordCount.prototype = Object.extendsObject(AbstractAjaxProcessor, {


  recordCount: function() {


  //Perform a query based on the table and query passed in


  var result = 0;


  var count = new GlideAggregate(this.getParameter("sys_table"));


  count.addAggregate("COUNT");


  count.addEncodedQuery(this.getParameter("sys_query"));


  count.query();


  if (count.next()) {


  result = count.getAggregate("COUNT");


  }


  return result;


  }


});



This is used to return the number of records in a particular query, used by the Module below.   It is based on the ModuleCount Script Include from Mark's article, but uses GlideAggregate instead.



3. Module:


Title: Email - SMTP Status


Order: 200


Application menu: System Properties


Link type: HTML (from Arguments)


Active: Checked


Arguments:


<img src='/images/send.gifx' oncontextmenu='return showModuleContext(event, "8867cceddc6e2100dc62facef0cff51c")'/><span id='modCount8867cceddc6e2100dc62facef0cff51c'></span>


<script>


refreshNumber8867cceddc6e2100dc62facef0cff51c();


function refreshNumber8867cceddc6e2100dc62facef0cff51c() {


    //Set the module title and link


    var mod8867cceddc6e2100dc62facef0cff51c = gel('modCount8867cceddc6e2100dc62facef0cff51c');


    //Only construct the link if it does not exist yet


    if(!gel('8867cceddc6e2100dc62facef0cff51clink')){


          var menu_link = document.createElement('a');


          menu_link.id = '8867cceddc6e2100dc62facef0cff51clink';


          menu_link.innerHTML = "Email - SMTP Status";


          menu_link.title = "";


          menu_link.href = 'sys_properties_list.do?sysparm_userpref_module=8867cceddc6e2100dc62facef0cff51c&amp;sysparm_query=name=glide.smtp.status%5eEQ%5e&amp;sysparm_view=';


          menu_link.target = 'gsft_main';


          menu_link.className ='menu';


          menu_link.onclick = function() {refreshNumber8867cceddc6e2100dc62facef0cff51c()};


          mod8867cceddc6e2100dc62facef0cff51c.appendChild(menu_link);


    }



  //Query for the count of records for the module


    var count = new GlideAjax('u_RecordCount');


    count.addParam('sysparm_name', 'recordCount');


    count.addParam('sys_table', 'sys_properties');


    count.addParam('sys_query', decodeURI('nameSTARTSWITHglide.smtp.status%5evalue=Cannot%20connect%5eEQ'));


    count.getXML(u_displayModuleCount8867cceddc6e2100dc62facef0cff51c);


}



function u_displayModuleCount8867cceddc6e2100dc62facef0cff51c(response){


    var answer = response.responseXML.documentElement.getAttribute('answer');


    var menu_link = gel('8867cceddc6e2100dc62facef0cff51clink');


    if(answer > 0){


    menu_link.innerHTML = "&#60;font color='Red'&#62;Email - SMTP Status - Problem&#60;/font&#62;";


    top.u_smtpServerProblemWarning("Problem detected with the SMTP Server");


    }


    else {


    menu_link.innerHTML = "&#60;font color='Green'&#62;Email - SMTP Status - OK&#60;/font&#62;";


    }


    window.setTimeout('refreshNumber8867cceddc6e2100dc62facef0cff51c()', 900000);


}


</script>



The code for the module was generated by Mark's solution and then I modified it in a couple places for it to work independently.



The basic idea is the Module is HTML with some script included.   The script calls the Script Include to see if there are any System Properties where the name is "glide.smtp.status" with a value of "Cannot connect".   If there is, the function in the UI Script is called to display the message to the admins.



The UI Script is loaded for everyone, but it does not really do anything.   It just makes the function available and is only called by the code in the Module.   Nice thing about that is the Module can be protected by roles so it will only be loaded for users with the "admin" role.   I had to load the function via the UI Script becuase the UI Notification would not work from within the Navigator.



The code in the module will run on initial login, whenever the Navigator is refreshed and every 15 minutes.   You can change that on the window.setTimeout line of the module, right at the bottom (in milliseconds).



If the AJAX call returns a value > 0, the module title is changed and the message is displayed.



The only thing that I did not address is the "doesn't harass the admins" part.   Clicking on the module will show the system property - I guess you could change the text slightly so the messages are not continuously triggered.   That's what I've been doing while testing.   I see you had something where you would check records to see if the admin acknowledged the message already.   Problem with that is the records would have to be cleared for them to get notified in case of a future issue.



The idea is actually kinda neat, BUT the big problem is there would have to be at least 1 admin logged in to see the message.   A better solution would be to use a Web Service to alert the admins independently of the app.   Maybe fire off a text message or an email somehow using some external means.



Works fine in Dublin, seems to work OK in Eureka (but you might want to remove the image at the top of the HTML for the module if using UI14).   Have not tested in other versions.



I've attached XML files for the 3 records.


View solution in original post

10 REPLIES 10

Darth Jed
Tera Guru

These are the links to the examples that I've based my scripts on.


Global UI Script


http://www.servicenowguru.com/scripting/business-rules-scripting/display-messages-ui-notification/



These are the scripts that I've been using. The UI page and UI script below work by themselves, but are triggered when any page is displayed.


UI Page


HTML field:


<g:ui_table>


<g:evaluate var="jvar_user_sys_id" expression="RP.getWindowProperties().get('user_sys_id')" />


<tr class="header">


  <td class="column_head">


      <h2>SMTP error</h2>


  </td>


</tr>


<tr>


  <td>


        <p>The connection to sn.kcsouthern.com is down. Contact Networking.</p>


  </td>


</tr>


<tr><td>


<!-- twin SELECTS (what does that mean?)-->


<div align="center">


<g:dialog_button onclick="return onSubmit();" name="ok_button" id="map_button">${gs.getMessage('OK')}</g:dialog_button>


<g:dialog_button onclick="cancel();" name="cancel_button" id="cancel_button">${gs.getMessage('Cancel')}</g:dialog_button>


<input id="user_sys_id" type="hidden" value="${jvar_user_sys_id}"></input>


</div>


</td></tr>


</g:ui_table>



Client script


function onSubmit() {


      var user_id = document.getElementById("user_sys_id").value;


      var pref = new GlideRecord('sys_user_preference');


      pref.initialize();


      pref.name = 'splash_viewed';


      pref.user = user_id;


      pref.value = "true";


      pref.insert();


      GlideDialogWindow.get().destroy();


      return true;


}



function cancel() {


    GlideDialogWindow.get().destroy();


}



UI Script


window.setTimeout(smtp_splash,2000);


function smtp_splash() {


  var uids = [];


  var sys_ids = [];


  var i = 0;


  var members = new GlideRecord('sys_user_grmember');


      members.addQuery('group','adc37984ed88b000fba4666d7b351b2a');


      members.addQuery('user.active','true');


      members.query(); //Find all SN Admins group members.



  while (members.next()) {


  sys_ids[i] = members.user.toString();


  i++;


  }


  //Find all group members' user names.


  var users = new GlideRecord('sys_user');


  users.addQuery('sys_id','IN',sys_ids);


  users.query();


  var j = 0;


  while (users.next()) {


  uids[j] = users.user_name.toString();


  j++;


  }


  var session = new GlideRecord('v_user_session');


  session.addQuery('user','IN',uids.toString());


  session.query(); //Find all logged in sessions for each SN Admin user


  var k = 0;


  while (session.next()) {


  var pref = new GlideRecord('sys_user_preference');


  pref.addQuery('user',sys_ids[k]);


              pref.addQuery('name','splash_viewed');


              pref.query();


  if (pref.next()) {


  k++;


  return;   //if there is a splash viewed preference record for the user, break.


  }


  var user_id = sys_ids[k];


  var gDialog = new GlideDialogWindow("Open Issue");


  gDialog.setTitle('Open Issue');


  gDialog.setPreference('table','Splash_Screen');


  gDialog.setPreference('user_sys_id',user_id + '' || '');


  gDialog.render();


  k++;


  }


}



The job below will trigger the business rule following it, but the UINotification is not displayed. I trimmed out a lot of the extra code Jim Pisello used in his version since I don't need changed field details.


Scheduled job


Condition:


//If the SMTP status property displays Cannot connect, trigger the scheduled job script.


(function(){


  if (gs.getProperty(slide.smtp.status).search('Cannot connect') == 1) {


  return true;


  } else {


  return false;


  }


})();



Script:


var trigger = new GlideRecord('sys_properties');


trigger.addQuery('name','glide.smtp.status');


trigger.query();



Business rule (triggered on before query):


//Next, pass the values into the UI Notification we defined in the UI Script


var notification;


if(typeof UINotification != 'undefined') {


    notification = new UINotification('demo_notification'); //Calgary and later releases use the UINotification object call


}



var messageText = 'This is a test';


/*


Assign the message text to the 'text1' attribute. Additional attributes can be passed into the UI notification. Just be sure each one is accounted for in the UI Script via getAttribute() method calls.


*/


notification.setAttribute('text1', messageText);


notification.send();


smtp_splash();



UI Script:


//Ensure that the CustomEvent object exists


var intervalTest = setInterval(function(){


    if(typeof CustomEvent != 'undefined'){


          clearInterval(intervalTest);


          setCustomEvents();


    }


},500);




//Add the UI Notification to the DOM using the CustomEvent object


function setCustomEvents(){


    //Set the name of the notification


    var notifName = "demo_notification";


    CustomEvent.observe('glide:ui_notification.' + notifName,


    function(notification){


          var msgText = notification.getAttribute('text1'); //get the message text from the attribute passed in by the business rule


          var options = {}; //create a new object to store all of the message attributes.


          options.text = "<span style='color:red;'>" + msgText + "</span>";


          options.sticky = true;


          options.closeDelay = 5000;


          options.fadeIn = 500;


          options.fadeOut = 500;


          new NotificationMessage(options); //display the UI Notification


    });


}


Hi John,



I do not think this approach will work but more on that in a moment.   To have a truly global Notification, your best bet is probably to have a global UI Script that calls a Script Include over GlideAjax to check the property and pending the result, the UI Script will or won't display your custom alert.   You will want the UI Script to be quite efficient so it should run after all loaded, check for the frame in which it is executing (limit execution to one frame), check for admin role, then run the ajax.   I can give you further direction on this if need be.



Why UINotification will likely not work



UINotification appears to only work when run from a Business Rule executed by a User action.   When you call the send function of the UINotification, it adds a <span class="ui_notification"> tag to the next page being displayed.   The GlideUI object then fires notifications by collecting these tags.   The adding of the tag happens behind the curtain and I have not been able to figure out how exactly that works yet.   The important point is that it appears to be synchronous with a User action, meaning a Scheduled Job will not cause the trigger that you need.   Think of the UINotification call as acting in a similar way to g_scratchpad.



With that in mind, AJAX appears to be your next best option for a Global Notification.


Travis,


  Thank you for the help. The explanation about UINotifications makes sense. I knew I was trying something unusual and wasn't too surprised when it didn't work. I'll try the AJAX call and see if I can get that to work.


I've actually got something for you - I'm working on posting the solution shortly...