Error zen.zenmodeler traceback most recent call last

error zen.zenmodeler traceback most recent call last

Now, when I select the device, I get directed to a "Site Error" page log]$ zenchkrels -r -x1 Traceback (most recent call last): File. This release of Resource Manager installs the following ZenPacks at the current version listed in the table. ZenPack. Current version. Previous version. 2.4. zendmd: Command-line Access to the Device Management Database (DMD). Creating a ZenPack. AssertionError: Traceback (most recent call last).

Related Videos

Fix ImportError: No module named nyawc.Options SQLIV in Kali Linux

Error zen.zenmodeler traceback most recent call last - what necessary

{}, {componentType: ’NetBotzTemperatureSensor’,autoExpandColumn: ’name’,sortInfo: {field: ’name’,direction: ’ASC’},fields: [{name: ’uid’},{name: ’name’},{name: ’status’},{name: ’severity’},{name: ’usesMonitorAttribute’},{name: ’monitor’},{name: ’monitored’},{name: ’locking’},{name: ’enclosure’},{name: ’port’}],columns: [{id: ’severity’,dataIndex: ’severity’,header: _t(’Events’),renderer: Zenoss.render.severity,sortable: true,width: 50},{id: ’name’,dataIndex: ’name’,header: _t(’Name’),sortable: true},{id: ’enclosure’,dataIndex: ’enclosure’,header: _t(’Enclosure ID’),sortable: true,width: 120},{id: ’port’,dataIndex: ’port’,header: _t(’Port ID’),sortable: true,width: 120},{id: ’monitored’,dataIndex: ’monitored’,header: _t(’Monitored’),renderer: Zenoss.render.checkbox,sortable: true,width: 70},{id: ’locking’,dataIndex: ’locking’,header: _t(’Locking’),renderer: Zenoss.render.locking_icons,width: 651.1. ZenPack Development Guide 33

ZenossLabsDocumentation, Release});}]});}, config);Ext.reg(’NetBotzTemperatureSensorPanel’, ZC.NetBotzTemperatureSensorPanel);This is a length snippet of JavaScript. Let’s go through it section by section.(a) Define a new ExtJS class named NetBotzTemperatureSensorPanel.We define this class within theZenoss.component namespace by using the ZC variable we created in theprevious step. Just like we’ve been defining our Python classes by extending existing Zenoss Pythonclasses, we do the same with JavaScript classes within the ExtJS framework. In this case we’re extendingthe ComponentGridPanel class.(b) Create the NetBotzTemperatureSensorPanel constructor.Our constructor method only does two things. First we override the config variable. Then we close bycalling our superclass’ constructor. Our superclass is the ComponentGridPanel we extended.(c) Override config variable.This is where all of the interesting stuff happens, and where we’ll be making changes. Let’s look at eachof the fields we’re changing within config.i. componentTypeMust match the meta_type on our TemperatureSensor Python class.ii. autoExpandColumnOne of the following columns can be picked to automatically expand to use the remaining space withinthe user’s web browser. Typically this is set to the name field, but it can be useful to choose a differentfield if the name is a well-known length and another field is more variable.iii. sortInfoControls which field and direction that the grid will be sorted by when it initially appears. This is anoptional field and defaults to sort by name in ascending order if not set. So in this example the sortInfofield could have been left out.iv. fieldsControls which fields will be requested from the TemperatureSensorInfo API adapter on the server todisplay a column. You can request any attribute that you made available on the Info adapter. This evenincludes fields that are not present in the IInfo interface.You must be sure to include fields needed to display all of the columns specified below. For consistencyI recommend having the following minimum set plus any that are specific to your componenttype.• uid• name• status• severity• usesMonitorAttribute1.1. ZenPack Development Guide 34

ZenossLabsDocumentation, Release• monitor• monitored• lockingv. columnsControls the visual display of columns in the grid. Each column can specify the following fields.• idA unique identifier for the field. Typically this is set to the same value as dataIndex.• dataIndexReference to one of the items from fields above.• headerText that will appear in the column’s header.• rendererJavaScript function that will be used to render the column’s data. This is an optional field and willdefault to displaying the data’s natural string representation.You can find the standard renderer choices in the following file:$ZENHOME/Products/ZenUI3/browser/resources/js/zenoss/Renderers.js• sortableWhether or not the user can choose to sort the grid by this column.• widthThe width in pixels of the column. This is an optional field, but I highly recommend setting in onall columns except for the column that’s referenced in autoExpandColumn.Test the Component DisplayWe test our component display JavaScript by looking at it in the web interface.If you’re running zopectl in the foreground, it is not necessary to restart it after making changes to existing files withinour resourceDirectory. However, you will have to force your browser to do a full refresh to make sure your browsercache isn’t interfering. This can typically be done by holding the SHIFT key while clicking the refresh button or typingthe refresh shortcut.I recommend having the browser’s JavaScript console open while testing so you don’t miss any errors.Component MonitoringThis section covers monitoring component metrics using SNMP. I assume that you’ve completed the ComponentModeling steps and now have temperature sensor components modeled for the NetBotz device. Currently there willbe no graphs for these temperature sensors.We will add collection, thresholding and graphing for the temperature monitored by each sensor.1.1. ZenPack Development Guide 35

ZenossLabsDocumentation, ReleaseFind the SNMP OIDIn the Component Modeling section we used smidump and snmpwalk to find which values would be useful to model,and which would be useful to monitor. We found tempSensorValue to be the best OID to use for monitoring a sensor’scurrent temperature.Let’s use snmpwalk again to see what tempSensorValue looks like for all of the sensors on our NetBotz device.snmpwalk NETBOTZV2-MIB::tempSensorValueStrThis gives of the current temperature (in celsius) for each sensor:NETBOTZV2-MIB::tempSensorValueStr.21604919 = STRING: 26.500000NETBOTZV2-MIB::tempSensorValueStr.1095346743 = STRING: 27.000000NETBOTZV2-MIB::tempSensorValueStr.1382714817 = STRING: 22.100000NETBOTZV2-MIB::tempSensorValueStr.1382714818 = STRING: 21.100000NETBOTZV2-MIB::tempSensorValueStr.1382714819 = STRING: 19.600000NETBOTZV2-MIB::tempSensorValueStr.1382714820 = STRING: 19.900000NETBOTZV2-MIB::tempSensorValueStr.1382714833 = STRING: 20.500000NETBOTZV2-MIB::tempSensorValueStr.1382714834 = STRING: 20.100000NETBOTZV2-MIB::tempSensorValueStr.1382714865 = STRING: 19.700000NETBOTZV2-MIB::tempSensorValueStr.1382714866 = STRING: 20.500000NETBOTZV2-MIB::tempSensorValueStr.1382714867 = STRING: 20.100000NETBOTZV2-MIB::tempSensorValueStr.1382714868 = STRING: 20.000000NETBOTZV2-MIB::tempSensorValueStr.2169088567 = STRING: 26.600000NETBOTZV2-MIB::tempSensorValueStr.3242830391 = STRING: 27.400000As we go on to add a monitoring template below, we’ll need to know what OID to poll to collect this value. The keyto determining this for components with an snmpindex attribute like TemperatureSensor has is to find the OID for thevalues above and remove the SNMP index from the end of it. Let’s use snmptranslate to do this.snmptranslate -On NETBOTZV2-MIB::tempSensorValueStrThis results in the following output:. OID (minus the leading .) is what we’ll need.Add a Monitoring TemplateIn the Component Modeling section we added a getRRDTemplateName method to our TemperatureSensor class. Wemade this method return TemperatureSensor. This means that each temperature sensor will have a template by thisname automatically bound to it.This makes life easy when adding a monitoring template to be used. All we have to do is create a monitoring templatenamed TemperatureSensor in the /NetBotz device class.1. Navigate to Advanced -> Monitoring Templates.2. Add a template.(a) Click the + in the bottom-left of the template list.(b) Set Name to TemperatureSensor(c) Set Template Path to /NetBotz(d) Click SUBMIT3. Add a data source.1.1. ZenPack Development Guide 36

ZenossLabsDocumentation, Release(a) Click the + at the top of the Data Sources panel.(b) Set Name to tempSensorValueStr(c) Set Type to SNMP(d) Click SUBMIT(e) Double-click to edit the tempSensorValueStr data source.(f) Set OID to Click SAVE5. Add a threshold.(a) Click the + at the top of the Thresholds panel.(b) Set Name to high temperature(c) Set Type to MinMaxThreshold(d) Click ADD(e) Double-click to edit the high temperature threshold.(f) Move the datapoint to the list on the right.(g) Set Maximum Value to 32(h) Set Event Class to /Environ(i) Click SAVE6. Add a graph.(a) Click the + at the top of the Graph Definitions panel.(b) Set Name to Temperature(c) Click SUBMIT(d) Double-click to edit the Temperature graph.(e) Set Units to degrees c.(f) Click SUBMIT7. Add a graph point.(a) Click to select the Temperature graph.(b) Choose Manage Graph Points from the gear menu.(c) Choose Data Point from the + menu.(d) Choose tempSensorValueStr then click SUBMIT(e) Double-click to edit the tempSensorValueStr graph point.(f) Set Name to Temperature(g) Set Format to %7.2lf(h) Click SAVE then SAVE again.1.1. ZenPack Development Guide 37

ZenossLabsDocumentation, ReleaseTest Monitoring Template You can now refer back to the Test Monitoring Template section of Device Monitoringfor using zenperfsnmp to test the data point collection aspect of your monitoring template.You can verify that your monitoring template is getting bound to each temperature sensor properly by navigating to oneof the temperature sensors in the web interface and choosing Templates from it’s Display drop-down box. Furthermore,you can verify that your Temperature graph is shown when choosing Graphs from the temperature sensor’s Displaydrop-down.SNMP TrapsThis section covers how to handle SNMP traps.Zenoss will accept SNMP traps from your devices as soon as you configure those devices to send traps to your Zenossserver. The zentrap daemon will listen to the standard SNMP trap port of 162/udp and create an event for every trapthat it receives.However, without you giving Zenoss more information about the contents of those traps, the events will containnumeric OIDs and be nearly impossible for a human to decipher.Importing MIBsLet’s import the NETBOTZV2-MIB that we’ve been working with through these examples.1. Copy the MIB to Zenoss’ site-specific MIB directory.cp /usr/share/snmp/mibs/NETBOTZV2-MIB.mib $ZENHOME/share/mibs/site/2. Import the MIB directory.zenmib runFrom which we should get the following output:Found 1 MIBs to import.Parsed 214 nodes and 256 notifications from NETBOTZV2-MIBLoaded MIB NETBOTZV2-MIB into the DMDLoaded 1 MIB file(s)3. Add the imported MIB to the NetBotz ZenPack.(a) Navigate to Advanced -> MIBs in the web interface.(b) Select NETBOTZV2-MIB.(c) Choose Add to ZenPack from the gear menu at the bottom of the list.(d) Choose the then click SUBMIT.Simulating SNMP TrapsTo more easily configure and test Zenoss’ trap handling, it’s useful to know how to simulate SNMP traps. Thealternative is breaking your real devices in various ways and hoping to be able to get the device to send all of the trapsyou need. This isn’t always possible.Let’s start by picking an SNMP trap to simulate.1. Navigate to Advanced -> MIBs in the web interface.2. Choose NETBOTZV2-MIB from the list of MIBs.1.1. ZenPack Development Guide 38

ZenossLabsDocumentation, Release3. Choose Traps from the drop-down box in the middle of the right panel.4. Choose netBotzTempTooHigh in the list of traps.We’ll now see information about this trap in the bottom-right panel. The first thing to note is the OID. This is all weneed to get started.Send a Simple Trap1. Make sure zentrap is running.Use the following steps to get your feet wet sending a basic trap.As detailed in Development Environment section, I prefer to only run the processes necessary for debugging. SoI would run zentrap in the foreground in a new terminal session with the following command.zentrap run -v10 --cycle2. Run the following snmptrap command.snmptrap localhost 0 NETBOTZV2-MIB::netBotzTempTooHighIf you were running zentrap in the foreground you should have seen a log message similar to the following aftersending the trap. This is Zenoss using the MIB to turn the trap into an event:Queued event (total of 1) {’sysUpTime.0’: 0L,’firstTime’: 1343072200.36731,’severity’: 3,’eventClassKey’: ’netBotzTempTooHigh’,’oid’: ’’,’component’: ’’,’community’: ’public’,’summary’: ’snmp trap netBotzTempTooHigh’,’eventGroup’: ’trap’,’sysUpTime’: 0L,’device’: ’’,’lastTime’: 1343072200.36731,’monitor’: ’localhost’}You can see how Zenoss has maintained the numeric OID in the event’s oid field. It has also decoded it tonetBotzTempTooHigh using the MIB we imported and used that value in the eventClassKey and summary fields.3. Find this netBotzTempTooHigh event in web interface’s event console.Send a Full Trap Now that we’ve proved out a simple trap, we should add variable bindings or varbinds to the trap.If you look at the netBotzTempTooHigh trap in theZenoss web interface’s MIB explorer again, you’ll see that there’san extensive list of Objects associated with the trap definition. These are variable bindings.A variable binding allows the device sending the SNMP trap to attach additional information to the trap. In thisexample, one of the variable bindings for the netBotzTempTooHigh trap is netBotzV2TrapSensorID. This will give usa way to know which one of the sensors has exceeded it’s high temperature threshold.1. Run the following snmptrap command.snmptrap localhost 0 NETBOTZV2-MIB::netBotzTempTooHigh \NETBOTZV2-MIB::netBotzV2TrapSensorID s ’nbHawkEnc_1_TEMP1’As you can see, this zentrap command starts exactly the same as in the example. We then add the followingthree fields.(a) NETBOTZV2-MIB::netBotzV2TrapSensorID (OID)1.1. ZenPack Development Guide 39

ZenossLabsDocumentation, Release(b) s (type)(c) ’nbHawkEnc_1_TEMP1’ (value)We can continue to add sets of these three parameters to add as many other variable bindings to the trap as wewant.Assuming you were running zentrap in the foreground you should see a log that looks like the following:Queued event (total of 1) {’sysUpTime.0’: 0L,’firstTime’: 1343073249.083523,’severity’: 3,’netBotzV2TrapSensorID’: ’nbHawkEnc_1_TEMP1’,’eventClassKey’: ’netBotzTempTooHigh’,’oid’: ’’,’component’: ’’,’community’: ’public’,’summary’: ’snmp trap netBotzTempTooHigh’,’eventGroup’: ’trap’,’sysUpTime’: 0L,’device’: ’’,’lastTime’: 1343073249.083523,’monitor’: ’localhost’}Note that the only difference between this event and the simple event is the addition of the net-BotzV2TrapSensorID field. So now you see how Zenoss take the name/value pairs that are the SNMP trap’svariable bindings and turn them into name/value pairs within the resulting event.Mapping SNMP Trap EventsNow that we’re able to create SNMP traps anytime we want, it’s time to use Zenoss’ event mapping system to makethem more useful. The most important field on an incoming event when it comes to mapping is the eventClassKeyfield. Fortunately for us, SNMP traps get that great eventClassKey set that gives us a big head start.1. Map the event.(a) Navigate to Events in the web interface.(b) Select the netBotzTempTooHigh event you just created.(c) Click the toolbar button that looks like a hierarchy. If you hover over it, the tooltip will say Reclassify anevent.(d) Choose the /Environ event class then click SUBMITNow the next time a netBotzTempTooHigh trap is received it will be put into the /Environ event class insteadof /Unknown.2. Enrich the event.(a) Click the Go to new mapping link to navigate to the new mapping.(b) Click Edit in the left navigation pane.(c) Set Transform to the following:evt.component = getattr(evt, ’netBotzV2TrapSensorID’, ’’)This will use the name of the sensor as described by the netBotzV2TrapSensorID variable binding as theevent’s component field.1.1. ZenPack Development Guide 40

ZenossLabsDocumentation, ReleaseThere are endless possibilities of what you could do within the transform for this event and others. This is just onepractical example.1.1.6 Monitoring an HTTP APIThe following sections will describe an efficient approach to monitoring data via a HTTP API. We’ll start by usingzenpacklib to more easily extend theZenoss object model. Then we’ll use a Python modeler plugin to fill out theobject model. Then we’ll use PythonCollector to monitor for events, datapoints and even to update the model.For purposes of this guide we’ll be building a ZenPack that monitors the weather using The Weather Channel’s WeatherUnderground API.Note: It is recommended that you have already finished the Monitoring an SNMP-Enabled Device section as itprovides much more detail and troubleshooting advice. This exercise builds on that experience.Exercises:Wunderground APIThe Weather Underground provides an API that can be used to get all sorts of data related to the weather. Before youcan use most endpoints on the API you must first create an account. Fortunately you can get a Developer account withall of the bells and whistles for free by signing up at So go sign up andget your API key. You’ll need it for the rest of this exercise.We’ll be using the following APIs for this exercise.1. AutoComplete2. Alerts3. ConditionsAutoComplete APIBoth the Alerts and Conditions APIs require that you query for a specific location. It can be hard to know what thename or code for a location is without doing some manual research. That’s where the AutoComplete API comes in.You can provide a reasonable name for a location and it will return a list of possible matches along with a unique linkfor that location.We’ll use the AutoComplete API during modeling so that theZenoss user can enter nearly any city or county namethen let Zenoss do the work of converting that into the link that we’ll subsequently use to query for weather alerts andconditions.Here’s an example query for Austin, TX: “Austin%2C%20TX” is the URL encoded version of “Austin, TX”. We won’t have to worry about that whenwe work with it because our HTTP library automatically encodes URLs.Here’s the response to that example query for Austin, TX:{"RESULTS": [{"c": "US",1.1. ZenPack Development Guide 41

ZenossLabsDocumentation, Release}]}"l": "/q/zmw:78701.1.99999","lat": "30.271158","ll": "30.271158 -97.741699","lon": "-97.741699","name": "Austin, Texas","type": "city","tz": "America/Chicago","tzs": "CDT","zmw": "78701.1.99999"There are a few things to note about this request and response. The first is that we didn’t need to use our API key.This is because the AutoComplete API doesn’t require an API key. The second is that there’s only a single result forAustin, TX. The third is the l value which is the unique link to Austin, TX that we can use when accessing the otherAPI endpoints such as Alerts and Conditions.Alerts APIThe Alerts API provides information about severe weather alerts such as tornado warnings, flood warnings and otherspecial weather statements. We’ll be collecting these alerts to create corresponding Zenoss events. This way operatorscan know when severe weather may be impacting areas of concern.Here’s an example query for alerts in Austin, TX: Note how the URL ends with /alerts/.json using the l link value from the AutoComplete query for Austin,TX above.Here’s the relevant portion of the response to an alerts query. Of course Austin doesn’t have severe weather so we’llbe looking at Des Moines alerts instead:{}"alerts": [{"date": "1:07 PM CDT on June 16, 2014","date_epoch": "1402942020","description": "Severe Thunderstorm Warning","expires": "2:15 PM CDT on June 16, 2014","expires_epoch": "1402946100","message": "\nThe National Weather Service in Des Moines has issued a\n\n* Severe Thunder"phenomena": "SV","significance": "W","type": "WRN","tz_long": "America/Chicago","tz_short": "CDT"}]It’s easy to imagine turning this alert into a Zenoss event. We’ll see how to do this a bit later. The Alerts APIdocumentation has a link to a document that describes what the phenomena, significance, and type values represent.1.1. ZenPack Development Guide 42

ZenossLabsDocumentation, ReleaseConditions APIThe Conditions API provides information about current weather conditions for a given location. The Conditions APIis used in exactly the same way as the Alerts API, and accepts the same link to specify the location. There’s a lot ofnumeric data that would be useful to graph and threshold as Zenoss datapoints.Here’s an example query for conditions in Austin, TX:’s the relevant portion of the response to a conditions query:{"current_observation": {"UV": "1","dewpoint_c": 11,"dewpoint_f": 51,"dewpoint_string": "51 F (11 C)","display_location": {"city": "San Francisco","country": "US","country_iso3166": "US","elevation": "47.00000000","full": "San Francisco, CA","latitude": "37.77500916","longitude": "-122.41825867","magic": "1","state": "CA","state_name": "California","wmo": "99999","zip": "94101"},"estimated": {},"feelslike_c": "13.9","feelslike_f": "57.0","feelslike_string": "57.0 F (13.9 C)","forecast_url": "","heat_index_c": "NA","heat_index_f": "NA","heat_index_string": "NA","history_url": "","icon": "partlycloudy","icon_url": "","image": {"link": "","title": "Weather Underground","url": ""},"local_epoch": "1402931138","local_time_rfc822": "Mon, 16 Jun 2014 08:05:38 -0700","local_tz_long": "America/Los_Angeles","local_tz_offset": "-0700","local_tz_short": "PDT","nowcast": "","ob_url": ",-122.4"observation_epoch": "1402931132","observation_location": {"city": "SOMA - Near Van Ness, San Francisco","country": "US",1.1. ZenPack Development Guide 43

ZenossLabsDocumentation, Release}}"country_iso3166": "US","elevation": "49 ft","full": "SOMA - Near Van Ness, San Francisco, California","latitude": "37.773285","longitude": "-122.417725","state": "California"},"observation_time": "Last Updated on June 16, 8:05 AM PDT","observation_time_rfc822": "Mon, 16 Jun 2014 08:05:32 -0700","precip_1hr_in": "0.00","precip_1hr_metric": " 0","precip_1hr_string": "0.00 in ( 0 mm)","precip_today_in": "0.00","precip_today_metric": "0","precip_today_string": "0.00 in (0 mm)","pressure_in": "29.89","pressure_mb": "1012","pressure_trend": "+","relative_humidity": "81%","solarradiation": "--","station_id": "KCASANFR58","temp_c": 13.9,"temp_f": 57.0,"temperature_string": "57.0 F (13.9 C)","visibility_km": "16.1","visibility_mi": "10.0","weather": "Scattered Clouds","wind_degrees": 238,"wind_dir": "WSW","wind_gust_kph": 0,"wind_gust_mph": 0,"wind_kph": 4.8,"wind_mph": 3.0,"wind_string": "From the WSW at 3.0 MPH","windchill_c": "NA","windchill_f": "NA","windchill_string": "NA"Using ZenPackLib (zenpacklib)In the Device Modeling and Component Modeling sections of the Monitoring an SNMP-Enabled Device documentationwe covered what could be called the “long way” to extend theZenoss object model to add the new NetBotzDevicedevice type, it’s TemperatureSensor component type and the relationship between them. A new library called zenpacklibis now available that we use to make this process easier.In this case we want to add a new WundergroundDevice device type, a WundergroundLocation component type anda relationship between them so we can add a Weather Underground device such as to Zenoss andadd one or more locations where we want to monitor weather conditions. This section will describe how we do thatusing zenpacklib.Create the Weather Underground ZenPackSince we don’t yet have a Weather Underground ZenPack to work with, let’s create that first.1.1. ZenPack Development Guide 44

ZenossLabsDocumentation, Release1. Go to the Advanced -> Settings -> ZenPacks in theZenoss web interface.2. Choose Create a ZenPack... from the gear menu above the list of ZenPacks.3. Name the ZenPack then click OK.Download zenpacklibCreating the ZenPack will create the ZenPack’s source directory under $ZEN-HOME/ZenPacks/ Run the following commands as the zenoss user tomake this directory, and it’s important subdirectory more easily accessible:export ZP_TOP_DIR=$ZENHOME/ZenPacks/ ZP_DIR=$ZP_TOP_DIR/ZenPacks/training/WeatherUndergroundThen download the latest zenpacklib from GitHub into the ZenPack’s main code directory with the following $ZP_DIRwget the ZenPackSpecOnce is in $ZP_DIR you can create what is called a ZenPackSpec in the ZenPack’s ThisZenPackSpec will define the specification for the ZenPack. Specifically its name, zProperties, classes and class relationships.Replace the contents of $ZP_DIR/ with the following:# Import zenpacklib from the current directory ( . import zenpacklib# Create a ZenPackSpec and name it CFG.CFG = zenpacklib.ZenPackSpec(name=__name__,zProperties={’DEFAULTS’: {’category’: ’Weather Underground’},’zWundergroundAPIKey’: {},},’zWundergroundLocations’: {’type’: ’lines’,’default’: [’Austin, TX’, ’San Jose, CA’, ’Annapolis, MD’],},classes={’WundergroundDevice’: {’base’: zenpacklib.Device,’label’: ’Weather Underground API’,},’WundergroundLocation’: {’base’: zenpacklib.Component,’label’: ’Location’,1.1. ZenPack Development Guide 45

ZenossLabsDocumentation, Release’properties’: {’country_code’: {’label’: ’Country Code’,’order’: 4.0,},’timezone’: {’label’: ’Time Zone’,’order’: 4.1,},},},}’api_link’: {’label’: ’API Link’,’order’: 4.9,’grid_display’: False,},)class_relationships=zenpacklib.relationships_from_yuml("""[WundergroundDevice]++-[WundergroundLocation]""")# Create the specification.CFG.create()You can see this ZenPackSpec defines the following important aspects of our ZenPack.1. The name is set to __name__ which will evaluate to This shouldalways be set in this way as it will helpfully figure out the name for you.2. The zProperties contains configuration properties we want the ZenPack to add to theZenoss system when it isinstalled.Note that DEFAULTS is not added as configuration property. It is a special value that will cause it’s properties tobe added as the default for all of the other listed zProperties. Specifically in this case it will cause the categoryof zWundergroundAPIKey and zWundergroundLocations to be set to Weather Underground. This is aconvenience to avoid having to repeatedly type the category for each added property.The zWundergroundAPIKey zProperty has an empty dictionary ({}). This is because we want it to be a stringtype with an empty default value. These happen to be the defaults so they don’t need to be specified.The zWundergroundLocations property uses the lines type which allows the user to specify multiple lines of text.Each line will be turned into an element in a list which you can see is also how the default value is specified. Theidea here is that unless the user configures otherwise, we will default to monitoring weather alerts and conditionsfor Austin, TX, San Jose, CA, and Annapolis, MD.3. The classes contains each of the object classes we want the ZenPack to add.In this case we’re adding WundergroundDevice which because base is set to zenpacklib.Device will be a subclassor specialization of the standard Zenoss device type. We’re also adding WundergroundLocation which becausebase is set to zenpacklib.Component will be a subclass of the standard component type.The label for each is simply the human-friendly name that will be used to refer to the resulting objects whenthey’re seen in theZenoss web interface.The properties for WundergroundLocation are extra bits of data we want to model from the API and show to theuser in the web interface. order will be used to show the properties in the defined order, and setting grid_display1.1. ZenPack Development Guide 46

ZenossLabsDocumentation, Releaseto false for api_link will allow it be shown in the details panel of the component, but not in the component grid.4. class_relationships uses the relationships_from_yuml helper to succinctly define a relationship stating WundergroundDevicecan contain many WundergroundLocation objects.5. Finally CFG.create() create what has been defined in the ZenPackSpec. Without this call, nothing wouldhappen.Reinstall the ZenPackBecause you added new zProperties you must now reinstall the ZenPack. This is required because new zPropertiesonly get installed into Zenoss when the ZenPack is installed. Everything else in the ZenPackSpec is created each timeZenoss starts.Run the following command to reinstall the ZenPack and keep it in development mode.zenpack --link --install $ZP_TOP_DIRPython Modeler PluginNow that we’ve created a WundergroundLocation component type, we need to create a modeler plugin to createlocations in the database. We’re dealing with a custom HTTP API, so we’ll want to base our modeler plugin on thePythonPlugin class. This gives us full control of both the collection and processing of the modeling data.The modeler plugin will pass each location the user has specified in the zWundergroundLocations property to WeatherUnderground’s AutoComplete API to retrieve some basic information about the location, and very importantly the l(link) that uniquely identifies the location. The link will later be used to monitor the alerts and conditions for thelocation.Create WeatherUnderground.Locations Modeler PluginUse the following steps to create our modeler plugin.1. Make the directory that will contain our modeler plugin.mkdir -p $ZP_DIR/modeler/plugins/WeatherUnderground2. Create or dunder-init files.touch $ZP_DIR/modeler/__init__.pytouch $ZP_DIR/modeler/plugins/__init__.pytouch $ZP_DIR/modeler/plugins/WeatherUnderground/__init__.pyThese empty files are mandatory if we ever expect Python to import modules from these directories.3. Create $ZP_DIR/modeler/plugins/WeatherUnderground/ with the followingcontents."""Models locations using the Weather Underground API."""# stdlib Importsimport json# Twisted Importsfrom twisted.internet.defer import inlineCallbacks, returnValuefrom twisted.web.client import getPage1.1. ZenPack Development Guide 47

ZenossLabsDocumentation, Release# Zenoss Importsfrom Products.DataCollector.plugins.CollectorPlugin import PythonPluginclass Locations(PythonPlugin):"""Weather Underground locations modeler plugin."""relname = ’wundergroundLocations’modname = ’’requiredProperties = (’zWundergroundAPIKey’,’zWundergroundLocations’,)deviceProperties = PythonPlugin.deviceProperties + [email protected] collect(self, device, log):"""Asynchronously collect data from device. Return a deferred.""""%s: collecting data", = getattr(device, ’zWundergroundAPIKey’, None)if not apikey:log.error("%s: %s not set. Get one from",,’zWundergroundAPIKey’)returnValue(None)locations = getattr(device, ’zWundergroundLocations’, None)if not locations:log.error("%s: %s not set.",,’zWundergroundLocations’)returnValue(None)rm = self.relMap()for location in locations:try:response = yield getPage(’{query}’.format(query=location))response = json.loads(response)except Exception, e:log.error("%s: %s",, e)returnValue(None)for result in response[’RESULTS’]:rm.append(self.objectMap({1.1. ZenPack Development Guide 48

ZenossLabsDocumentation, ReleasereturnValue(rm)’id’: self.prepId(result[’zmw’]),’title’: result[’name’],’api_link’: result[’l’],’country_code’: result[’c’],’timezone’: result[’tzs’],}))def process(self, device, results, log):"""Process results. Return iterable of datamaps or None."""return resultsWhile it looks like there’s quite a bit of code in this modeler plugin, a lot of that is the kind of error handlingyou’d want to do in a real modeler plugin. Let’s walk through some of the highlights.(a) ImportsWe import the standard json module because the Weather Underground API returns json-encoded responses.We import inlineCallBacks and returnValue because the PythonPlugin.collect method should return a Deferredso that it can be executed asynchronously by zenmodeler. You don’t need to use inlineCallbacks, butI find it to be a nice way to make Twisted’s asynchronous callback-based code look more procedural andbe easier to understand. I recommend Dave Peticolas’ excellent Twisted Introduction for learning moreabout Twisted. inlineCallback is covered in part 17.We also import Twisted’s getPage function. This is an extremely easy to use function for asynchronouslyfetching a URL.We import PythonPlugin because it will be the base class for our modeler plugin class. It’s the best choicefor modeling data from HTTP APIs.(b) Locations ClassRemember that your modeler plugin’s class name must match the filename or Zenoss won’t be able to loadit. So because we named the file we must name the class Locations.(c) relname and modname PropertiesThese should be defined in this way for modeler plugins that fill a single relationship like we’re doing inthis case. It states that this modeler plugin creates objects in the device’s wundergroundLocations relationship,and that it creates objects of the within this relationship.Where does relname come from? It comes from the [WundergroundDevice]++-[WundergroundLocation]relationship we defined in Because it’s a to-many relationship to the WundergroundLocationtype, zenpacklib will name the relationship by lowercasing the first letter and adding an “s” to theend to make it plural.Where does modname come from? It will be ..So because we defined the WundergroundLocation class in, and theZenPack’s name is, the modname will be deviceProperties PropertiesThe class’ deviceProperties property provides a way to get additional device properties available to yourmodeler plugin’s collect and process methods. The default properties that will be available for a Python-Plugin are: id, manageIp, _snmpLastCollection, _snmpStatus, and zCollectorClientTimeout. Our modeler1.1. ZenPack Development Guide 49

ZenossLabsDocumentation, Releaseplugin will also need to know what values the user has set for zWundergroundAPIKey and zWunderground-Locations. So we add those to the defaults.(e) collect MethodThe collect method is something PythonPlugin has, but other base modeler plugin types like SnmpPlugindon’t. This is because you must write the code to collect the data to be processed, and that’s exactly whatyou should do in the collect method.While the collect method can return either normal results or a Deferred, it is highly recommend to returna Deferred to keep zenmodeler from blocking while your collect method executes. In this examplewe’ve decorated the method with @inlineCallbacks and have returned out data at the endwith returnValue(rm). This causes it to return a Deferred. By decorating the method [email protected] we’re able to make an asynchronous request to the Weather Underground APIwith response = yield getPage(...).The first thing we do in the collect method is log an informational message to let the user know whatwe’re doing. This log will appear in zenmodeler.log, or on the console if we run zenmodeler in theforeground, or in the web interface when the user manually remodels the device.Next we make sure that the user has configured a value for zWundergroundAPIKey. This isn’t strictlynecessary here because the modeler plugin only uses Weather Underground’s AutoComplete API whichdoesn’t require an API key. I put this check here because I didn’t want to get into a situation where thelocations modeled successfully, but then failed to collect because an API key wasn’t set.Next we make suer that the user as configured at least one location in zWundergroundLocations. This ismandatory because this controls what locations will be modeled.Next we create rm which is a common convention we use in modeler plugins and stands for RelationshipMap.Because we set the relname and modname class properties this will create a RelationshipMapwith it’s relname and modname set to the same.Now we iterate through each location making a call to the AutoComplete API for each. For each matchinglocation in the response we will append an ObjectMap to rm with some key properties set.• id is mandatory and should be set to a value unique to all components on the device. If you look backthe example AutoComplete response you’ll see that the zmw property is useful for this purpose. Notethat prepId should always be used for id. It will make any string safe to use as a Zenoss id.• title will default to the value of id if it isn’t set. It’s usually a good idea to explicitly set it as we’redoing here. It should be a human-friendly label for the component. The location’s name is a goodcandidate for this. It will look something like “Austin, Texas”.• api_link is a property we defined for the WundergroundLocation class in This iswhere we’ll store the returned link or l property. This will be important for monitoring the alerts andconditions of the location later on.• country_code is another property we defined. It’s purely informational and will simply be shown tothe user when they’re viewing the location in the web interface.• timezeone is another property we defined just for informational purposes.(f) process MethodThe process method is usually where you take the data in the results argument and process it into DataMapsto return. However, in the case of PythonPlugin modeler plugins, the data returned from the collect methodwill be passed into process as the results argument. In this case that is already complete processed data.So we just return it.4. Restart Zenoss.1.1. ZenPack Development Guide 50

ZenossLabsDocumentation, ReleaseAfter adding a new modeler plugin you must restart Zenoss. If you’re following the Running a Minimal Zenossinstructions you really only need to restart zopectl and zenhub.That’s it. The modeler plugin has been created. Now we just need to do some Zenoss configuration to allow us to useit.Add WeatherUnderground Device ClassTo support adding our special WundergroundDevice devices that we defined in to Zenoss we mustcreate a new device class. This will give us control of the zPythonClass configuration property that defines what typeof devices will be created. It will also allow us to control what modeler plugins and monitoring templates will be used.Use the following steps to add the device class.1. Navigate to the Infrastructure view.2. Select the root of the DEVICES tree.3. Click the + button at the bottom of the list to add a new organizer.4. Set the Name to WeatherUnderground then click SUBMIT.The new WeatherUnderground device will now be selected. We’ll want to check on some important configurationproperties using the following steps.Set Device Class Properties1. Click the DETAILS button at the top of the list.2. Select Configuration Properties. Set the following properties.• zPythonClass:• zPingMonitorIgnore: true• zSnmpMonitorIgnore: true3. Select Modeler Plugins from the left navigation pane.You’ll likely find a list of selected modeler plugins that looks something like the following.• zenoss.snmp.NewDeviceMap• zenoss.snmp.DeviceMap• zenoss.snmp.InterfaceMap• zenoss.snmp.RouteMap4. Remove all of the modeler plugins from the Selected list.5. Move WeatherUnderground.Locations from the Available to the Selected list.6. Click Save.Add the WeatherUnderground Device Class to the ZenPack Now that we’ve setup the WeatherUnderground deviceclass, it’s time to add it to our ZenPack using the following steps. Adding a device class to your ZenPack causesall settings in that device class to be added to the ZenPack. This includes modeler plugin configuration, configurationproperty values and monitoring templates.1. Make sure that you have the WeatherUnderground device class selected in the Infrastructure view.2. Choose Add to ZenPack from the gear menu in the bottom-left.1.1. ZenPack Development Guide 51

ZenossLabsDocumentation, Release3. Select then click SUBMIT.4. Export the ZenPack. (Exporting a ZenPack)Add the Device This would be a good time to add a device to the new device class. There aremany ways to add devices to Zenoss, but if you’re Running a Minimal Zenoss you may not be running zenjobs andsome of them won’t work. During ZenPack development it’s often easiest to use zendisc to add devices.Run the following command to add a device.zendisc run --deviceclass=/WeatherUnderground --device=wunderground.comYou should see output similar to the following:INFO zen.ZenModeler: Collecting for device wunderground.comINFO zen.ZenModeler: No WMI plugins found for wunderground.comINFO zen.ZenModeler: Python collection device wunderground.comINFO zen.ZenModeler: plugins: WeatherUnderground.LocationsINFO zen.PythonClient: collecting dataERROR zen.PythonClient: zWundergroundAPIKey not set. Get one from http://www.wunderINFO zen.PythonClient: Python client finished collection for wunderground.comWARNING zen.ZenModeler: The plugin WeatherUnderground.Locations returned no results.INFO zen.ZenModeler: No change in configuration detectedINFO zen.ZenModeler: No command plugins found for wunderground.comINFO zen.ZenModeler: SNMP monitoring off for wunderground.comINFO zen.ZenModeler: No portscan plugins found for wunderground.comINFO zen.ZenModeler: Scan time: 0.02 secondsINFO zen.ZenModeler: Daemon ZenModeler shutting downNote: The error about zWundergroundAPIKey not being set is expected because we haven’t set it. The solutionis to go to the device in the web interface and add your API key to the zWundergroundAPIKeyconfiguration property. After adding the API key you should remodel the device.Another good way to add device to Zenoss is with zenbatchload. Using zenbatchload also allows us to set configurationproperties such as zWundergroundAPIKey as the device is added.Create a wunderground.zenbatchload file with the following contents:/Devices/ zWundergroundAPIKey=’’, zWundergroundLocations=[’Austin, TX’, ’Des MoiNow run the following command to load from that file:zenbatchload wunderground.zenbatchloadYou should now be able to see a list of locations on the device!Using PythonCollectorThe PythonCollector ZenPack adds the capability to write high performance datasources in Python. They will becollected by the zenpython daemon that comes with the PythonCollector ZenPack.I’d recommend reading the PythonCollector Documentation for more information.1.1. ZenPack Development Guide 52

ZenossLabsDocumentation, ReleaseInstalling PythonCollectorThe first thing we’ll need to do is to make sure the PythonCollector ZenPack is installed on our system. If it isn’t,follow these instructions to install it.1. Download the latest release from the PythonCollector page.2. Run the following command to install the ZenPack:zenpack --install ZenPacks.zenoss.PythonCollector-.egg3. Restart Zenoss.Add PythonCollector Dependency Since we’re going to be using PythonCollector capabilities in our ZenPack wemust now update our ZenPack to define the dependency.Follow these instructions to define the dependency.1. Navigate to Advanced -> Settings -> ZenPacks.2. Click into the ZenPack.3. Check ZenPacks.zenoss.PythonCollector in the list of dependencies.4. Click Save.5. Export the ZenPack. (Exporting a ZenPack)Python Collector Plugin (Events)Now that we have one or more locations modeled on our device, we’ll want to start monitoringeach location. Using PythonCollector we have the ability to create events, record datapoints and even update themodel. We’ll start with an example that creates events from weather alert data.The idea will be that we’ll create events for locations that have outstanding weather alerts such as tornado warnings.We’ll try to capture severity data so tornado warnings are higher severity events than something like a frost advisory.Create Alerts Data Source PluginTo make Zenoss able to run the data source plugin we’re going to create, we must first make sure the PythonCollectorZenPack is installed, and that our ZenPack depends on it. See the Using PythonCollector section if you haven’t alreadydone this.Follow these steps to create the Alerts data source plugin:1. Create $ZP_DIR/ with the following contents."""Monitors current conditions using the Weather Underground API."""# Loggingimport loggingLOG = logging.getLogger(’zen.WeatherUnderground’)# stdlib Importsimport jsonimport time# Twisted Importsfrom twisted.internet.defer import inlineCallbacks, returnValue1.1. ZenPack Development Guide 53

ZenossLabsDocumentation, Releasefrom twisted.web.client import getPage# PythonCollector Importsfrom ZenPacks.zenoss.PythonCollector.datasources.PythonDataSource import (PythonDataSourcePlugin,)class Alerts(PythonDataSourcePlugin):"""Weather Underground alerts data source plugin."""@classmethoddef config_key(cls, datasource, context):return (context.device().id,datasource.getCycleTime(context),,’wunderground-alerts’,)@classmethoddef params(cls, datasource, context):return {’api_key’: context.zWundergroundAPIKey,’api_link’: context.api_link,’location_name’: context.title,}@inlineCallbacksdef collect(self, config):data = self.new_data()for datasource in config.datasources:try:response = yield getPage(’{api_key}/alerts{api_link}.json’.format(api_key=datasource.params[’api_key’],api_link=datasource.params[’api_link’]))response = json.loads(response)except Exception:LOG.exception("%s: failed to get alerts data for %s",,datasource.location_name)continuefor alert in response[’alerts’]:severity = Noneif int(alert[’expires_epoch’])

ZenossLabsDocumentation, Releaseseverity = 2data[’events’].append({’device’:,’component’: datasource.component,’severity’: severity,’eventKey’: ’wu-alert-{}’.format(alert[’type’]),’eventClassKey’: ’wu-alert’,returnValue(data)’summary’: alert[’description’],’message’: alert[’message’],’wu-description’: alert[’description’],’wu-date’: alert[’date’],’wu-expires’: alert[’expires’],’wu-phenomena’: alert[’phenomena’],’wu-significance’: alert[’significance’],’wu-type’: alert[’type’],})Let’s walk through this code to explain what is being done.(a) LoggingThe first thing we do is import logging and create LOG as our logger. It’s important that the name of thelogger in the logging.getLogger() begins with zen.. You will not see your logs otherwise.The stdlib and Twisted imports are almost identical to what we used in the modeler plugin, and they’reused for the same purposes.Finally we import PythonDataSourcePlugin from the PythonCollector ZenPack. This is the class our datasource plugin will extend, and basically allows us to write code that will be executed by the zenpythoncollector daemon.(b) Alerts ClassUnlike our modeler plugin, there’s no need to make the plugin class’ name the same as the filename. Aswe’ll see later when we’re setting up the monitoring template that will use this plugin, there’s no specificname for the file or the class required because we configure where to find the plugin in the datasourceconfiguration within the monitoring template.(c) config_key Class MethodThe config_key method must have the @classmethod decorator. It is passed datasource, and context.The datasource argument will be the actual datasource that the user configures in the monitoring templatessection of the web interface. It has properties such as eventClass, severity, and as you can see a getCycleTime()method that returns the interval at which it should be polled. The context argument will be theobject to which the monitoring template and datasource is bound. In our case this will be a location objectsuch as Austin, TX.The purpose of the config_key method is to split monitoring configuration into tasks that will be executedby the zenpython daemon. The zenpython daemon will create one task for each unique value returned fromconfig_key. It should be used to optimize the way data is collected. In some cases it is possible to make asingle query to an API to get back data for many components. In these cases it would be wise to from the config_key so we get one task for all components.In our case, the Weather Underground API must be queried once per location so it makes more sense toput in the config_key so we get one task per location.1.1. ZenPack Development Guide 55

ZenossLabsDocumentation, ReleaseThe value returned by config_key will be used when zenpython logs. So adding something likewunderground-alerts to the end makes it easy to see logs related to collecting alerts in the log file.The config_key method will only be executed by zenhub. So you must restart zenhub if you make changesto the config_key method. This also means that if there’s an exception in the config_key method it willappear in the zenhub log, not zenpython.(d) params Class MethodThe params method must have the @classmethod decorator. It is passed the same datasource andcontext arguments as config_key.The purpose of the params method is to copy information from theZenoss database into the config.datasources[*]that will be passed as an argument to the collect method. Since the collect methodis run by zenpython it won’t have direct access to the database, so it relies on the params method toprovide it with any information it will need to collect.In our case you can see that we’re copying the context’s zWundergroundAPIKey, api_link and title properties.All of these will be used in the collect method.Just like the config_key method, params will only be executed by zenhub. So be sure to restart zenhub ifyou make changes, and look in the zenhub log for errors.(e) collect MethodThe collect method does all of the real work. It will be called once per cycletime. It gets passed a configargument which for the most part has two useful properties: and config.datasources. config.idwill be the device’s id, and config.datasources is a list of the datasources that need to be collected.You’ll see in the collect method that each datasource in config.datasources has some useful properties.datasource.component will be the id of the component against which the datasource is run, or blank inthe case of a device-level monitoring template. datasource.params contains whatever the params methodreturned.Within the body of the collect method we see that we create a new data variable using data =self.new_data(). data is a place where we stick all of the collected events, values and maps. datalooks like the following:data = {’events’: [],’values’: defaultdict(, {}),’maps’: [],}Next we iterate over every configured datasource. For each one we make a call to Weather Underground’sAlerts API, then iterate over each alert in the response creating an event for each.The following standard fields are being set for every event. You should read Zenoss’ event managementdocumentation if the purpose of any of these fields is not clear. I highly recommend setting all of thesefields to an appropriate value for any event you send into Zenoss to improve the ability of Zenoss andZenoss’ operators to manage the events.• device: Mandatory. The device id related to the event.• component: Optional. The component id related to the event.• severity: Mandatory. The severity for the event.• eventKey: Optional. A further uniqueness key for the event. Used for de-duplication and clearing.• eventClassKey: Optional. An identifier for the type of event. Used during event class mapping.• summary: Mandatory: A (hopefully) short summary of the event. Truncated to 128 characters.1.1. ZenPack Development Guide 56

ZenossLabsDocumentation, Release• message: Optional: A longer text description of the event. Not truncated.You will also see many wu-* fields being added to the event. Zenoss allows arbitrary fields on events soit can be a good practice to add any further information you get about the event in this way. It can makeunderstanding and troubleshooting the resulting event easier.Finally we return data with all of events we appended to it. zenpython will take care of getting the eventssent from this point.2. Restart Zenoss.After adding a new datasource plugin you must restart Zenoss. If you’re following the Running a MinimalZenoss instructions you really only need to restart zenhub.That’s it. The datasource plugin has been created. Now we just need to do some Zenoss configuration to allow us touse it.Configure Monitoring TemplatesRather than use the web interface to manually Add a Monitoring Template, we’ll jump to the next section on UsingYAML Templates to show how we can describe a monitoring template using YAML.Using YAML TemplatesOrdinarily monitoring templates are built within theZenoss web interface. If you want one of these templates includedwith a ZenPack, you simply use the web interface to add the template to any of the ZenPacks you have in development.Sometimes you know that you have a lot of monitoring templates to create, or the monitoring templates have a lot ofdatasource, datapoints, thresholds and graphs. It can be tedious and error-prone to add all of these through the webinterface. In these cases it may be preferable to load monitoring templates from files.There’s a script available called load-templates that makes it possible to load monitoring templates from YAML sourcefiles.Download load-templatesRun the following commands to download the load-templates script into your ZenPack:cd $ZP_TOP_DIRwget 755 load-templatesNote: Note that we’re putting this script into $ZP_TOP_DIR, not $ZP_DIR. This isn’t required, but it’s a goodpractice. Files located in $ZP_TOP_DIR won’t by default be included in your ZenPack once it’s built. Since loadtemplatesis only used by the ZenPack developer, it doesn’t need to be included in the built ZenPack.This script requires that the PyYAML library be installed on your system. You can run the following command as thezenoss user to install it:easy_install PyYAML1.1. ZenPack Development Guide 57

ZenossLabsDocumentation, ReleaseCreate templates.yamlWe now want to create the YAML file that can be used to create a monitoring template that will be bound to ourlocations and execute our Alerts data source plugin.Follow these steps to create this monitoring template:1. Create $ZP_TOP_DIR/templates.yaml with the following contents./WeatherUnderground/Location:description: Weather Underground location monitoring.targetPythonClass: Pythonplugin_classname: "600"At least some of this should be self-explanatory. The YAML vocabulary has been designed to be as intuitive andconcise as possible. Let’s walk through it.(a) The highest-level element (based on indentation) is /WeatherUnderground/Location. This means to createa Location monitoring template in the /WeatherUnderground device class.Note: Because we’re using zenpacklib the monitoring template must be called Location because the isthe label for the WundergroundLocation class to which we want the template bound.(b) The description is for documentation purposes and should describe the purpose of the monitoring template.(c) The targetPythonClass is a hint to what type of object the template is meant to be bound to. Currently thisis only used to determine if users should be allowed to manually bind the template to device classes ordevices. Providing a valid component type like we’ve done prevents users from making this mistake.(d) Next we have datasources with a single alerts datasource defined.The alerts datasource only has three properties:• type: This is what makes zenpython collect the data.• plugin_classname: This is the fully-qualified class name for the PythonDataSource plugin we createdthat will be responsible for collecting the datasource.• cycletime: The interval in seconds at which this datasource should be collected.2. Run the following commands to create the monitoring template defined in $ZP_TOP_DIR./load-templates templates.yaml3. Navigate to Advanced -> Monitoring Templates in the web interface to verify that the Location monitoringtemplate has been created as defined.4. Export the ZenPack. (Exporting a ZenPack)The /WeatherUnderground device class is already part of our ZenPack, and we put the Location monitoring templateinto that device class. So exporting causes the template to be dumped into the ZenPack’s objects.xmlfile.1.1. ZenPack Development Guide 58

ZenossLabsDocumentation, ReleaseTest Monitoring Weather AlertsTesting this is a bit tricky since we’ll have to be monitoring a location that currently has an active weather alert.Fortunately there’s an easy way to find one of these locations.Follow these steps to test weather alert monitoring:1. Go to the following URL for the current severe weather map of the United States. Click on one of the colored areas. Orange and red are more exciting. This will take you to the text of thewarning. It should reference city or county names.3. Update zWundergroundLocations on the device to add one of the cities or counties that hasan active weather alert. For example, “Buffalo, South Dakota”.4. Remodel the device then verify that the new location is modeled.5. Run the following command to collect from run -v10 --device=wunderground.comThere will be a lot of output from this command, but we’re mainly looking for an event to be sent for the weatheralert. It will look similar to the following output:DEBUG zen.zenpython: Queued event (total of 1) {’rcvtime’: 1403112635.631883, ’wu-type’: u’FIR’,Python Collector Plugin (Data Points)We’ve already created a data source plugin that creates Zenoss events for weather alerts. Now we want to use theWeather Underground Conditions API to monitor current weather conditions for each location. The purpose of this isto illustrate that these Python data source plugins can also be used to collect datapoints.Create Conditions Data Source PluginFollow these steps to create the Conditions data source plugin:1. Add the following contents to the end of $ZP_DIR/ of the Conditions plugin is almost identical to the Alerts plugin so I won’t repeat whatcan be read back in that section. The main difference starts at the current_observation =response[’current_observation’] line of the collect method.It grabs the current_observation data from the response then iterates over every datapoint configured on thedatasource. This is a nice approach because it allows for some user-flexibility in what datapoints are capturedfrom the Conditions API. If the API made temp_c and temp_f available, we could choose to collect temp_c justby adding a datapoint by that name.The following line is the most important in terms of explaining how to have your plugin return datapoint values.Basically we just stick (value, ’N’) into the