Cisco EEM – Passing Syslog Messages to a TCL Script in Cisco EEM

ciscocisco-eemsyslog

I'm on a Cisco 1811 with IOS 15.1(3)T – yes it's really old, but that probably doesn't matter here.

I have this TCL script that makes an HTTP POST to a destination and i want to pass in ANY Syslog message emitted by the system into the request body.

<< my own http_get implementation here >>

namespace import ::cisco::eem::*
namespace import ::cisco::lib::*

set url "http://some.url.out.there:8000"
set mystdin [gets stdin]
set res [http_get $url -query $mystdin]

puts "POSTed to url"

My Event Manager applet looks like this:

event manager applet TRIGGER
    event syslog occurs 1 pattern "%.*"
    action 2.0 policy sendevent.tcl $_syslog_msg

Now this works fine except that i'm not getting anything into $mystdin, so my request body is empty.

How do i pass in Event Manager's $_syslog_msg to the TCL script?
Maybe there's a way to query for "last Syslog event" from within the script without reading from stdin?

Best Answer

Alright, so

action policy POLICY_NAME $arg

doesn't pass arguments to the script - had to switch to

action cli command "tclsh flash:/script.tcl $arg"`

which does, HOWEVER...

EEM actions are unable to capture beyond a newline from a variable. I did find a way out though - trim the newline from $_syslog_msg (newline is the first char) and assign to a new variable.

Here's the end-to-end solution:

CISCO-1811#sh run | s event
event manager directory user policy "flash:/"
event manager directory user library "flash:/"
event manager directory user repository tftp://1.1.1.3/
event manager applet TRIGGER_ON_SYSLOG
  event syslog occurs 1 pattern "%.*"
  action 1.0 string trimleft "$_syslog_msg"
  action 2.0 cli command "enable"
  action 2.1 cli command "tclsh flash:/sendevent.tcl \"$_string_result\""

$_string_result is an EEM built-in variable that collects the output of string trimleft

From http://www.cisco.com/c/en/us/td/docs/ios-xml/ios/eem/command/eem-cr-book/eem-cr-a2.html:

Use the action string trimleft command to trim a string from the left end of another string. This command trims the characters specified by string2 from the left end of string1. By default, string2 corresponds to white space.

The table below shows the built-in variable in which the result of the action string trimleft command is stored.

Built-in Variable: $_string_result

Description: The result of the action string trimleft command is stored in this variable.

The above is actually incomplete/misleading/or Cisco has no idea how the language they picked works: string trimleft without a second argument trims space, tab, newline, and CR not just white space (See this: http://wiki.tcl.tk/10177).

sendevent.tcl

#...
# my own http_get implementation here
# ...

# My actual event code

set url "http://some.http.destination:8000"

# Event log message is passed in as "$argv 0".
# That's the first item in $argv (which is a list)
set rawmsg [lindex $argv 0]

# Strip quotes from syslog message
set cleanmsg [string map { "\"" "" } $rawmsg]

# Get the time stamp at source
set timestamp [clock format [clock seconds] -format "%Y-%m-%dT%H:%M:%S"]

set json "{
    \"RouterTimestamp\": \"$timestamp\",
    \"Message\": \"$cleanmsg\"
}\n"

# This makes a POST request. Yes a POST.
# Don't know, ask the TCL developers what's with the name :)
if {[catch {http_get $url -query $json} token]} {
    puts "HTTP POST request failed: $token"
} else {
    # Everything is fine
    puts "POST successful."
}

Result

POST / HTTP/1.1
Accept: */*
Host: some.http.destination
User-Agent: Snobu Speshul TCL HTTP/1.1 Client library // build 21
Connection: close
Content-Length: 151
Content-Type: application/json

{
    "RouterTimestamp": "2017-02-02T10:09:49",
    "Message": "*Feb  2 10:09:49.307: %CLEAR-5-COUNTERS: Clear counter on all interfaces by console"
}

GitHub repo here (code complete): https://github.com/snobu/cisco-syslog-over-http