diff --git a/docs/jsonformat.rst b/docs/jsonformat.rst
index 5541535..d0b7e46 100644
--- a/docs/jsonformat.rst
+++ b/docs/jsonformat.rst
@@ -24,7 +24,8 @@ Json Options for a ZenPack::
"organizers": [],
"zProperties": [],
"deviceClasses": [],
- "relationships": []
+ "relationships": [].
+ "discoveryMappings": []
}
* id: A string defining the unique name of your Zenpack. [required]
@@ -39,6 +40,7 @@ Json Options for a ZenPack::
* zproperties: An Array of :ref:`zproperty` elements. [optional]
* deviceClasses: An Array of :ref:`deviceClass` elements. [optional]
* relationships: An Array of :ref:`relationship` elements. [optional]
+* discoveryMappings: An Array of :ref:`discoveryMapping` elements. [optional]
* organizers: An Array of :ref:`organizer` elements. [optional]
.. _zproperties:
@@ -258,6 +260,21 @@ Json Options for a DeviceType::
* impactedBy: An Array of components that this component is impactedBy.
* The component can be in :ref:`shorthand` notation.
+.. _discoveryMapping:
+
+discoveryMapping {}
+-------------------
+
+Json Options for a DiscoveryMapping::
+
+ "discoveryMappings": [{
+ "oid": '1.3.6.1.5',
+ "deviceClass": 'Network/Switch/Cisco/Foo'
+ }]
+
+* oid: a devices SnmpOid to match
+* deviceClass: The destination device Class
+
.. _properties:
properties []
diff --git a/examples/autoclassification.json b/examples/autoclassification.json
new file mode 100644
index 0000000..d083ba2
--- /dev/null
+++ b/examples/autoclassification.json
@@ -0,0 +1,9 @@
+{
+ "deviceClasses": [ { "path": "Device" } ],
+ "discoveryMappings": [
+ { "oid": "1.2.3.4.5",
+ "deviceClass": "Network/Example"},
+ { "oid": "1.2.3.4.5.6",
+ "deviceClass": "Network/Example"}],
+ "id": "ZenPacks.training.AutoClassification"
+}
diff --git a/setup.py b/setup.py
index e653d24..7e7b913 100644
--- a/setup.py
+++ b/setup.py
@@ -47,6 +47,7 @@
'Mock',
'nose',
'pep8',
+ 'argparse'
]
diff --git a/zpg/AutoClassificationZcml.py b/zpg/AutoClassificationZcml.py
new file mode 100644
index 0000000..f4b5f28
--- /dev/null
+++ b/zpg/AutoClassificationZcml.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+##############################################################################
+#
+# Copyright (C) Zenoss, Inc. 2013, all rights reserved.
+#
+# This content is made available according to terms specified in the LICENSE
+# file at the top-level directory of this package.
+#
+##############################################################################
+
+from ._defaults import Defaults
+from ._zenoss_utils import KlassExpand, zpDir
+from .Template import Template
+defaults = Defaults()
+
+
+class AutoClassificationZcml(Template):
+ """Build the autoclassification.zcml file"""
+
+ def __init__(self,
+ zenpack,
+ ):
+ '''Args:
+ zenpack: ZenPack class instance
+
+ '''
+
+ super(AutoClassificationZcml, self).__init__(zenpack)
+ self.source_template = 'autoclassification.zcml.tmpl'
+ self.zenpack = zenpack
+ self.components = zenpack.components
+
+ self.dest_file = "%s/autoclassification.zcml" % zpDir(zenpack)
+
+ def discoveryMappings(self):
+ if self.zenpack.discoveryMappings:
+ return True
+ return False
+
+ def write(self):
+ '''Write the file'''
+
+ if self.discoveryMappings():
+ self.processTemplate()
diff --git a/zpg/Component.py b/zpg/Component.py
index ddc9fad..598f48a 100644
--- a/zpg/Component.py
+++ b/zpg/Component.py
@@ -312,12 +312,13 @@ def displayInfo(self):
imports = "from Products.Zuul.infos.device import DeviceInfo"
else:
imports = "from Products.Zuul.infos.component import ComponentInfo"
- if self.properties:
- self.imports.append(imports)
- return True
- if self.ManyRelationships():
- self.imports.append(imports)
- return True
+ if self.componentInZenPackNameSpace():
+ if self.properties:
+ self.imports.append(imports)
+ return True
+ if self.ManyRelationships():
+ self.imports.append(imports)
+ return True
return False
def displayIInfo(self):
@@ -327,13 +328,14 @@ def displayIInfo(self):
imports = "from %s import IDeviceInfo" % name
else:
imports = "from %s.component import IComponentInfo" % name
- for p in self.properties.values():
- if p.detailDisplay:
+ if self.componentInZenPackNameSpace():
+ for p in self.properties.values():
+ if p.detailDisplay:
+ self.imports.append(imports)
+ return True
+ if self.ManyRelationships():
self.imports.append(imports)
return True
- if self.ManyRelationships():
- self.imports.append(imports)
- return True
return False
@classmethod
@@ -445,9 +447,16 @@ def impactSingle(self, impactee):
return True
return False
+ def componentInZenPackNameSpace(self):
+ 'return true if the component id startswith the zenpack id'
+ return self.id.startswith(self.zenpack.id)
+
def write(self):
"""Write the component files"""
self.updateImports()
self.findUpdateComponents()
self.convertImpactStringsToRealComponents()
- self.processTemplate()
+
+ # Only write components that are prefixed in this zenpacks namespace.
+ if self.componentInZenPackNameSpace():
+ self.processTemplate()
diff --git a/zpg/ComponentJS.py b/zpg/ComponentJS.py
index 7419d5d..c008b0c 100644
--- a/zpg/ComponentJS.py
+++ b/zpg/ComponentJS.py
@@ -23,8 +23,9 @@ def __init__(self, deviceClass):
self.deviceClass = deviceClass
self.zenpack = self.deviceClass.zenpack
self.zPythonClass = self.deviceClass.zPythonClass
- self.ConfigureComponent = "%s.%s" % (
- self.zPythonClass, self.zPythonClass.split('.')[-1])
+ self.ConfigureComponent = self.zPythonClass
+ #self.ConfigureComponent = "%s.%s" % (
+ # self.zPythonClass, self.zPythonClass.split('.')[-1])
self.source_template = 'component.js.tmpl'
self.dest_file = "%s/resources/js/%s.js" % (
diff --git a/zpg/Configure.py b/zpg/Configure.py
index 31aeb33..e93691c 100644
--- a/zpg/Configure.py
+++ b/zpg/Configure.py
@@ -43,6 +43,11 @@ def impactAdapters(self):
return True
return False
+ def discoveryMappings(self):
+ if self.zenpack.discoveryMappings:
+ return True
+ return False
+
# TODO
# Router and facade
# custom device loaders
diff --git a/zpg/DiscoveryMapping.py b/zpg/DiscoveryMapping.py
new file mode 100644
index 0000000..e01080c
--- /dev/null
+++ b/zpg/DiscoveryMapping.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+#
+# Copyright (C) Zenoss, Inc. 2013, all rights reserved.
+#
+# This content is made available according to terms specified in the LICENSE
+# file at the top-level directory of this package.
+#
+#
+
+from .colors import error, warn, debug, info, green, red, yellow
+
+
+class DiscoveryMapping(object):
+ ''' Discovery Mapping Object '''
+
+ discoveryMappings = {}
+
+ def __init__(self,
+ zenpack,
+ oid,
+ deviceClass,
+ *args,
+ **kwargs
+ ):
+ """Args:
+ zenpack: the containing zenpack
+ oid: a devices snmpoid to match
+ deviceClass: destination device class to move the
+ discovered device
+ """
+ self.zenpack = zenpack
+ self.oid = oid
+ self.deviceClass = deviceClass
+
+ for key in kwargs:
+ do_not_warn = False
+ clsname = self.__class__.__name__
+ layer = "%s:%s" % (clsname, self.name)
+ msg = "WARNING: [%s] unknown keyword ignored in file: '%s'"
+ margs = (layer, key)
+ if not do_not_warn:
+ warn(self.logger, yellow(msg) % margs)
+
+ DiscoveryMapping.discoveryMappings[self.oid] = self
+ self.zenpack.registerDiscoveryMapping(self)
diff --git a/zpg/ImpactPy.py b/zpg/ImpactPy.py
index cbdfc08..b434190 100644
--- a/zpg/ImpactPy.py
+++ b/zpg/ImpactPy.py
@@ -50,8 +50,17 @@ def updateImports(self):
if istring not in self.imports:
self.imports.append(istring)
+ def impactAdapters(self):
+ '''Return true if there are any impact relationships'''
+ for c in self.components.values():
+ if c.hasImpact():
+ return True
+ return False
+
def write(self):
'''Write the impact file'''
self.updateImports()
- self.processTemplate()
+
+ if self.impactAdapters():
+ self.processTemplate()
diff --git a/zpg/ImpactZcml.py b/zpg/ImpactZcml.py
index 71b3e9c..0019d7d 100644
--- a/zpg/ImpactZcml.py
+++ b/zpg/ImpactZcml.py
@@ -29,8 +29,16 @@ def __init__(self,
self.dest_file = "%s/impact.zcml" % zpDir(zenpack)
+ def impactAdapters(self):
+ '''Return true if there are any impact relationships'''
+ for c in self.components.values():
+ if c.hasImpact():
+ return True
+ return False
+
def write(self):
'''Write the impact file'''
#self.updateImports()
- self.processTemplate()
+ if self.impactAdapters():
+ self.processTemplate()
diff --git a/zpg/Templates/autoclassification.zcml.tmpl b/zpg/Templates/autoclassification.zcml.tmpl
new file mode 100644
index 0000000..ed4c2e0
--- /dev/null
+++ b/zpg/Templates/autoclassification.zcml.tmpl
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+#for $discoveryMapping in $zenpack.discoveryMappings.values
+
+
+#end for
+
+
+
+
diff --git a/zpg/Templates/configure.zcml.tmpl b/zpg/Templates/configure.zcml.tmpl
index 6954e59..137e592 100644
--- a/zpg/Templates/configure.zcml.tmpl
+++ b/zpg/Templates/configure.zcml.tmpl
@@ -81,4 +81,11 @@
-->
#end if
+#if $discoveryMappings
+
+#end if
diff --git a/zpg/ZenPack.py b/zpg/ZenPack.py
index fe16c79..ea02542 100644
--- a/zpg/ZenPack.py
+++ b/zpg/ZenPack.py
@@ -22,6 +22,7 @@
from .ComponentJS import ComponentJS
from .Configure import Configure
from .DirLayout import DirLayout
+from .DiscoveryMapping import DiscoveryMapping
from .DeviceClass import DeviceClass
from .License import License
from .ObjectsXml import ObjectsXml
@@ -33,6 +34,7 @@
from .ZenPackUI import ZenPackUI
from .ImpactPy import ImpactPy
from .ImpactZcml import ImpactZcml
+from .AutoClassificationZcml import AutoClassificationZcml
defaults = Defaults()
@@ -58,6 +60,7 @@ def __init__(self,
zProperties=None,
deviceClasses=None,
relationships=None,
+ discoveryMappings=None,
opts=None,
*args,
**kwargs
@@ -70,6 +73,7 @@ def __init__(self,
self.deviceClasses = {}
self.components = {}
self.relationships = {}
+ self.discoveryMappings = {}
self.organizers = {}
self.componentJSs = {}
self.zproperties = {}
@@ -107,16 +111,24 @@ def __init__(self,
self.zenpackUI = ZenPackUI(self)
self.objects_xml = ObjectsXml(self)
self.impact_zcml = ImpactZcml(self)
+ self.autoclassification_zcml = AutoClassificationZcml(self)
self.impact = ImpactPy(self)
if zProperties:
for zp in zProperties:
self.addZProperty(**zp)
+
if deviceClasses:
for dc in deviceClasses:
self.addDeviceClass(**dc)
+
if relationships:
for rel in relationships:
self.addRelation(**rel)
+
+ if discoveryMappings:
+ for mapping in discoveryMappings:
+ self.addDiscoveryMapping(**mapping)
+
# Make sure we create the organizers after the deviceClasses
# because we look up the zPythonClasses out of the deviceClasses
if organizers:
@@ -143,11 +155,33 @@ def addRelation(self, *args, **kwargs):
r = Relationship(self, *args, **kwargs)
return r
+ def addDiscoveryMapping(self, *args, **kwargs):
+ r = DiscoveryMapping(self, *args, **kwargs)
+ return r
+
def addOrganizer(self, *args, **kwargs):
o = Organizer(self, *args, **kwargs)
return o
- def addZProperty(self, name, type_='string', default='', Category=None):
+ def addZProperty(self, name, type_='string', default='',
+ Category=None, **kwargs):
+
+ for key in kwargs:
+ do_not_warn = False
+ layer = self.__class__.__name__
+ msg = "WARNING: JSON keyword ignored in layer '%s': '%s'"
+ margs = (layer, key)
+ if key == "Type":
+ msg = "WARNING: JSON keyword deprecated in '%s' layer. "\
+ "'%s' is now '%s'."
+ margs = (layer, key, key.lower())
+ self.type_ = kwargs[key]
+ elif key == "type":
+ self.type_ = type_ = kwargs[key]
+ do_not_warn = True
+ if not do_not_warn:
+ warn(self.logger, yellow(msg) % margs)
+
if type_ == 'string':
if not default.startswith('\''):
default = '\'' + default
@@ -155,6 +189,7 @@ def addZProperty(self, name, type_='string', default='', Category=None):
default = default + '\''
if not default.endswith('\''):
default = default + '\''
+
self.zproperties[name] = (name, default, type_, Category)
def registerComponent(self, component):
@@ -172,6 +207,9 @@ def registerDeviceClass(self, deviceClass):
def registerOrganizer(self, organizer):
self.organizers[organizer.id] = organizer
+ def registerDiscoveryMapping(self, discoveryMapping):
+ self.discoveryMappings[discoveryMapping.oid] = discoveryMapping
+
def __repr__(self):
return "%s \n\tAUTHOR: %s\n\tVERSION: %s\n\tLICENSE: %s" \
% (self.id, self.author, self.version, self.license)
@@ -226,4 +264,7 @@ def write(self, verbose=False):
# Create the impact.py
self.impact.write()
+ # Create the autoclassification.zcml
+ self.autoclassification_zcml.write()
+
self.updateGitTemplates()
diff --git a/zpg/_defaults.py b/zpg/_defaults.py
index 6b48e25..682dd02 100644
--- a/zpg/_defaults.py
+++ b/zpg/_defaults.py
@@ -26,7 +26,7 @@
'author': 'ZenossLabs ',
'author_email': 'labs@zenoss.com',
'description': 'A tool to assist building zenpacks.',
- 'version': '1.0.12',
+ 'version': '1.0.13',
'license': 'GPLv2',
'component_classes': [
'Products.ZenModel.DeviceComponent.DeviceComponent',
diff --git a/zpg/tests/test_Component.py b/zpg/tests/test_Component.py
index 1e7198d..9aab5fa 100644
--- a/zpg/tests/test_Component.py
+++ b/zpg/tests/test_Component.py
@@ -301,19 +301,19 @@ class TestDisplayInfo(SimpleSetup):
def test_properties(self):
dc = self.zp.addDeviceClass('Device', zPythonClass='a.b.c.d.Device')
- e = dc.addComponentType('a.Enclosure')
+ e = dc.addComponentType('Enclosure')
e.addProperty('foo')
self.assertEqual(e.displayInfo(), True)
def test_trueManyRelationships(self):
dc = self.zp.addDeviceClass('Device', zPythonClass='a.b.c.d.Device')
- e2 = dc.addComponentType('a.Enclosure2')
+ e2 = dc.addComponentType('Enclosure2')
e2.addComponentType('Drive2')
self.assertEqual(e2.displayInfo(), True)
def test_donotdisplayInfo(self):
dc = self.zp.addDeviceClass('Device', zPythonClass='a.b.c.d.Device')
- e = dc.addComponentType('a.Enclosure')
+ e = dc.addComponentType('Enclosure3')
self.assertEqual(e.displayInfo(), False)
@@ -321,19 +321,19 @@ class TestDisplayIInfo(SimpleSetup):
def test_properties(self):
dc = self.zp.addDeviceClass('Device', zPythonClass='a.b.c.d.Device')
- e = dc.addComponentType('a.Enclosure')
+ e = dc.addComponentType('Enclosure')
e.addProperty('foo')
self.assertEqual(e.displayIInfo(), True)
def test_trueManyRelationships(self):
dc = self.zp.addDeviceClass('Device', zPythonClass='a.b.c.d.Device')
- e2 = dc.addComponentType('a.Enclosure2')
+ e2 = dc.addComponentType('Enclosure2')
e2.addComponentType('Drive2')
self.assertEqual(e2.displayIInfo(), True)
def test_donotdisplayIInfo(self):
dc = self.zp.addDeviceClass('Device', zPythonClass='a.b.c.d.Device')
- e = dc.addComponentType('a.Enclosure')
+ e = dc.addComponentType('Enclosure3')
self.assertEqual(e.displayIInfo(), False)