As previously promised (although admittedly, a little late) I'll be walking you through the new Server-Sent Events (SSE) feature we're introducing in Prime Infrastructure 3.5. Please see the previous blog for an overview of SSE and why we're bringing it to Prime Infrastructure; but as a brief reminder, SSE allows you to subscribe to an event stream and receive notifications in your API client as the information in Prime Infrastructure is updated. While we can't promise that you'll receive events in real time, we can say that the event stream should be pretty snappy and a much faster way of staying up-to-date than periodically polling the API.
For those of you who want the details of how it all works, please continue reading. For those just looking for a piece of example client code, please skip to the bottom. With that bit of preamble out of the way, let's get started!
Subscribing to an SSE Stream
Subscribing to an SSE event stream is somewhat similar to querying a Prime Infrastructure API REST resource (like GET data/Devices or GET data/AccessPoints). SSE piggybacks off of HTTP and TCP, so establishing an SSE subscription starts with making an HTTP request and establishing a long-lived TCP connection. The URL will specify the particular SSE resource (and possibly an action type, more on that later). For example, to get events for CLI templates in JSON, the first line of our HTTP request to Prime Infrastructure will be
GET /webacs/api/v4/sse/CliTemplate.json HTTP/1.1
Notice that if we were request CLI templates from our standard REST API, the only difference in the URL would be that "sse" is replaced with "data". Just like with requests to our standard REST API, SSE subscriptions must be authenticated using basic authentication.
A complete SSE subscription request might look something like this
GET /webacs/api/v4/sse/CliTemplate.json HTTP/1.1
Host: szier-m8-106.cisco.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
User-Agent: foo/1.2.3
Accept: */*
Prime Infrastructure will then respond in kind. Assuming everything went well in establishing the subscription, the response headers will look something like
HTTP/1.1 200
Cache-Control: private
Vary: Accept-Encoding
Content-Type: text/event-stream
Transfer-Encoding: chunked
Note the 200 status code, the content type and transfer encoding headers, and the absence of a content length header. The content type of an SSE event stream will be, surprisingly enough, "text/event-stream". And the "chunked" transfer encoding signals your HTTP client to expect that the response payload will be delivered in chunks over time. From that point forward, the subscription is established and event stream data will be flowing over the connection.
The Event Stream
The first event you'll receive from Prime Infrastructure will be a greeting. SSE breaks down the event stream into discrete events, each with a specific type.
event: greeting
: Successfully subscribed to CliTemplate events
The greeting event lets you know that the subscription was successfully established. Periodically, Prime Infrastructure will send ping events. These ping events help Prime Infrastructure detect when your client is no longer there so that it can tear down unused subscriptions. Ping events also help ensure that your client, and any network devices between your client and Prime Infrastructure, treat the TCP connection as alive if there happen to be periods of time where there is no event traffic. Most SSE clients and libraries will automatically filter out greeting and ping events, but in case yours doesn't, here's what a ping event will look like.
event: ping
: ping
So, what happens if a user saves a change to a CLI template in Prime Infrastructure? Almost as soon as they click the save button, we'll receive an event! Note that the event stream covers events happening during the period of your subscription; if you need historic data it's available via our standard REST API.
event: event
data: {"streamResponse":{"@type":"CliTemplate","@requestUrl":"https://szier-m8-106.cisco.com/webacs/api/v4/sse/CliTemplate","@responseType":"listEvents","@rootUrl":"https://szier-m8-106.cisco.com/webacs/api/v4/sse","streamEvent":[{"@action":"UPDATED","@dtoType":"cliTemplateDTO","@eventTime":"2018-12-12T05:09:27.360Z","@id":"5959954","cliTemplateDTO":{"@displayName":"5959954","@id":5959954,"author":"root","configContainerId":5961956,"content":"show interfaces","createdOn":"2018-12-07T05:11:17.702Z","deployCount":0,"deployJobCount":0,"deviceType":"Routers","name":"Test","path":"My Templates/CLI Templates (User Defined)","templateId":5959954}}]}}
Because we opted to receive JSON, the payload of the event will be in JSON. The format should be pretty familiar for you if you've used our REST API. Each event in the stream will be returned in a "streamResponse" envelope (just like the "queryResponse" envelope for our REST API). Inside that envelope there will be a "streamEvent" list. The "action" attribute lets you know if this is a new template, an updated one, or if a template was deleted. Inside the streamEvent, we get to structure that's specific to our subscription type; if we subscribe to sse/CliTemplate we get cliTemplateDTO objects, if we subscribe to sse/Alarms we get alarmsDTO objects. Just as with our REST API, we document the structure, and each parameter via our built-in documentation.
Had we asked for XML when we subscribed, we might receive an event that looked like this.
event: event
data: <?xml version="1.0" ?><streamResponse type="CliTemplate" responseType="listEvents" requestUrl="https://szier-m8-106.cisco.com/webacs/api/v4/sse/CliTemplate" rootUrl="https://szier-m8-106.cisco.com/webacs/api/v4/sse"><streamEvent dtoType="cliTemplateDTO" id="5959954" action="UPDATED" eventTime="2018-12-11T22:06:11.177-08:00"><cliTemplateDTO displayName="5959954" id="5959954"><author>root</author><configContainerId>5961956</configContainerId><content>show interfaces</content><createdOn>2018-12-06T21:11:17.702-08:00</createdOn><deployCount>0</deployCount><deployJobCount>0</deployJobCount><deviceType>Routers</deviceType><name>Test</name><path>My Templates/CLI Templates (User Defined)</path><templateId>5959954</templateId></cliTemplateDTO></streamEvent></streamResponse>
Action Types and Filtering
Because of the performance requirements and overhead of maintaining SSE subscriptions, we support only limited filtering based on action types for SSE subscriptions. There are three action types
When subscribing to an SSE resource, you can subscribe to the particular action type of interest to you. For example, to see only new CLI templates in JSON, you'd open your request to "/webacs/api/v4/sse/CliTemplate/CREATED.json". Note that for DELETED events, we generally will no longer have access to the details of the object that was deleted, so expect that the "streamEvent" won't contain much more than the ID of of the deleted object. For example
event: event
data: {"streamResponse":{"@type":"CliTemplate","@requestUrl":"https://szier-m8-106.cisco.com/webacs/api/v4/sse/CliTemplate","@responseType":"listEvents","@rootUrl":"https://szier-m8-106.cisco.com/webacs/api/v4/sse","streamEvent":[{"@action":"DELETED","@dtoType":"cliTemplateDTO","@eventTime":"2018-12-12T06:30:12.887Z","@id":"5959956"}]}}
Trying it Out!
If you have a Prime Infrastructure 3.5 server handy, there are a couple ways to quickly try our new SSE feature out.
With Your Browser
After logging into the Prime Infrastructure interface, you can simply open a new tab to an SSE resource.
With curl
A simple curl command is enough to start exploring our SSE functionality
$ curl -N -s -k 'https://user:pass@host/webacs/api/v4/sse/CliTemplate.json'
Note that in the above, -N disables output buffering so that events are printed immediately, -s turns off progress reporting so that the event stream is clear, and -k enables insecure mode which you should not use in a production environment. These options work well for version 7.50.3, but might require some adjustment for your environment.
SSE Client Example with Python
To help get you started on using our SSE feature, I wanted to give you a simple client script that I wrote in Python. Assuming you already have Python 2.7 and Pip in your environment, the first thing you'll need to do is download the required packages.
$ pip install requests
$ pip install sseclient-py
Requests is a very popular package for handling HTTP requests. The second package is, of course, our SSE client library. Next, copy the script into a file.
import requests
import sseclient
import sys
import json
#Configuration
user = 'USER'
password = 'PASS'
url = 'https://hostname/webacs/api/v4/sse/RESOURCETYPE.json'
print 'Establishing SSE connection...'
response = requests.get(
url,
auth = (user, password),
stream = True,
verify = False) #Set to True for production (only needed for self-signed certs)
print 'Creating SSE client...'
client = sseclient.SSEClient(response)
print 'SSE Event stream...'
sys.stdout.flush() #flush() to ensure output is emitted in a timely fashion
for event in client.events(): #execution blocks here until events are received
jsonData = json.loads(event.data) #load JSON payload from event stream
streamEvents = jsonData['streamResponse']['streamEvent']
for streamEvent in streamEvents:
print streamEvent
sys.stdout.flush()
And there you have it, by replacing the username, password, hostname, and resource type you have the start of a Python SSE client. From here, you can feed the data into a warehouse, setup your own SSE server to power a dashboard, or whatever else you can think of!
So Long for Now
This wraps up our preview of the API changes coming to Prime Infrastructure 3.5. As always, if you have any questions or feedback, please do let me know.