Component modeling
This section will cover creation of a custom Component subclass,
creation of a relationship to our NetBotDevice
class, and modeling of
the components to fill the relationship.
In Device modeling, we added
a temp_sensor_count
attribute to our NetBotz devices. This isn’t very
useful. It would be more useful to monitor the temperature being
reported by each of these sensors. So that’s what we’ll do. Modeling
each sensor as a component allows Zenoss to automatically discover and
monitor sensors regardless of how many a particular device has.
Find temperature sensor attributes
In Device modeling, we used smidump
to extract
temperature sensor information from NETBOTZV2-MIB
. This will be even
more applicable as we decide what attributes and metrics are available
on each sensor. Let’s use smidump
and snmpwalk
for a refresher on
what’s available.
Find temperature information in NETBOTZV2-MIB using the following command.
smidump -f identifiers /usr/share/snmp/mibs/NETBOTZV2-MIB.mib | grep -Ei temp
You should see the following in the output:
NETBOTZV2-MIB tempSensorTable table 1.3.6.1.4.1.5528.100.4.1.1
NETBOTZV2-MIB tempSensorEntry row 1.3.6.1.4.1.5528.100.4.1.1.1
NETBOTZV2-MIB tempSensorId column 1.3.6.1.4.1.5528.100.4.1.1.1.1
NETBOTZV2-MIB tempSensorValue column 1.3.6.1.4.1.5528.100.4.1.1.1.2
NETBOTZV2-MIB tempSensorErrorStatus column 1.3.6.1.4.1.5528.100.4.1.1.1.3
NETBOTZV2-MIB tempSensorLabel column 1.3.6.1.4.1.5528.100.4.1.1.1.4
NETBOTZV2-MIB tempSensorEncId column 1.3.6.1.4.1.5528.100.4.1.1.1.5
NETBOTZV2-MIB tempSensorPortId column 1.3.6.1.4.1.5528.100.4.1.1.1.6
NETBOTZV2-MIB tempSensorValueStr column 1.3.6.1.4.1.5528.100.4.1.1.1.7
NETBOTZV2-MIB tempSensorValueInt column 1.3.6.1.4.1.5528.100.4.1.1.1.8
NETBOTZV2-MIB tempSensorValueIntF column 1.3.6.1.4.1.5528.100.4.1.1.1.9
Let’s now use snmpwalk
to see what these values look like on our
NetBotz device.
snmpwalk 172.17.0.1 1.3.6.1.4.1.5528.100.4.1.1.1
You should see a lot of output that begins with the following:
NETBOTZV2-MIB::tempSensorId.21604919 = STRING: nbHawkEnc_1_TEMP
NETBOTZV2-MIB::tempSensorId.1095346743 = STRING: nbHawkEnc_0_TEMP
NETBOTZV2-MIB::tempSensorId.1382714817 = STRING: nbHawkEnc_2_TEMP1
NETBOTZV2-MIB::tempSensorId.1382714818 = STRING: nbHawkEnc_2_TEMP2
Note the 21604919
in the first response. This is the SNMP index of the
first temperature sensor, or the first row in the table. I like to then
restrict my snmpwalk results to only show this row with a command like
the following.
snmpwalk 172.17.0.1 1.3.6.1.4.1.5528.100.4.1.1.1 | grep "\.21604919 ="
Which will show us the value of each column for that one temperature sensor:
NETBOTZV2-MIB::tempSensorId.21604919 = STRING: nbHawkEnc_1_TEMP
NETBOTZV2-MIB::tempSensorValue.21604919 = INTEGER: 265
NETBOTZV2-MIB::tempSensorErrorStatus.21604919 = INTEGER: normal(0)
NETBOTZV2-MIB::tempSensorLabel.21604919 = STRING: Temperature
NETBOTZV2-MIB::tempSensorEncId.21604919 = STRING: nbHawkEnc_1
NETBOTZV2-MIB::tempSensorPortId.21604919 = STRING:
NETBOTZV2-MIB::tempSensorValueStr.21604919 = STRING: 26.500000
NETBOTZV2-MIB::tempSensorValueInt.21604919 = INTEGER: 26
NETBOTZV2-MIB::tempSensorValueIntF.21604919 = INTEGER: 79
Now we have everything we should need to make decisions about what attributes we should model for our sensors and which would better be collected as datasources to have thresholds applied and plotted over time on graphs.
My initial thoughts would be to model the following as attributes.
tempSensorId
tempSensorEncId
(enclosure ID)tempSensorPortId
I would then want to collect tempSensorValueStr
as a datasource
because it offers the best precision. Zenoss is capable of handling
numeric strings so we don’t have to collect tempSensorValue
and divide
it by 10 like other systems might.
Create a component subclass
Use the following steps to create a NetBotzTemperatureSensor
class
with the attributes discovered above.
-
Update
zenpack.yaml
to include the followingNetBotzTemperatureSensor
entry in theclasses
section, and the newclass_relationships
section.zenpack.yamlclasses: NetBotzDevice: base: [zenpacklib.Device] label: NetBotz properties: temp_sensor_count: type: int NetBotzTemperatureSensor: base: [zenpacklib.Component] label: Temperature Sensor properties: enclosure: label: Enclosure port: label: Port class_relationships: - NetBotzDevice 1:MC NetBotzTemperatureSensor
-
It’s important to pick unique class names. The best practice is to use a short prefix based on the ZenPack’s name followed by the type of thing the class represents as is being done here.
-
Both of the new properties should be strings. Since string is the default type, we don’t need to specify it. This just leaves the label.
Despite noting above that we always wanted to model the
tempSensorId
attribute, we aren’t adding an attribute for it here. This is becauseComponent
already has both anid
andtitle
attribute that wherein we can store the value oftempSensorId
. -
The
class_relationships
section is very important. We could never have any temperature sensors in the system if we didn’t relate them to something else. The1:MC
between the two class names describes the type of relationship. Specifically it says that oneNetBotzDevice
can contain manyNetBotzTemperatureSensor
objects. For more information, see Relationships.
-
Test TemperatureSensor class
With our component class defined and relationships setup we can
use zendmd
to make sure we didn’t make any mistakes. Execute the
following snippet in zendmd
.
from ZenPacks.training.NetBotz.NetBotzTemperatureSensor import NetBotzTemperatureSensor
sensor = NetBotzTemperatureSensor('test_sensor_01')
device = find("Netbotz01")
device.netBotzTemperatureSensors._setObject(sensor.id, sensor)
sensor = device.netBotzTemperatureSensors._getOb(sensor.id)
print sensor
print sensor.device()
You’ll most likely get the following error when executing the above snippet:
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: netBotzTemperatureSensors
This error is indicating that we have
no netBotzTemperatureSensors
relationship on the device object. This
would seemingly make no sense because we just added it. The key here is
that existing objects like the Netbotz01
device don’t automatically
get new relationships. We have to either delete the device and add it
again, or execute the following in zendmd
to create the newly-defined
relationship.
device.buildRelations()
commit()
Now you can go back and run the original snippet again. You should see the name of the sensor and device objects printed if everything worked as planned.
Update the modeler plugin
As with the NetBotzDevice
class, the next step after creating our
model class is to populate it with a modeler plugin. We could create a
new modeler plugin to only capture the temperature sensor components,
but we’ll update the NetBotz
modeler plugin we previously created to
model the sensors instead.
-
Edit
$ZP_DIR/modeler/plugins/training/snmp/NetBotz.py
and replace its contents with the following.from Products.DataCollector.plugins.CollectorPlugin import ( SnmpPlugin, GetTableMap, ) class NetBotz(SnmpPlugin): relname = 'netBotzTemperatureSensors' modname = 'ZenPacks.training.NetBotz.NetBotzTemperatureSensor' snmpGetTableMaps = ( GetTableMap( 'tempSensorTable', '1.3.6.1.4.1.5528.100.4.1.1.1', { '.1': 'tempSensorId', '.5': 'tempSensorEncId', '.6': 'tempSensorPortId', } ), ) def process(self, device, results, log): temp_sensors = results[1].get('tempSensorTable', {}) rm = self.relMap() for snmpindex, row in temp_sensors.items(): name = row.get('tempSensorId') if not name: log.warn('Skipping temperature sensor with no name') continue rm.append(self.objectMap({ 'id': self.prepId(name), 'title': name, 'snmpindex': snmpindex.strip('.'), 'enclosure': row.get('tempSensorEncId'), 'port': row.get('tempSensorPortId'), })) return rm
Let’s take a closer look at how we changed the modeler plugin.
-
We added
relname
andmodname
as class attributes.These two settings control the meta-data that will automatically be set when the
self.relMap
andself.objectMap
methods are called in theprocess
method.The target
relname
we should use depends on a couple of things. First, all leading uppercase letters of the class name will be converted to lowercase; that is,NetBotzTemperatureSensor
becomesnetBotzTemperatureSensor
. Second, the letters
is added to the end if it is a to-many relationship; that is,netBotzTemperatureSensor
becomesnetBotzTemperatureSensors
.Setting
relname
tonetBotzTemperatureSensors
will cause theself.relMap
call to create aRelationshipMap
that will be applied to thenetBotzTemperatureSensors
relationship defined on theNetBotzDevice
object.Setting
modname
toZenPacks.training.NetBotz.TemperatureSensor
will cause theself.objectMap
calls in theprocess
method to createObjectMap
instances that will be turned into instances of ourTemperatureSensor
class. -
We’re now requesting the
tempSensorEncId
andtempSensorPortId
columns be returned in the SNMP table request results. We’ll use these to populate their corresponding fields on theTemperatureSensor
class. -
Most of the
procses
method has been changed.We’re now creating a
RelationshipMap
and appending anObjectMap
to it for each temperature sensor in the results. We use theself.relMap
andself.objectMap
utility methods to make this easier.
-
-
Restart
Zope
andzenhub
to load the changed module.serviced service restart zope serviced service restart zenhub
Test the modeler plugin
We already added the training.snmp.NetBotz
modeler plugin the
the /NetBotz
device class in an earlier exercise. So we only need to
run zenmodeler
to test the temperature sensor modeling updates.
-
Run
zenmodeler run --device=Netbotz01
We should see
Changes in configuration applied
near the end of zenmodeler’s output. The changes referred to should be 14 temperature sensor objects being created and added to the device’snetBotzTemperatureSensors
relationship. -
Check the
Netbotz01
device in the web interface. The temperature sensors should now be visible.