2

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?

9
  • This seems like a real kludge. Why not set up a syslog server that gets all the syslog messages, and deal with this on the syslog server?
    – Ron Maupin
    Commented Jan 31, 2017 at 15:35
  • That would normally be the case. However this is a special case - there's zero infrastructure on-prem so everything needs to go to an HTTP endpoint.
    – evilSnobu
    Commented Jan 31, 2017 at 15:49
  • You can have an off-site syslog server, too. If you can get to an HTTP server, you can get to a syslog server.
    – Ron Maupin
    Commented Jan 31, 2017 at 15:51
  • Yes, absolutely. What I'm trying to say is that in this particular scenario HTTP is all I have, can't talk to raw endpoints.
    – evilSnobu
    Commented Jan 31, 2017 at 16:08
  • I think you are going to need to explain. If the device can connect to an HTTP server, the device can connect to a syslog server, and it has the built-in ability to automatically send syslog data to the syslog server. You could even run a syslog server on the same server as the HTTP server. I really don't understand the need for what you are trying to do.
    – Ron Maupin
    Commented Jan 31, 2017 at 16:15

1 Answer 1

3

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

1
  • If you have a complaint for Cisco, you will need to address that directly to Cisco. Network Engineering is not a Cisco site.
    – Ron Maupin
    Commented Feb 3, 2017 at 15:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.