Search the Community

Showing results for tags 'Groovy'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • LogicModule Exchange
    • LM Exchange
    • LM Staff Contributions
  • Product Announcements
    • LogicMonitor Notices
  • LogicMonitor Product Q&A
    • Feature Requests
    • Ask the Community
    • From the Front

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


About Me

Found 14 results

  1. I just cannot bring myself to paste the same complex code into multiple LogicModule scripts, leaving little land mines scattered randomly. I was working today on a general template for using the API from within LogicModules using code I found scattered around different modules (we keep backups of everything, making it somewhat easy to search for those). Just a few things I noticed: * all the code is different * nothing I found so far accounts for API rate limiting * various inefficiencies exist in at least some of what I found The correct solution to all of this is to make a library feature available so we can maintain Groovy functions and such in one place, calling them from LogicModule scripts. It is very sad to see how little re-use is possible within the framework at all levels, and this one is especially bad in terms of maintenance and things breaking easily when changes are made in the API backend.
  2. I recently published CGYKT4. In this DataSource, a groovy script checks for the presence of the custom.MonitoredServices property. If the value is "SYN_Windows_Basic_Services" then the script replaces that with a hard-coded set of standard services. If custom.MonitoredServices contains any comma-spearated list of services, then the script just uses those. Finally, if custom.MonitoredServices contains "SYN_Windows_Basic_Services" and a comma-separated list of services, then the standard list of services is added to the values from the property. Once the list of services is determined, the script checks if those services exist on the target device. If so, an instance is created and the script generates an alert if the service is not running.
  3. Most of the Linux environments I work with are RHEL or CentOS and our prompts look like: [username@hostname ~]$ I can't seem to find a prompt pattern that works for both the closing square bracket and the dollar sign when writing a script using the com.santaba.agent.groovyapi.expect.Expect package. No amount of escaping seems to work like '\\]\\$' or ']\\$'. I do need the multi-character prompt because the output I want to parse will include errant $, but not ]$. Multi-character prompts are generally not a problem because I often have to expect a sudo password prompt using a 'username:' pattern and this works without issue. Any ideas on how to tackle this?
  4. I took a working groovy script datasource and am now trying to adjust it to some needs we have. This data will end up giving us alert totals for each month so we can build reports. Any ideas? Here is what I have so far. import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Hex; import groovy.json.JsonSlurper; //define credentials and url def accessId = hostProps.get('lmaccess.id'); def accessKey = hostProps.get('lmaccess.key'); def account = hostProps.get('lmaccount'); def alertgroup = hostProps.get('lmaccess.group'); def collectionFailures = 0 def failures = [:] def client = new LogicMonitorRestClient(accessId, accessKey, account, this.&println) try { def alerts = client.get("/device/groups/" + alertgroup + "/alerts", fields: "severity", filter: "startEpoch>:1538370000,endEpoch<:1541048399,cleared:*") //warnings = alerts.findAll {it.severity == 2}.size() println "WarningCount: ${alerts.findAll {it.severity == 2}.size()}" println "ErrorCount: ${alerts.findAll { it.severity == 3 }.size()}" println "CriticalCount: ${alerts.findAll { it.severity == 4 }.size()}" println "TotalAlerts: ${alerts.size()}" } catch (Throwable e) { failures["alerts"] = e.toString() collectionFailures += 1 } // Do error reporting println "CollectionFailures:${collectionFailures}" failures.each{ query, exception -> println "Exception while querying $query:" println exception } return 0 ////////////////////// // HELPER FUNCTIONS // ////////////////////// class LogicMonitorRestClient { String userKey String userId String account int maxPages = 20 int itemsPerPage = 1000 def println LogicMonitorRestClient(userId, userKey, account, printFunction) { this.userId = userId this.userKey = userKey this.account = account this.println = printFunction } def generateHeaders(verb, path) { def headers = [:] def epoch = System.currentTimeMillis() def requestVars = verb + epoch + path // Calculate signature def hmac = Mac.getInstance('HmacSHA256') def secret = new SecretKeySpec(userKey.getBytes(), 'HmacSHA256') hmac.init(secret) // Sign the request def hmac_signed = Hex.encodeHexString(hmac.doFinal(requestVars.getBytes())) def signature = hmac_signed.bytes.encodeBase64() headers["Authorization"] = "LMv1 " + userId + ":" + signature + ":" + epoch headers["Content-Type"] = "application/json" return headers } def packParams(params) { def pairs = [] params.each{ k, v -> pairs << ("${k}=${v}")} return pairs.join("&") } // Non paginating, raw version of the get function def _rawGet(path, params) { def baseUrl = 'https://' + account + '.logicmonitor.com' + '/santaba/rest' + path def packedParams = "" if(params) { packedParams = "?"+packParams(params) } def query = baseUrl+packedParams def url = query.toURL() def response = url.getText(useCaches: true, allowUserInteraction: false, requestProperties: generateHeaders("GET", path)) return response } // Public interface for getting stuff. def get(Map args=[:], path) { def itemsReceived = [] def pageReads = 0 // Impose our own paging parameters. args.size = itemsPerPage args.offset = 0 while(true) { // Do da nastieh def response = new JsonSlurper().parseText(_rawGet(path, args)) if (response.errmsg == "OK") { // Catch individual items if (response.data.items == null) { return response.data } itemsReceived += response.data.items // Check if there are more items // if (response.data.total > itemsReceived.size()) // { args.offset = args.size + args.offset // } // else // { // break // we are done // } } else { // Throw an exception with whatever error message we got. throw new Exception(response.errmsg) } pageReads += 1 // Check that we don't exceed max pages. if (pageReads >= maxPages) { break } if (response.data.total > 0) { break } } return itemsReceived } } If I run the URL with the API creds in my test powershell script, it works perfectly. When I test it in LM as a datasource, I get the attached error.
  5. I have created a Groovy script based PropertySource, that uses a REST call to contact an external site using the included HTTP helper methods in order to fill some information fields. This works fine in itself. The issue I have is the need to set the proxy details on the object after it is instantiated in the script. As far as I know, the collector properties/settings are not exposed in any way to a scripted PropertySource, despite the script running locally on the collector. Nor does the HTTP connection use the existing proxy as set on the collector. Unless I am missing something, it seems exceptionally convoluted to get those details, and then propagate them into property fields for all devices on that collector. to then be able to reference them properly inside the script. Is there any way to call the equivalent of getHostProps(), but for the collector properties? This would also be useful in cases of say, independent customer sites that use separate API keys for the same script.
  6. I've setup a few custom properties that define if an Oracle database exists and the port it listens on in a key-value pair/JSON syntax. These properties are named in <ApplicationName>.databases format (e.g. app1.databases & app2.databases). Not wanting to repeat myself, I have a set of datasources that query the common Oracle datapoints, but I would like a way to combine all of these *.databases properties together without having to constantly update the datasources or a propertysource whenever I have a new application to monitor. It would be nice to have a method in the hostProps class that supports getting device properties using a wildcard, glob, or regular expression.
  7. Hello LogicMonitor Community ! Andrey here from LM's Monitoring Engineer Team. To supplement the suite of scripted LogicModules provided in the LM Support Center, my colleagues and I have created a public GitHub repository with many more examples. Have a look at the LogicMonitor GitHub Repo, where you'll find various recipes for solving common monitoring script problems. Both Groovy and PowerShell examples are included Feel free to comment below with requests and/or suggested improvements. Happy scripting !
  8. Although a TEST step button is available in service checks (external and internal) this only shows the request made and the response output windows. Neither type gives you any kind of output from the post-processing of the response i.e. JSONPATH results. For the Internal service check that has the scripted option (rather than settings) this is even more noticeable as there is no obvious way to put any println or debug in your response script to make sure it is doing the write thing or work out why it is not doing the right thing. The addition of a stdout/output window that shows response (or request) script output would be really helpful. Also possibly the addition of a Test Script button for the response script that would run the script and show output as per datasources would also be great. I ran these ideas past support and they suggested raising a feature request so I have. They did also suggest I could test my scripts (request or response) in the console debug window which is possible but not obvious ways to mock LMRequest/LMResponse objects so that the script can run the same way as it would normally as a service check. If there are examples or ways to do this then this may be a good subject to create a support page explaining how to do it.
  9. This is a PropertySource which 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);
  10. Hi all, I'm trying to find ideas, scripts, processes for a function our customer approached us with. In short, they want LM to place a call once per hour to the Interactive Voice Response application to ensure the IVR system answers the call, prompts are being displayed, and host database connection is established. My only is guess is to somehow create a Datasource that runs a script to interact with an API that somehow makes a phone call and then displays information as a result. I would like to know if this already part of LM or would need to be heavily developed in-house or is even remotely possible to implement. If you have had any success with this type of monitoring, I'd appreciate your insight. Thanks much
  11. Info: Groovy script is the base for 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 linux kernel) It is a request some years ago (seems so long though, which actually is +2 years) by a LogicMonitor's Customer: Obviously, this is not an official datasource crafted by the amazing Monitoring Engineering team of LogicMonitor, but patting my own back, it suffices to say that it serves the purpose for a better security connecting remotely which has been a common best-practice by anyone who enjoys text-based command 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 a security 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/device and a monitored device (both happen to be Ubuntu 16 although Windows can be used for the collector as well and on the other hand, definitely it can not be used for Windows target device, for the obvious reason). For my simple test, it is just to monitor a log file size which is a syslog. 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 secure place in collector server. You need to make sure the file 'dwelleth in the secret place of the most High and 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 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 ). Below are some screenshots from the implementation:
  12. I recently wrote a datasource that pulled an API and alerted when the return value was greater than 0 The problem I ran into is the API never returned a 0, instead it would return NaN. I worked around this issue by using Key = Value datapoints and a "if (strv.isEmpty) {" statement. Basically, if their is a value returned the output in the script will be "events=[returned value]" the same as most key=value datapoints. If the returned value is empty, the script will fill out the entire string returning "events=0" which puts a 0 in the datapoint and allows the alert to clear. This a nice workaround for a LogicMonitor Admin's bag of tricks. //Print KeyValue strv = response_obj['results']['2']; if ( strv.isEmpty() ) { println "events=0" } else { println "events=" + strv; } return(0);
  13. Singapore is an island nation located in Southeast Asia and also home of LogicMonitor Asia office. Singapore while it is relatively small in sizes and population, it is strategically located blooming South East Asian market. Singapore is well known as country with good infrastructure, modern government and multi cultural background. One good example of this is Singapore government have been publishing various information such as Economics, Society, Health, Environment and other data publicly through their own website (data.gov.sg). Example of published data include Singapore population, Birth Rate, GDP data, government expenditure, and even environmental related data. Some example of environmental related data that we can retrieved are Temperature, PSI Reading, and rainfall reading. Here is an example of PM2.5 data which is formatted in JSON. We can make a LogicMonitor datasource based on this which can monitor air quality in Singapore. These information can be retrieved using HTTP request and supplying API key as part of HTTP headers. The response data can be divided into two parts, the first part mainly listed down regions and second part of the response is the actual air quality data. We can use Groovy script for Active Discovery to parse each region and listed it as an individual instance in LogicMonitor. Here is the sample of Active Discovery script What this script will do is to send http request to api.data.gov.sg and look for region name in the response body and print region name to this format instance1_id##instance1_name. After all instances are defined we can use regular HTTP data collection method to properly collect relevant data and displayed them in LogicMonitor. Here is data collection part of this datasource This is how it looks like on LogicMonitor when data is being collected and from here we can easily create graphs which can be use to show history of Singapore air quality On top of PM2.5 reading, we can also create another datasource that show PSI reading and current temperatures. We can clone this datasource and make some modification to match with http output for PM2.5 and Temperature. The next logical step would be to create a dashboard that can show all these information at glance which would look great if we show it on large screen such as TV in the meeting room. This would be useful especially during hazy season where air quality can go up to dangerous level from time to time. Here is an example of how we display these information on large screen using dashboard We can also use the same datasource to create an alert when air quality drops to unhealthy range and then deliver to our HipChat room. This is just an example of how LogicMonitor can monitor more than just IT infrastructure. In our case this will help us decide where we should go for lunch and whether we should bring our N95 masks along.
  14. Accessing the Zendesk API with LogicMonitor. Details for the Zendesk API can be found from the below link https://developer.zendesk.com/rest_api/docs/core/introduction The below blog is not intended as a copy\paste Zendesk datasource but as instructions to create Groovy Script datasources based on custom criteria. Zendesk data can be imported and alerted on with LogicMonitor using various API methods. This post will focus on using Zendesk Views and the Zendesk query First we will focus on the View, specifically the count of tickets in the view. Zendesk views are a way to organize your tickets by grouping them into lists based on certain criteria. In my example I’ve created a view for tickets for my rated tickets within the last 7 days. The criteria can be anything you require. Zendesk has various json files already created for views. This example will use the count.json. Create a Zendesk view. Load the view in Zendesk and the view ID will be in the URL For example: https://logicmonitor.zendesk.com/agent/filters/90139988 90139988 is the view ID. Zendesk documentation on views can be viewed with the below link https://support.zendesk.com/hc/en-us/articles/203690806-Using-views-to-manage-ticket-workflow From the LogicMonitor side we can use a datasource with an embedded groovy script with the built-in json slurper to parse the data. More information on Groovy Script Datasources can be found in the below URLs https://www.logicmonitor.com/support/datasources/scripting-support/embedded-groovy-scripting https://www.logicmonitor.com/support/datasources/groovy-support/how-to-debug-your-groovy-script/ Create a new Groovy Script Datasource and be sure to import the Jason slurper and http API by adding the below 3 lines to the top of the script. // import the logicmonitor http and JsonSlurp class import com.santaba.agent.groovyapi.http.*; import groovy.json.JsonSlurper; The URL and authentication information to retrieve the view’s count json is defined as url=’https://logicmonitor.zendesk.com/api/v2/views/90139988/count.json’ Authentication is using a Zendesk token instead of a password by appending the ‘/token’ string to the user ID user = 'jeff.woeber@logicmonitor.com' + '/token' pwd = '##ZENDESK TOKEN##' Next use the groovyapi HTTP // get data from the ZenDesk API http_body = HTTP.body(url, user, pwd); This will return the count json file which should look similar to the below view_count=[url:https://logicmonitor.zendesk.com/api/v2/views/101684888/count.json, view_id:101684888, value:45, pretty:45, fresh:false] Value=45 is the count data, so we need to parse the Value data You can print the json to output in the !groovy debug window by using the below code // Debug - print json slurp to identify keyvalues // iterate over the response object, assigning key-value pairs response_obj.each() { key, value -> // print each key-value pair println key + "=" + value; } This can be done by using the json slurper // user groovy slurper json_slurper = new JsonSlurper(); response_obj = json_slurper.parseText(http_body); LogicMonitor can use multi-line key value pairs, so parse so adding in a key of “TicketCount=” to the ouput will make it easy to add a datapoint for the count value. Do this by printing to the output // Print key Value pair for Logicmonitor Datapoint println "TicketCount=" + response_obj.view_count.value In your datasource you can add a new datapoint *Content the script writes to the standard output Inturpret output with - Multi-line Key Value Pairs Key = TicketCount LogicMonitor will reconize the TicketCount as a datapoint and alert thresholds can be set accordingly. In the attached Example Zendesk_TixCnt.xml The zendesk specific values have been tokenized to be added on the device level were the datasource will be applied. Tokens required on the device are ZEN.ACCOUNT - i.e. logicmonitor ZEN.EMAIL - i.e. Jeff.Woeber@LogicMonitor.com ZEN.TOKEN - Token for authentication ZEN.VIEW - view ID if using a view (This can be found in the URL while viewing the view) The second example uses the search API to query for tickets with a specified status for the last 48 hours. When building the URL it’s important to remember spaces and special characters are not allowed. Use Encoded Characters instead http://www.w3schools.com/tags/ref_urlencode.asp an example for solved tickets in the last 48 hours query=type:ticket status:solved created>48hours The URL after including encoded Characters. url = "https://logicmonitor.zendesk.com/api/v2/search.json?query=tickets%20status:solved%20created%3E48hours" The output will look similar to ~brand_id:854608, allow_channelback:false, result_type:ticket]] facets=null next_page=null previous_page=null count=29 We only need the count. Using the same key value output as the previous example we can add a key for "Solved" println "solved=" + response_obj.count.value In the attached example ZenDesk_TixStatus this process is repeated for Created, Open, Solved, Pending, On-Hold, and closed. Zendesk_TixStatus.xml Zendesk_TixCount.xml