Windows Services Monitoring with quite a bit more Automation applied
So today we use LM's Microsoft Windows ServicesDataSource to monitor Windows Services. This DS uses Groovy Script and WMI calls under the hood to fetch the service metrics like state, start mode, status, etc... Everything works fine but one of the prerequisites is to go and manually populate the list of Windows services which then the DS parses out as a WILDVALUE variable in the script. You know, go to the device, click on Down Arrow (Manage Resource Options) --> Add Additional Monitoring --> and CHOOSE from the list of Windows Services. Rinse and Repeat and Save. Then the DS goes to work. Well, what if you have a list of over 100 Windows Services you need to add to let's say 20 Windows devices? That would take forever to populate that list manually... That's a problem number 1. Scratch that. This is not really a problem since one can run a PowerShell script (or Groovy Script) to perform this task using undocumented - but working very well - LM API calls. That problem is solved. Next - This list of over 100 Services needs to be *refreshed* every let's say 24 hours to remove nonexistent services and add new ones based on the Regex filter. That's a problem number 2. And again, one can do it programmatically running API calls but this is where I am trying to figure out how to do it. Run my script as a custom PropertySource? I am not really writing Resource Properties, I am updating instance list (Windows Services) within Additional Monitoring on bunch of Resources. Plus PropertySources are applied when ActiveDiscovery is run which is what, every 24 hours? Or should I write custom DataSource that would accomplish this refresh and specify 1 day collection period? Thanks.Solved700Views4likes2CommentsSSH key for remote session in LogicMonitor
Info: Groovy script is the basefor the environment and certainly the target/end device must be a linux-based, be it a distro of Linux server/desktop or an appliance with linux-kernel firmware (err...it's basically almost every device then: firewalls, switches, IDS/IPS, LB, Apple devices, Android devices, etc....it seems all gadgets on the planet using linuxkernel) It is a request someyears ago (seems so long though, which actually is +2 years) by a LogicMonitor's Customer: /topic/524-ssh-key-based-auth-for-groovy-scripts-datasources/ Obviously, this is not an official datasourcecrafted by the amazing Monitoring Engineering team of LogicMonitor, but patting my own back, it suffices to say that it serves the purpose fora better security connecting remotely which has been a common best-practice by anyone whoenjoystext-basedcommand line remote session. SSH Key is used over the option of sending password due to the apparent reason of information security, although one might argue that the ssh password will be only between a LogicMonitor collector and the endpoint, within a supposed-to-be internal network. Yet asecurity best practice may dictate such usage regardless of the network. Before progressing further, however, there is a catch in using ssh key, which is the necessity for a deployment of public key to each target device. Simply put, every time SSH Keys are used for remote session between two devices, there will be private key and public key used for authentication process, hence no password needed. These keys are to be put in the devices, private key in the source device where the remote session is originated and public key in the endpoint. Private key is not for a share whilst public key is for any device that the source will need to connect, should the ssh key be used. The only hassle, even if it is considered to be one, is to load that public key on each target device (if there are many). From the security standpoint, that is totally not an issue and rather a compulsory instead. (As a comparison, by using ssh user and password, the process would be similar too, that is to create user and password in each target device). This practice is really not an ancient stuff and almost every cloud provider,AWS being the one, has that feature recommended (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html). In LogicMonitor case where I did the test, it is between a collector server/deviceand a monitored device (both happen to be Ubuntu 16 although Windows can be used for the collectoras well and on the other hand, definitely it can not be used for Windows target device, for the obviousreason). For my simple test, it is just to monitor a log file size which is asyslog. One thing worth noting is, remote session type of monitoring will certainly consume more resource of collector, as a matter of fact, every datasource using script will do. Besides, using this method, the processing time seems to increase by a little bit, compared with user/password,but I have not done any thorough observations though (not only that I do not intend to, since this is just a test, nor have I the environment huge enough to do a high load-test). Security and processing speed, they do not go in parallel for sure, especially considering the recent havoc by a processor company caused a nightmare for information security worldwide, bypassing security measure for the sake of increasing a speed of data processing. So here is the script which is basically running a command to throw output of a data from a file named 'dusyslog' in the remote device and a datapoint will capture it (datapoint name: size): import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Properties; import com.jcraft.jsch.Channel; import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSch; import com.jcraft.jsch.Session; try{ String command = "cat dusyslog"; String host = hostProps.get("system.hostname"); String user = hostProps.get("ssh.user"); String key = hostProps.get("ssh.key"); JSch jsch = new JSch(); jsch.addIdentity(key); jsch.setConfig("StrictHostKeyChecking", "no"); Session session=jsch.getSession(user, host, 22); session.connect(); Channel channel = session.openChannel("exec"); channel.setCommand(command); channel.setErrStream(System.err); channel.connect(); InputStream input = channel.getInputStream(); channel.connect(); try{ InputStreamReader inputReader = new InputStreamReader(input); BufferedReader bufferedReader = new BufferedReader(inputReader); String line = null; while((line = bufferedReader.readLine()) != null){ println(line); } bufferedReader.close(); inputReader.close(); }catch(IOException err){ err.printStackTrace(); } channel.disconnect(); session.disconnect(); }catch(Exception err){ err.printStackTrace(); } The first thing you can notice is I am using : jsch.addIdentity(key) for adding the key file into the session for authentication. So what is this file? it is the private key file residing in a secureplace in collector server. You need to make sure the file 'dwelleth in the secret place of the most Highand shall abide under the shadow of the Almighty'.I really mean it that the private key should not be exposed to the world. But of course, I make it sound like a very serious security matter :)/emoticons/smile@2x.png 2x" title=":)" width="20">So just to make sure that file is given limited permission for collector service to access is sufficient. Undoubtedly the script is not built by myself from scratch but I have made some modification so it is safe to be copyrighted by me and you have the 'the right to copy' & enhance it if you need to and providing me a treat of coffee would be highly appreciated. Further to that, this part: key = hostProps.get("ssh.key"); as per normal, is defined at the device property and following is the sample from my test: Linux device: /security/key Windows device: C:\\security\\key Note: you can add an additional security to disguise the location of the file too and that folder "security" is not the original folder where the private key resides. This is for paranoid security practitioners. (But as I usually joke with friends in the IT field, the best data security was during WordStar era, before Windows 3.11 came and before tcp/ip was introduced to home users :)/emoticons/smile@2x.png 2x" title=":)" width="20">). Below are some screenshots from the implementation:499Views1like2CommentsUse a Property Source to find dead devices
This is a PropertySourcewhich runs with active discovery and adds the property auto.lmstatus to the device properties with the current Hoststatus value. It does this using the REST API and It works great with Dynamic grouping, for example if you wanted to know which device in your portal were currently in a dead status you could create a dynamic group with the applies to of “auto.lmstatus=="dead". One advantage to using a property source is if the device comes back on-line, Active Discovery will immediately run and change the property to "normal" removing the device from the group. A copy of the PropertySource is at the bottom of the post. Let’s walk through the groovy script. Define the account information This is polling the device properties. I recommend setting this at the group level so they are inherited to the devices. an example of the properties would be api.user =5kCPqLgY4DGYP27uw2hc api.pass =ye[$3y7)_4g6L6uH2TC72k{V6HBUf]Ys+9!vB)[9 *note, any property with .pass in the name will not have visible data. api.account = lmjeffwoeber //Account Info def accessId = hostProps.get("api.user"); def accessKey = hostProps.get("api.pass"); def account = hostProps.get("api.account"); Define the Query. We just need the HostSatatus for the device the script is running on. def queryParams = '?fields=hostStatus&filter=displayName:'+hostName; def resourcePath = "/device/devices" Next we build the URL for the API. def url = "https://" + account + ".logicmonitor.com" + "/santaba/rest" + resourcePath + queryParams; This next part builds the security and runs the API. It can pretty much be copy\pasted into any groovy scripts that use the REST API. //get current time epoch = System.currentTimeMillis(); //calculate signature requestVars = "GET" + epoch + resourcePath; hmac = Mac.getInstance("HmacSHA256"); secret = new SecretKeySpec(accessKey.getBytes(), "HmacSHA256"); hmac.init(secret); hmac_signed = Hex.encodeHexString(hmac.doFinal(requestVars.getBytes())); signature = hmac_signed.bytes.encodeBase64(); // HTTP Get CloseableHttpClient httpclient = HttpClients.createDefault(); httpGet = new HttpGet(url); httpGet.addHeader("Authorization" , "LMv1 " + accessId + ":" + signature + ":" + epoch); response = httpclient.execute(httpGet); responseBody = EntityUtils.toString(response.getEntity()); code = response.getStatusLine().getStatusCode(); The API will return a JSON payload. We use the Groovy Slurper to transfer the payload to respose_obj were we can use the data. // user groovy slurper json_slurper = new JsonSlurper(); response_obj = json_slurper.parseText(responseBody); They JSON will look like data=[total:1, items:[[hostStatus:dead]] We can use the value with response_obj.data.items[0].hostStatus.value Now we print the Key=Value for the property source //print output println "LMStatus=" +response_obj.data.items[0].hostStatus.value; httpclient.close(); This will add the property source "auto.lmstatus" to the device finally we return 0 do indicate a success. return (0); This is how the PropertySource will appear on the device Lastly, we can create Dynamic Groups based off the Hostatus. For example use an applies to auto.lmstatus=="dead" to group all of the dead devices into one group, or auto.lmstatus=~"dead" to also include Dead-Collector LMStatus PropertySource import org.apache.http.HttpEntity import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.client.methods.HttpGet import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.HttpClients import org.apache.http.util.EntityUtils import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Hex;import com.santaba.agent.groovyapi.http.*; import groovy.json.JsonSlurper; def hostName = hostProps.get("system.displayname"); //Account Info def accessId = hostProps.get("api.user"); def accessKey = hostProps.get("api.pass"); def account = hostProps.get("api.account"); data = '' def queryParams = '?fields=hostStatus&filter=displayName:'+hostName; def resourcePath = "/device/devices" def url = "https://" + account + ".logicmonitor.com" + "/santaba/rest" + resourcePath + queryParams; //get current time epoch = System.currentTimeMillis(); //calculate signature requestVars = "GET" + epoch + resourcePath; hmac = Mac.getInstance("HmacSHA256"); secret = new SecretKeySpec(accessKey.getBytes(), "HmacSHA256"); hmac.init(secret); hmac_signed = Hex.encodeHexString(hmac.doFinal(requestVars.getBytes())); signature = hmac_signed.bytes.encodeBase64(); // HTTP Get CloseableHttpClient httpclient = HttpClients.createDefault(); httpGet = new HttpGet(url); httpGet.addHeader("Authorization" , "LMv1 " + accessId + ":" + signature + ":" + epoch); response = httpclient.execute(httpGet); responseBody = EntityUtils.toString(response.getEntity()); code = response.getStatusLine().getStatusCode(); // user groovy slurper json_slurper = new JsonSlurper(); response_obj = json_slurper.parseText(responseBody); //print output println "LMStatus=" +response_obj.data.items[0].hostStatus.value; httpclient.close(); return (0);499Views0likes10CommentsDoes anyone have any experience with monitoring Windows Processes?
I’ve checked the community for datasources and I don’t see anything to what I’m specifically looking for. Our organization currently utilizes the Microsoft_Windows_Services datasource (modified a little bit for our specific needs) to monitor services. I’m looking for something similar to monitor windows processes. Similar to the Microsoft_Windows_Services datasource, what I am hoping to accomplish is provide a list of keywords that will either match or be contained in the process name that I want to monitor, provide a list of machines that I want to monitor those processes on, andthen get alerted on if those processes stop running. Some issues I am running into so far are: Win32_Process always returns a value of NULL for status and state. So I cannot monitor for those two class level properties. Powershell’s Get-Process does not return status or state, rather it just looks for processes that are actively running, so I would need to get creative in having LogicMonitor create the instance and what value to monitor in the instance. Some of the processes I want to monitorcreate multiple processes with the same name, and LogicMonitor then groups them all together into one instance, which makes monitoring diffucult. Some of the process I want to monitor are processes that only run if an application is manually launched, which means that again I will need to get creative in how I set up monitoring because I don’t want to get alerts when a process that I know shouldn’t be running is not running. Because the processes I am trying to monitor are not going to be common for everyone everywhere, something that other people could do to try to replicate my scenario would be: Open Chrome. When Chrome is launched, you will get a processed called “Chrome”. Now, open several other tabs of Chrome, you will just get more processes named “Chrome”. Now, keeping in mind the points I made earlier, set up monitoring to let you know when the 3rd tab in Chrome has been closed, even though the rest of the Chrome tabs arestill open. How would you break that down? My first thought would be to monitor the PIDs, however, when you reboot your machine, your PIDs will likely change. Also, I don’t want to have the datasource wild value search by PID, because that would get confusing really fast once you have 2 or 3 different PIDs that you want to monitor. All suggestions are welcome, and any help is greatly appreciated. Bonus points if you can get this to work with the discovery method as Script and you use an embedded Groovy or Powershell script.Solved400Views12likes19CommentsGroovyScriptHelper issues - method missing
I’m trying to build a datasource for our health-check API that returns a system name, status, and response time. I believe I need to set up active discovery to map the system names as the instance keys, then query the API again to get that data. Issue I’m running into is that the GSH methods intended to handle the instancing won’t load, giving me the below error. The actual script does run successfully, it’s just the instancing bit that’s giving me trouble. MissingMethodException: No signature of method: Script1.withBinding() is applicable for argument types: (groovy.lang.Binding) values: [groovy.lang.Binding@781fc692] Possible solutions: setBinding(groovy.lang.Binding), getBinding() com.logicmonitor.common.sse.utils.exception.ScriptExecutingFailedException: MissingMethodException: No signature of method: Script1.withBinding() is applicable for argument types: (groovy.lang.Binding) values: [groovy.lang.Binding@781fc692] Possible solutions: setBinding(groovy.lang.Binding), getBinding() at com.logicmonitor.common.sse.utils.GroovyScriptHelper.execute(GroovyScriptHelper.java:197) at jdk.internal.reflect.GeneratedMethodAccessor17.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at com.logicmonitor.common.sse.executor.impl.GroovyScriptHelperWrapper.execute(GroovyScriptHelperWrapper.java:86) at com.logicmonitor.common.sse.executor.GroovyScriptExecutor.execute(GroovyScriptExecutor.java:75) at com.logicmonitor.common.sse.SSEScriptExecutor$ScriptExecutingTask.call(SSEScriptExecutor.java:212) at com.logicmonitor.common.sse.SSEScriptExecutor$ScriptExecutingTask.call(SSEScriptExecutor.java:155) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at java.base/java.lang.Thread.run(Thread.java:829) elapsed time: 0 seconds Code-block I’m using for discovery: import com.santaba.agent.groovyapi.http.*; import groovy.json.JsonBuilder; import groovy.json.JsonSlurper; import com.logicmonitor.common.sse.utils.GroovyScriptHelper as GSH import com.logicmonitor.mod.Snippets def modLoader = GSH.getInstance()._getScript("Snippets", Snippets.getLoader()).withBinding(getBinding()) def lmEmit = modLoader.load("lm.emit", "0") // Load LM emit module def api_key = hostProps.get("boomi.client_id"); def api_secret = hostProps.get("boomi.client_secret"); def api_scope = hostProps.get("boomi.api_scope") if (!api_key && ! api_secret) { println "boomi.client_id and boomi.client_secret must be defined."; return 1; } // used to get an OAuth2 token def api_auth_host = "login.microsoftonline.com"; def api_tenant_id = hostProps.get("boomi.azure_tenant_id"); def api_auth_url = "https://login.microsoftonline.com/${api_tenant_id}/oauth2/v2.0/token" // define API path to get info from def api_base_host = hostProps.get("system.hostname"); def api_base_url = "https://${api_base_host}/healthcheck/v2"; def api_x_apikey = hostProps.get("boomi.apixkey"); // get OAuth2 token def auth_token; try { def conn = HTTP.open(api_auth_host, 443); def headers = [ "Content-Type": "application/x-www-form-urlencoded" ]; def postdata = "grant_type=client_credentials&client_id=${api_key}&client_secret=${api_secret}&scope=${api_scope}" def response = HTTPPost(api_auth_host, api_auth_url, postdata, headers); auth_token = ParseJsonResponse(response); } catch (Exception e) { println "OAuth2 Exception: " + e.message; return 1; } // get data from API using token def systems; try { def conn = HTTP.open(api_auth_host, 443); def headers = [ "Accept": "application/json", "Authorization": "${auth_token.token_type} ${auth_token.access_token}", "X-APIKey": api_x_apikey ]; def response = HTTPGet(api_base_host, api_base_url + "/oncloud", headers); systems = ParseJsonResponse(response); } catch (Exception e) { println "API Exception from oncloud: " + e.message; return 1; } systems?.each { val -> def wildvalue = val.systemName def wildalias = wildvalue def description = val.systemName lmEmit.instance(wildvalue, wildalias, description, [:]) } try { def conn = HTTP.open(api_auth_host, 443); def headers = [ "Accept": "application/json", "Authorization": "${auth_token.token_type} ${auth_token.access_token}", "X-APIKey": api_x_apikey ]; def response = HTTPGet(api_base_host, api_base_url + "/onpremise", headers); systems = ParseJsonResponse(response); } catch (Exception e) { println "API Exception from onpremise: " + e.message; return 1; } systems?.each { val -> def wildvalue = val.systemName def wildalias = wildvalue def description = val.systemName lmEmit.instance(wildvalue, wildalias, description, [:]) } return 0; // Functions // Convenience method for making HTTP POST requests to REST API def HTTPPost(host, url, post_data, request_headers, user = null, pass = null) { // Create an http client object def conn = HTTP.open(host, 443) // If we have credentials add them to the headers if (user && pass) { conn.setAuthentication(user, pass) } // Query the endpoint def response = conn.post(url, post_data, request_headers) def response_code = conn.getStatusCode() def response_body = conn.getResponseBody() // Close the connection conn.close(); return ; } // Convenience method for making HTTP POST requests to REST API def HTTPGet(host, url, request_headers, user = null, pass = null) { // Create an http client object def conn = HTTP.open(host, 443) // If we have credentials add them to the headers if (user && pass) { conn.setAuthentication(user, pass) } // Query the endpoint def response = conn.get(url, request_headers) def response_code = conn.getStatusCode() def response_body = conn.getResponseBody() // Close the connection conn.close(); return ; } // Convenience method for parsing REST responses. def ParseJsonResponse(response) { // Successful response? if (response.code =~ /^2\d\d/) { // yes, parse out a JSON object return new JsonSlurper()?.parseText(response.body); } else { // No, error out throw new Exception("There was an error fetching from the API: ${response.code}"); } }Solved300Views2likes2CommentsUsing groovy script to send email (inside a DS or CS)?
We use a resource associated with a specific collector to run scripts using config sources as the mechanism. One of the scripts I am writing now posts to a “UserDisable_Warning” config source a list of users whose suspension is imminent. I would like to use the config source to also send an email to the user, but I’m worried I’m going to have to do a bunch of crazy stuff to set up things on the collector. But I’d rather not use the collector’s exchange to send email, because I dont have direct access to it (go figure) and some of the datasources I’ve seen look like a serious pain to use (Email_TransitTime, for example) as a template. I was wondering if anyone knows a simpler way to send email. When a resource has an alert with an escalation chain that contains an email, I was under the assumption that no exchange server or imap or anything else has to be set up on the collector. Or so I thought. So I’m kinda hoping there is some easy way to avoid having to set up mail stuff on a collector, and just have that collector, via a groovy script, send an email out somehow. Like in my wildest dreams I would import something, and then do java.pipedream.Email.send (“blah@foo.blah”,”LM expiration for blah@foo.blah”,”Dear Blah,\nYour LM account expires in X days.\n\n--MyCompany”). Or at least something not reliant on setting up email servers or clients or etc on the collector. Maybe I’m completely off in the wrong direction. What would be the easiest way to have groovy send an email, preferably with me not having a lot of control of my own collector (even though I’m portal admin; work silos, know what I’m sayin’? :) )? Thanks!300Views3likes7Commentsstring filters in the API (groovy)
In the past I have not had much need of filters in API, and on the rare occasion I have, it has been numeric filters. But for some reason I cannot get string filters to work at all without throwing errors, either 400 responses, or else unexpected character errors. I can do this all day and get results: queryParams = '?offset=' + offset.toString() + '&size=1000&filter=id:1256'; But as soon as I try to make my filter a string filter, I get errors, and I’ve seen both of the following used in various LM docs online, as if they would work, but I just get unexpected string errors: queryParams = '?offset=' + offset.toString() + '&size=1000&filter=status:active'; queryParams = '?offset=' + offset.toString() + '&size=1000&filter=status:”active”'; Basically, I’m pretty sure I’m just not passing in the string value I want for status in a correct way. I’ve literally seen LM documentation of status:active and I’ve seen other examples when they do name~”whatever” but both of these throw errors. I can even test for status being a number (which obviously give no results), and with no filter, I get back everything (I’m doing setting/admins). And I’ve tried every permutation of quotes in double quotes or backslashed quotes or double and triple quotes and double quotes. Because it something NEWBIE-ish I’m doing wrong. When building a url for the API in groovy, is there some specific way to quote up a string value on a filter? //build the request URL resourcePath = "/setting/admins"; //queryParams = "?size=1000&offset=" + offset.toString(); //queryParams = '?offset=' + offset.toString() + '&size=1000&filter=id:1256'; //queryParams = '?offset=' + offset.toString() + '&size=1000&filter=status:1256'; queryParams = '?offset=' + offset.toString() + '&size=1000&filter=status:active'; queryParams = '?offset=' + offset.toString() + '&size=1000&filter=status:”active”'; url = "https://" + account +".logicmonitor.com/santaba/rest" + resourcePath + queryParams; The first 3 commented items work fine (tho the 3rd one has no results obviously because 1256 isnt a valid status) but as soon as I try status:active or status:”active” its game over. What newbie thing am I doing wrong? Thanks in advance. Cheers!Solved300Views2likes7CommentsTesting groovy script on local ubuntu machine
I am searching for a way to run groovy scripts on my local ubuntu (WSL) while being able to work with the same classes that are used by the collectors. To achieve this I installed a java+groovy and deployed a collector. However I am unable to import the classes like“com.santaba.agent.groovyapi.snmp.Snmp” Is there any way to achieve this? Or can I somehow locally access the collector shell to test groovy code without the need to use the LM web UI? So, I am basically searching for a way to develop scripted DataSources using CLI, without fiddling with the web UI. Any guidance very much appreciated.Solved208Views18likes9CommentsScripted Alert Thresholds
It should be possible to groovyscript Alert Thresholds, based on (for example) ILPs and hostProperties. I need to modify the SNMP_Network_Interfaces to vary the InDiscardPercent threshold depending on whether this is a radioMAC interface type and whether it is a given customer. Something along the lines of: def isRadio = instanceProps.get('auto.interface.type') == 'radioMAC'; def customerCode = hostProps.get('customer.code'); if(isRadio && customerCode == 'ACME') { // No threshold return ''; } // The default return '> 10';Solved200Views17likes26Comments