Skip navigation
1 2 Previous Next 6008 Views 15 Replies Latest reply: Aug 2, 2011 11:33 AM by Ryan Matte RSS
Ryan Matte ZenossMaster 653 posts since
Mar 26, 2009
Currently Being Moderated

Jul 26, 2011 1:33 PM

Can't get more than one data column to display in a report

I've written several reports in the past, but it was prior to the new Alias system that was put in place.  I wrote 3 basic reports that work fine, but there's nothing fancy about them, they are just 3 columns, device name, component, value.  I then tried to make a fourth report with 5 total columns, but no matter what I've tried it always only shows the data in one column.  I basically just copied and modified the code from the interface utilization report...

 

Here is the python plugin:

 

###########################################################################
#
# This program is part of Zenoss Core, an open source monitoring platform.
# Copyright (C) 2007, 2009 Zenoss Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published by
# the Free Software Foundation.
#
# For complete information please visit: http://www.zenoss.com/oss/
#
###########################################################################



import Globals
from Products.ZenReports.AliasPlugin import AliasPlugin, Column, \
                                            RRDColumnHandler, \
                                            PythonColumnHandler



class ipSLApktloss( AliasPlugin ):
    """
    ipSLA packet loss report
    """



    def getComponentPath(self):
        return 'ipSLAs'



    def _getComponents(self, device, componentPath):
        components=[]
        try:
            for s in device.ipSLAs():
                if s.rttMonCtrlAdminRttType == "Jitter":
                    components.append(s)
        except AttributeError:
            pass
        return components



    def getColumns(self):
        return [
                Column('deviceName', PythonColumnHandler( 'device.titleOrId()' )),
                Column('SLA', PythonColumnHandler( 'component.name()' )),
                Column('droppedPkts', RRDColumnHandler( 'rttMonLatestJitterOperPacketLossSD' )),
                Column('sentPkts', RRDColumnHandler( 'rttMonEchoAdminNumPackets' )),
                ]



    def getCompositeColumns(self):
        return [
                Column('percentLoss', PythonColumnHandler('float(droppedPkts)/float(sentPkts)'))
                ]

 

Here is the tales file:

 

<tal:block tal:define="
    tableName string:ipslapktlossreport;
    tm here/ZenTableManager;
    tableState python:tm.getTableState(tableName, sortedHeader='percentLoss',
                                           sortedSence='desc');



    sts python:here.ZenTableManager.setReqTableState;
    zem python:here.dmd.ZenEventManager;
    startDate python:sts(tableName, 'startDate',
                         zem.defaultAvailabilityStart());
    endDate python:sts(tableName, 'endDate',
                       zem.defaultAvailabilityEnd());
    how python:sts(tableName, 'how', 'AVERAGE');
    summaryOptions python:('AVERAGE', 'MAXIMUM', 'MINIMUM', 'LAST');
    deviceClass python:sts(tableName, 'deviceClass', '/');
    deviceFilter python:sts(tableName, 'deviceFilter', '');



    objects python:here.ReportServer.plugin('ipSLApktloss', tableState);
    objects python: (hasattr(request, 'doExport') and list(objects)) or objects;
    exportFields python:[('deviceName', 'Device'), ('SLA', 'SLA'), ('sentPkts', 'Sent Packets'), ('droppedPkts', 'Dropped Packets'), ('percentLoss', '% Loss')];
    batch python:here.ZenTableManager.getBatch(tableName,objects,sortedHeader='percentLoss', sortedSence='desc');
    ">
<tal:block metal:use-macro="here/reportMacros/macros/exportableReport">
<tal:block metal:fill-slot="report">



<tal:block metal:use-macro="here/templates/macros/page1">
<tal:block metal:fill-slot="breadCrumbPane">
    <span metal:use-macro="here/miscmacros/macros/reportBreadCrumbsList"/>
</tal:block>
<tal:block metal:fill-slot="contentPane">



<tal:block metal:use-macro="here/reportMacros/macros/utilizationForm"/>



<form method="POST" tal:attributes="action request/URL; name string:deviceList"
            tal:define="tabletitle string:ipSLA Packet Loss;
                        showfilterbox python:True;
                        tblcolspan string:5">
<tal:block metal:use-macro="here/zenuimacros/macros/zentable">
<tal:block metal:fill-slot="zentablecontents">



    <tr>
        <th tal:replace="structure python:here.ZenTableManager.getTableHeader(
                            tableName,'deviceName','Device')"/>
        <th tal:replace="structure python:here.ZenTableManager.getTableHeader(
                            tableName,'SLA','SLA')"/>
        <th tal:replace="structure python:here.ZenTableManager.getTableHeader(
                            tableName,'sentPkts','Sent Packets')"/>
        <th tal:replace="structure python:here.ZenTableManager.getTableHeader(
                            tableName,'droppedPkts','Dropped Packets')"/>
        <th tal:replace="structure python:here.ZenTableManager.getTableHeader(
                            tableName,'percentLoss','% Loss')"/>
    </tr>
    <tal:block tal:repeat="r batch">
        <tr tal:define="odd repeat/r/odd"
            tal:attributes="class python:test(odd,'odd','even')">
           <td><a tal:attributes="href python:r.device.getPrimaryUrlPath()+'/ipSLAipSlaDevice'"
                  tal:content="python:r.deviceName"/></td>
           <td><a tal:attributes="href python: r.component.getPrimaryUrlPath()"
                  tal:content="python: r.SLA"/></td>
           <td tal:content="python: r.sentPkts"/>
           <td tal:content="python: r.droppedPkts"/>
           <td tal:content="python: r.percentLoss"/>
        </tr>
    </tal:block>
    <tr>
        <td colspan="5" align='center'>
        <form metal:use-macro="here/zenTableNavigation/macros/navtool"/>
        </td>
    </tr>



</tal:block>
</tal:block>
</form>



</tal:block>
</tal:block>



</tal:block>
</tal:block>
</tal:block>

 

The resulting output is like...

 

DeviceSLASent Packets
Dropped Packets
% Loss
Device 1SLA Name 11000
Device 2SLA Name 21000

 

Only the Sent Packets column gets populated, the other two do not.  If I comment out the sentPkts column in the plugin then the Dropped Packets column gets populated.  It's always only using one column.  It's also never able to calculate percentLoss because it doesn't get the values for the other columns to be able to do the calculation.  I've spent hours trying to debug this and I'm completely out of ideas.

 

Any help would be greatly appreciated, I must be missing something here.

  • jplouis ZenossEmployee 93 posts since
    May 7, 2008

    Looked over the code and compared it to existing reports and didn't see anything wrong, though it has been a while since I've looked at reports at all.  Have you reloaded the report?

  • jplouis ZenossEmployee 93 posts since
    May 7, 2008

    I tested your plugin and rpt, since I don't have your zenpack I hard coded a few values in the plugin and remove the reference to '/ipSLAipSlaDevice' from the rpt. I only had a trunk build running but the report worked fine.  The next step would be to step through some of the code in debug, probably in the "run" and "_createRecord" methods in AliasPlugin.py

     

    Here is the code I used for the plugin

     

     

    class ipSLApktloss( AliasPlugin ):
        """
        ipSLA packet loss report
        """
        def getComponentPath(self):
            return 'ipSLAs'
        def _getComponents(self, device, componentPath):
            return device.getDeviceComponents()
        def getColumns(self):
            return [
                    Column('deviceName', PythonColumnHandler( 'device.titleOrId()' )),
                    Column('SLA', PythonColumnHandler( 'component.name()' )),
                    Column('droppedPkts', PythonColumnHandler(
                                'doFn1( )',
                                dict( doFn1= lambda : '10000') ) ),
                    Column('sentPkts', PythonColumnHandler(
                                'doFn2()',
                                dict( doFn2=lambda : '5000' ) ) ),
                    ]
        def getCompositeColumns(self):
            return [
                    Column('percentLoss', PythonColumnHandler('float(droppedPkts)/float(sentPkts)'))
                    ]
  • Andrey Telepin Rank: White Belt 70 posts since
    May 13, 2010

    For parametrs of RRDColumnHandler you need use Alias name of data point (not directly name of data point). Reason for that provide more abstract for data. For example if you have Temperature report and you wish value in Celcius, in future you can bay tempsensor which give only Fahrenheit, with alias it not problem, only you need enter formula which convert Fahrenheit to Celcius, witout change report. For add Alias just in Edit Data Point form enter any name you wish to field ID.

     

     

  • Andrey Telepin Rank: White Belt 70 posts since
    May 13, 2010

    I think it's no bug, you can add alias name without any formula and get raw value. But I not understand why your data point not support Alias.

  • jplouis ZenossEmployee 93 posts since
    May 7, 2008

    See if this helps

     

     

    Index: AliasPlugin.py
    ===================================================================
    --- AliasPlugin.py (revision 45747)
    +++ AliasPlugin.py (working copy)
    @@ -286,6 +286,7 @@
                     # the alias, then the column's aliasName
                     # was really the datapoint name
                     column = aliasColumnMap[ datapoint.id ]
    +                alias = datapoint.id
                 columnDatapointsMap[column].append( (alias,datapoint) )
  • jplouis ZenossEmployee 93 posts since
    May 7, 2008

    Sorry, it was a shot in the dark.  Revert it.

  • jplouis ZenossEmployee 93 posts since
    May 7, 2008

    I created a report similar to yours using datapoints without aliases and it worked.  You may want to debug around line 75 in AliasPlugin.py to see what is going on with your report.  The line is

     

        return entity.getRRDValue( datapoint.id, **summary )

    Entity should be the component and datapoint.id is self explanatory.
  • kkearney ZenossEmployee 118 posts since
    Sep 23, 2008

    This is a bug, unfortunately.  However, the fix is pretty easy:

     

    Index: RRDDataPoint.py
    ===================================================================
    --- RRDDataPoint.py     (revision 45732)
    +++ RRDDataPoint.py     (revision 46059)
    @@ -54,17 +54,13 @@
                               datasource, template.getPrimaryUrlPath())
                     continue
                 for datapoint in datasource.datapoints():
    -                thisDatapointsAliases = dict(
    -                        [ ( dpAlias.id, dpAlias ) for dpAlias in
    -                           datapoint.aliases() ] )
    -                found = False
    -                foundAlias = None
    +                thisDatapointsAliases = dict((dpAlias.id, dpAlias)
    +                                                for dpAlias in datapoint.aliases())
                     for alias in aliases:
    -                    if thisDatapointsAliases.has_key( alias ):
    -                        found = True
    +                    if alias in thisDatapointsAliases:
                             yield thisDatapointsAliases[alias], datapoint
    -                if alias == datapoint.id:
    -                    yield None, datapoint
    +                    if alias == datapoint.id:
    +                        yield None, datapoint
    
     def getDataPointsByAlias( context, alias ):
         """

     

      I'm really hoping that the forum doesn't munge the diff, the problem is in the Products/ZenModel/RRDDataPoint.py file, which contains a method which gets used by the AliasPlugin.py file.  What happens is that if multiple RRD datapoint ids are requested, then the for loop misses all but possibly one of them.

     

      The solution is to indent the "if alias == datapoint.id" code block over four spaces and then life is good.

     

      I've opened up a ticket for it and so it should hopefully be in Zenoss 3.2.

     

     

    kells

1 2 Previous Next

More Like This

  • Retrieving data ...

Legend

  • Correct Answers - 4 points
  • Helpful Answers - 2 points