ZenossZenoss

blog

Tech Talk Deep Dive: Managing Zenoss Dashboards

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 which contains a list of dashboards. Within each dashboard, you'll see state as a JSON-serialized string containing the portlets on the dashboard.

{
    "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":"eventviewportlet","height":400,"collapsed":false},{"title":"Open Events Chart","refreshInterval":300,"config":{"eventClass":"/","summaryFilter":"","daysPast":3},"xtype":"openeventsportlet","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.

 

Try Zenoss!

Categories

Subscribe

Enter your email address in the box below to subscribe to our blog.

Loading
FEATURED CONTENT
WHITE PAPER
Zenoss Cloud Product Overview: Intelligent Application & Service Monitoring
Analyst Report
451 Research: New Monitoring Needs Are Compounding Challenges Related to Tool Sprawl

Enabling IT to Move at the Speed of Business

Zenoss is built for modern IT infrastructures. Let's discuss how we can work together.

Schedule a Demo

Want to see us in action? Schedule a demo today.