Zenoss got a new dashboard with Zenoss 5. You can also get the same new dashboard in Zenoss 4 by installing the Dashboard (ZenPacks.zenoss.Dashboard) ZenPack. While the new dashboard looks similar to the old at first, it adds some frequently-requested features.
- Multiple dashboards.
- Dashboards private to individual users. (as before)
- Dashboards shared with specified user groups.
- Dashboards available to all Zenoss users.
- Popular Site Window and HTML portlets come standard.
- New standard portlets such as Event View and Network Map.
- JavaScript API for developing custom portlets.
All dashboard functionlity is easily accessible to Zenoss administrators and users through the web interface except for developing custom portlets.
The focus of this post will be on using the Zenoss JSON API and ZenPacks to manage dashboards. By following the steps outlined in this post you will be able to create scripts to automatically create, modify and remove dashboards. You'll also learn how to build a custom ZenPack that adds a new dashboard.
Basics of the Dashboard API
Before we get started into examples, it'll be useful to understand the basics of the dashboard API. Specifically what dashboards and portlets are, and what properties they can have.
What is a Dashboard?
The Dashboard could be what you call the screen that appears after you login to the Zenoss web interface. A dashboard is one of the multiple dashboards that can be chosen from the drop-down in the top-right of The Dashboard. Normally there's a single dashboard available to all Zenoss users that is appropriately named default.
A dashboard has the following properties.
- name (id)This is the name of the dashboard. This name must be unique among all other dashboards. As with all Zenoss ids, only the characters within the following brackets are allowed:Â
a-zA-Z0-9-_,.$()
- uidThe dashboard's unique identifier. All Zenoss objects have a UID. The UID for a dashboard available to all users would look like the following:
/zport/dmd/ZenUsers/
dashboards/my-dashboard-id The UID for a dashboard available only to a specific user or user group would look like the following:
/zport/dmd/ZenUsers/username/
dashboards/my-dashboard-id - ownerOwner isn't really a property of a dashboard, but it can help to think of it that way. The owner of a dashboard comes down to its UID as described above.
- columnsThe number of columns in a dashboard. Usually from 1 to 3 unless the dashboard will be displayed on an extra wide display.
- stateState is a JSON-serialized string that describes the location and configuration of all portlets on the dashboard.
What is a Portlet?
A portlet is an instance of one of the available portlet types (see Appendix A: Portlet Types) that has been added to a dashboard's state. For example, a user could have access to several different dashboards. Each would have a different set of portlets and configurations for those portlets. The same portlet type, such as Watch List could even exist multiple times on the same dashboard with different configurations.
All portlets have the following properties.
- titleThe title shown on the top title bar of the portlet on the dashboard.
- xtypeThe xtype of one of the portlet types outlined in Appendix A: Portlet Types, or the xtype of a custom portlet type added by another ZenPack. This is what defines what kind of portlet it will be.
- heightHow many pixels tall the portlet will be.
- refreshIntervalHow often the portlet's onRefresh function will be called. Typically this causes the data in the portlet to be refreshed.
Managing Dashboards with the JSON API
Now that we know what dashboards and portlets are, let's walk through some examples of using the Zenoss JSON API to work with them. We'll be using a common command line utility called curl.
Listing Available Portlets (JSON)
The following curl command will list all dashboards currently available for the user provided. The cat << EOF
 trick is useful because it allows us to format the request body more naturally as indented JSON.
cat << EOF | curl -k -u "zenoss:Zenoss123" -H "Content-Type: application/json" https://zenoss5.example.com/zport/dmd/dashboard_router -d @- | python -m json.tool { "action": "DashboardRouter", "method": "getAvailableDashboards", "tid": 1 } EOF
You should see output similar to the following if you haven't made any changes to the default dashboard, and you haven't added any new dashboards. Note that result contains dataÂ
{ "action": "DashboardRouter", "method": "getAvailableDashboards", "result": { "data": [ { "columns": 2, "contextType": "global", "contextUid": "/zport/dmd/ZenUsers", "description": "", "id": "default", "inspector_type": "ZenModelRM", "locked": false, "meta_type": "ZenModelRM", "name": "", "owner": "admin", "state": "[{"id":"col-0","items":[{"title":"Welcome to Zenoss!","refreshInterval": 3000,"config":{"siteUrl": "https://www2.zenoss.com/in- app-welcome?v=4.9.70&p=core"} ,"xtype":"sitewindowportlet ","height":399,"collapsed ":false},{"title":"Google Maps","refreshInterval": 300,"config":{"baselocation ":"/zport/dmd/Locations"," pollingrate":400},"xtype": "googlemapportlet","height" :400,"collapsed":false}]},{ "id":"col-1","items":[{" title":"Open Events","refreshInterval": 300,"config":{"stateId":" ext-gen1351"},"xtype":"eve ntviewportlet","height": 400,"collapsed":false},{"ti tle":"Open Events Chart","refreshInterval": 300,"config":{"eventClass" :"/","summaryFilter":"", "daysPast":3},"xtype":"op eneventsportlet","height": 400,"collapsed":false}]}]", "uid": "/zport/dmd/ZenUsers/ dashboards/default" } ], "success": true }, "tid": 1, "type": "rpc", "uuid": "c3fb5955-6ea5-4ed5-a45a- 843fed364000" }
Adding a New Dashboard (JSON)
Now let's add a new dashboard with our desired portlet configuration using the JSON API. This is a bit more difficult because we have to include that JSON-serialized state data above into our request. Putting JSON within JSON leads to escaping issues. So we'll start by just getting the state string we need into an environment variable named STATE.
The apostrophes/single-quotes (') around STATE's value, and the backslashes before all of the inner quotes (") are required to get around shell escaping issues. This is a bit more difficult on the command line than it would be in a proper programming language.
STATE=' [{ "id": "col-0", "items": [{ "title": "Event View", "xtype": "eventviewportlet", "height": 400, "refreshInterval": 300 },{ "title": "HTML", "xtype": "htmlportlet", "height": 100, "refreshInterval": 300, "config": { "html": "<h1>Some HTML..</h1>" } }] },{ "id": "col-1", "items": [{ "title": "Site Window", "xtype": "sitewindowportlet", "height": 400, "refreshInterval": 300, "config": { "siteUrl": "https://www.zenoss.com/" } }] }] '
Now we can add a dashboard using this state. Note that uid is set to /zport/dmd/ZenUsers. This is what uid should be set to if you want to make the portlet available to all users.
cat << EOF | curl -k -u "zenoss:Zenoss123" -H "Content-Type: application/json" https://zenoss5.example.com/zport/dmd/dashboard_router -d @- #| python -m json.tool { "action": "DashboardRouter", "method": "addDashboard", "data": [{ "newId": "json-example", "uid": "/zport/dmd/ZenUsers", "columns": 2, "state": "$STATE" }], "tid": 1 } EOF
Managing Dashboards with ZenDMD
It's also possible to manage dashboards more directly using zendmd if you have direct access to the Zenoss server. Let's try listing all available portlets, and adding a portlet using zendmd.
If you're using Zenoss 5 you should first attach to the zenhub container by running the following command on your serviced master host.
serviced service attach zenhub
Then regardless of your Zenoss version, you should get get a login shell for the zenoss user, and run zendmd with the following commands.
su - zenoss zendmd
Listing Available Portlets (ZenDMD)
Execute the following commands at the zendmd prompt (In [1]:
 in Zenoss 5, and >>>
 in earlier versions).
f = getFacade('dashboard') dashboards = f.getAvailableDashboards() # Look into the details of the first dashboard returned. dashboard = dashboards[0] dashboard.id dashboard.uid dashboard.columns import json json.loads(dashboard.state)
Adding a New Dashboard (ZenDMD)
Now let's add that same dashboard we added via JSON using zendmd. This is easier than the shell example because we don't have to worry as much about escaping within python.
f = getFacade('dashboard') name = 'zendmd-example' owner_uid = '/zport/dmd/ZenUsers' # available to all users columns = 2 state = [{ 'id': 'col-0', 'items': [{ 'title': 'Event View', 'xtype': 'eventviewportlet', 'height': 400, 'refreshInterval': 300, },{ 'title': 'HTML', 'xtype': 'htmlportlet', 'height': 100, 'refreshInterval': 300, 'config': { 'html': '<h1>Some HTML..</h1>', }, }], },{ 'id': 'col-1', 'items': [{ 'title': 'Site Window', 'xtype': 'sitewindowportlet', 'height': 400, 'refreshInterval': 300, 'config': { 'siteUrl': 'https://www.zenoss.com/', }, }], }] import json f.addDashboard(name, owner_uid, columns, json.dumps(state))
Managing Dashboards with a ZenPack
The final topic I'll cover here is how you'd have a custom ZenPack add a new dashboard to the system when it's installed. We'll reuse what we learned in the zendmd example. The main difference is that we need to think about what to do when the ZenPack is upgraded or removed.
Adding a New Dashboard (ZenPack)
The first thing you'll need to do is to create a custom ZenPack using zenpacklib. Now you'll need to edit the __init__.py file that's in the same directory as the ZenPack's zenpack.yaml file.
Add the following lines to the end of __init__.py.
import logging LOG = logging.getLogger('zen.MyZenPack') from Products.Zuul import getFacade from . import schema class ZenPack(schema.ZenPack): def install(self, dmd): """Install ZenPack. Executed during first install, and upgrades.""" super(ZenPack, self).install(dmd) name = 'zenpack-example' owner_uid = '/zport/dmd/ZenUsers' columns = 2 state = [{ 'id': 'col-0', 'items': [{ 'title': 'Event View', 'xtype': 'eventviewportlet', 'height': 400, 'refreshInterval': 300, },{ 'title': 'HTML', 'xtype': 'htmlportlet', 'height': 100, 'refreshInterval': 300, 'config': { 'html': '<h1>Some HTML..</h1>', }, }], },{ 'id': 'col-1', 'items': [{ 'title': 'Site Window', 'xtype': 'sitewindowportlet', 'height': 400, 'refreshInterval': 300, 'config': { 'siteUrl': 'https://www.zenoss.com/', }, }], }] self.remove_dashboard(name) self.add_dashboard(name, owner_uid, columns, json.dumps(state)) def remove(self, dmd, leaveObjects=False): """Called when ZenPack is removed, but also when it's upgraded. leaveObjects will be True during an upgrade, and False during a remove. """ self.remove_dashboard(name) def add_dashboard(self, name, owner_uid, columns, state): getFacade('dashboard'). addDashboard( name, owner_uid, columns, json.dumps(state)) def remove_dashboard(self, name): """Remove any dashboards with given name.""" for dashboard in f.getAvailableDashboards(): if dashboard.id == name: LOG.info("removing %s dashboard", dashboard.id) getFacade('dashboard'). deleteObject(dashboard.uid)
The dashboard will be installed the next time you install this custom ZenPack.
Appendix A: Portlet Types
The following standard portlet types are available for use in dashboards. It's also possible for ZenPacks to include new types of portlets, so you may have more options available depending on what ZenPacks you have installed.
Portlet Type | xtype | config properties |
---|---|---|
HTML Portlet | htmlportlet | html |
Google Maps | googlemapportlet | baselocation, pollinginterval |
Site Window | sitewindowportlet | siteUrl |
Device Issues | deviceissuesportlet | |
Daemon Processes Down | daemonprocessportlet | |
Production States | productionstateportlet | productionStates |
Watch List | watchlistportlet | uids |
Open Events Chart | openeventsportlet | eventClass, summaryFilter, daysPast |
Past Events Line Chart | pasteventschart | eventClass, summaryFilter, daysPast |
Network Map | networkmapportlet | network |
Event View | eventviewportlet | |
Top Level Organizers | toplevelorganizersportlet | rootOrganizer, childOrganizer |
Device Chart* | devicechartportlet | deviceClass, graphPoints |
Impact Services** | impactgridportlet | serviceOrg |
* Only available in Zenoss 5. ** Only available with Impact.