Package Products :: Package ZenRelations :: Module ZenPropertyManager |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenRelations.ZenPropertyManager

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  import re
     15  import logging
     16  import exceptions
     17
     18  from OFS.PropertyManager import PropertyManager
     19  from zExceptions import BadRequest
     20  from Globals import DTMLFile
     21  from Globals import InitializeClass
     22  from Acquisition import aq_base, aq_chain
     23  from ZPublisher.Converters import type_converters
     24  from Products.ZenModel.ZenossSecurity import *
     25  from AccessControl import ClassSecurityInfo
     26  from Exceptions import zenmarker
     27  from Products.ZenWidgets.interfaces import IMessageSender
     28
     29  iszprop = re.compile("^z[A-Z]").search
     30
     31  from Products.ZenUtils.Utils import unused
     32
     33  log = logging.getLogger('zen.PropertyManager')
     34
     35  # Z_PROPERTIES is a list of (id, type, value) pairs that define all the
     36  # zProperties.  The values are set on dmd.Devices in the
     37  # buildDeviceTreeProperties of DeviceClass
     38  Z_PROPERTIES = [
     39
     40      # zPythonClass maps device class to python classs (separate from device
     41      # class name)
     42      ('zPythonClass', '', 'string'),
     43
     44      # zProdStateThreshold is the production state threshold at which to start
     45      # monitoring boxes
     46      ('zProdStateThreshold', 300, 'int'),
     47
     48      # zIfDescription determines whether or not the ifdescripion field is
     49      # displayed
     50      ('zIfDescription', False, 'boolean'),
     51
     52      # Snmp collection properties
     53      ('zSnmpCommunities', ['public', 'private'], 'lines'),
     54      ('zSnmpCommunity', 'public', 'string'),
     55      ('zSnmpPort', 161, 'int'),
     56      ('zSnmpVer', 'v1', 'string'),
     57      ('zSnmpTries', 2, 'int'),
     58      ('zSnmpTimeout', 2.5, 'float'),
     59      ('zSnmpSecurityName', '', 'string'),
     60      ('zSnmpAuthPassword', '', 'password'),
     61      ('zSnmpPrivPassword', '', 'password'),
     62      ('zSnmpAuthType', '', 'string'),
     63      ('zSnmpPrivType', '', 'string'),
     64      ('zRouteMapCollectOnlyLocal', False, 'boolean'),
     65      ('zRouteMapCollectOnlyIndirect', False, 'boolean'),
     66      ('zRouteMapMaxRoutes', 500, 'int'),
     67      ('zInterfaceMapIgnoreTypes', '', 'string'),
     68      ('zInterfaceMapIgnoreNames', '', 'string'),
     69      ('zFileSystemMapIgnoreTypes', [], 'lines'),
     70      ('zFileSystemMapIgnoreNames', '', 'string'),
     71      ('zFileSystemSizeOffset', 1.0, 'float'),
     72      ('zHardDiskMapMatch', '', 'string'),
     73      ('zSysedgeDiskMapIgnoreNames', '', 'string'),
     74      ('zIpServiceMapMaxPort', 1024, 'int'),
     75      ('zDeviceTemplates', ['Device'], 'lines'),
     76      ('zLocalIpAddresses', '^127|^0\\.0|^169\\.254|^224', 'string'),
     77      ('zLocalInterfaceNames', '^lo|^vmnet', 'string'),
     78
     79      # Status monitor properties
     80      ('zSnmpMonitorIgnore', False, 'boolean'),
     81      ('zPingMonitorIgnore', False, 'boolean'),
     82      ('zWmiMonitorIgnore', True, 'boolean'),
     83      ('zStatusConnectTimeout', 15.0, 'float'),
     84
     85      # DataCollector properties
     86      ('zCollectorPlugins', [], 'lines'),
     87      ('zCollectorClientTimeout', 180, 'int'),
     88      ('zCollectorDecoding', 'latin-1', 'string'),
     89      ('zCommandUsername', '', 'string'),
     90      ('zCommandPassword', '', 'password'),
     91      ('zCommandProtocol', 'ssh', 'string'),
     92      ('zCommandPort', 22, 'int'),
     93      ('zCommandLoginTries', 1, 'int'),
     94      ('zCommandLoginTimeout', 10.0, 'float'),
     95      ('zCommandCommandTimeout', 10.0, 'float'),
     96      ('zCommandSearchPath', [], 'lines'),
     97      ('zCommandExistanceTest', 'test -f %s', 'string'),
     98      ('zCommandPath', '/usr/local/zenoss/libexec', 'string'),
     99      ('zTelnetLoginRegex', 'ogin:.$', 'string'),
    100      ('zTelnetPasswordRegex', 'assword:', 'string'),
    101      ('zTelnetSuccessRegexList', ['\\$.$', '\\#.$'], 'lines'),
    102      ('zTelnetEnable', False, 'boolean'),
    103      ('zTelnetEnableRegex', 'assword:', 'string'),
    104      ('zTelnetTermLength', True, 'boolean'),
    105      ('zTelnetPromptTimeout', 10.0, 'float'),
    106      ('zKeyPath', '~/.ssh/id_dsa', 'string'),
    107      ('zMaxOIDPerRequest', 40, 'int'),
    108
    109      # Extra stuff for users
    110      ('zLinks', '', 'string'),
    111
    112      # Windows WMI collector properties
    113      ('zWinUser', '', 'string'),
    114      ('zWinPassword', '', 'password'),
    115      ('zWinEventlogMinSeverity', 2, 'int'),
    116      ('zWinEventlog', False, 'boolean'),
    117
    118      # zIcon is the icon path
    119      ('zIcon', '/zport/dmd/img/icons/noicon.png', 'string'),
    120      ]
    121
    122 -class PropertyDescriptor(object):
    123      """
    124      Transforms the property value based on its type.
    125
    126      Follows the Descriptor protocol defined at
    127      http://docs.python.org/reference/datamodel.html#descriptors
    128      """
    129
    130 -    def __init__(self, id, type, transformer):
    131          self.id = id
    132          self.type = type
    133          self.transformer = transformer
    134
    135 -    def __get__(self, instance, owner):
    136          """
    137          Returns self for class attribute access.  Returns the transformed
    138          value for instance attribute access.
    139          """
    140          try:
    141              if instance is None:
    142                  retval = self
    143              else:
    144                  self._migrate(instance)
    145                  value = instance._propertyValues[self.id]
    146                  retval = self._transform(instance, value, 'transformForGet')
    147              return retval
    148          except:
    149              raise AttributeError
    150
    151 -    def __set__(self, instance, value):
    152          """
    153          Transforms the value and sets it.
    154          """
    155          self._migrate(instance)
    156          self._set(instance, value)
    157
    158 -    def __delete__(self, instance):
    159          """
    160          Delete the property.
    161          """
    162          self._migrate(instance)
    163          del instance._propertyValues[self.id]
    164
    165 -    def _migrate(self, instance):
    166          """
    167          If the id is in __dict__ then move the value to the _propertyValues
    168          dictionary. Check to make sure that the type of this descriptor class
    169          and the type in the Zope OFS PropertyManager metadata are the same.
    170          """
    171          if not hasattr(instance, '_propertyValues'):
    172              instance._propertyValues = {}
    173          if self.id in vars(instance):
    174              self._set(instance, vars(instance)[self.id])
    175              del instance.__dict__[self.id]
    176              instance._p_changed = True
    177          for dct in instance._properties:
    178              if dct['id'] == self.id:
    179                  if dct['type'] != self.type:
    180                      dct['type'] = self.type
    181                      instance._p_changed = True
    182                  break
    183
    184 -    def _set(self, instance, value):
    185          """
    186          Transform and set the value in the _propertyValues dictionary.
    187          """
    188          valueToSet = self._transform(instance, value, 'transformForSet')
    189          instance._propertyValues[self.id] = valueToSet
    190
    191 -    def _transform(self, instance, value, method):
    192          """
    193          Lookup the transformer for the type and transform the value. The
    194          method parameter can be 'transformForGet' or 'transformForSet' and
    195          determines the transformer method that is called.
    196          """
    197          return getattr(self.transformer, method)(value)
    198
    199 -class ZenPropertyDoesNotExist(exceptions.ValueError):
    200      pass
    201
    202 -class ZenPropertyManager(object, PropertyManager):
    203      """
    204      ZenPropertyManager adds keyedselection type to PropertyManager.
    205      A keyedselection displayes a different name in the popup then
    206      the actual value the popup will have.
    207
    208      It also has management for zenProperties which are properties that can be
    209      inherited long the acquision chain.  All properties are for a branch are
    210      defined on a "root node" specified by the function which must be returned
    211      by the function getZenRootNode that should be over ridden in a sub class.
    212      Prperties can then be added further "down" the aq_chain by calling
    213      setZenProperty on any contained node.
    214
    215      ZenProperties all have the same prefix which is defined by iszprop
    216      this can be overridden in a subclass.
    217
    218      ZenPropertyManager overrides getProperty and getPropertyType from
    219      PropertyManager to support acquisition. If you want to query an object
    220      about a property, but do not want it to search the acquistion chain then
    221      use the super classes method or aq_base.  Example:
    222
    223          # acquires property from dmd.Devices
    224          dmd.Devices.Server.getProperty('zCollectorPlugins')
    225
    226          # does not acquire property from dmd.Devices
    227          PropertyManager.getProperty(dmd.Devices.Server, 'zCollectorPlugins')
    228
    229          # also does not acquire property from dmd.Devices
    230          aq_base(dmd.Devices.Server).getProperty('zSnmpCommunity')
    231
    232      The properties are stored as attributes which is convenient, but can be
    233      confusing.  Attribute access always uses acquistion.  Setting an
    234      attribute, will not add it to the list of properties, so subsquent calls
    235      to hasProperty or getProperty won't return it.
    236
    237      Property Transformers are stored at dmd.propertyTransformers and transform
    238      the property based on type during calls to the _setProperty,
    239      _updateProperty, and getProperty methods. Adding a property using
    240      _setProperty applies the appropriate transformer and adds its value as an
    241      attribute, but when you access it as an attribute the property transformer
    242      is again applied, but this time using its transformForGet method.
    243      """
    244      __pychecker__='no-override'
    245
    246      security = ClassSecurityInfo()
    247
    248      manage_propertiesForm=DTMLFile('dtml/properties', globals(),
    249                                     property_extensible_schema__=1)
    250
    251 -    def _setPropValue(self, id, value):
    252          """override from PerpertyManager to handle checks and ip creation"""
    253          self._wrapperCheck(value)
    254          propType = self.getPropertyType(id)
    255          if  propType == 'keyedselection':
    256              value = int(value)
    257          if not getattr(self,'_v_propdict',False):
    258              self._v_propdict = self.propdict()
    259          if self._v_propdict.has_key('setter'):
    260              settername = self._v_propdict['setter']
    261              setter = getattr(aq_base(self), settername, None)
    262              if not setter:
    263                  raise ValueError("setter %s for property %s doesn't exist"
    264                                      % (settername, id))
    265              if not callable(setter):
    266                  raise TypeError("setter %s for property %s not callable"
    267                                      % (settername, id))
    268              setter(value)
    269          else:
    270              setattr(self, id, value)
    271
    272
    273 -    def _setProperty(self, id, value, type='string', label=None,
    274                      visible=True, setter=None):
    275          """for selection and multiple selection properties
    276          the value argument indicates the select variable
    277          of the property
    278          """
    279          self._wrapperCheck(value)
    280          if not self.valid_property_id(id):
    281              raise BadRequest, 'Id %s is invalid or duplicate' % id
    282
    283          def setprops(**pschema):
    284              self._properties=self._properties+(pschema,)
    285              if setter: pschema['setter'] = setter
    286              if label: pschema['label'] = label
    287
    288          if type in ('selection', 'multiple selection'):
    289              if not hasattr(self, value):
    290                  raise BadRequest, 'No select variable %s' % value
    291              setprops(id=id,type=type, visible=visible,
    292                       select_variable=value)
    293              if type=='selection':
    294                  self._setPropValue(id, '')
    295              else:
    296                  self._setPropValue(id, [])
    297          else:
    298              setprops(id=id, type=type, visible=visible)
    299              self._setPropValue(id, value)
    300
    301 -    def _updateProperty(self, id, value):
    302          """ This method sets a property on a zope object. It overrides the
    303          method in PropertyManager. If Zope is upgraded you will need to check
    304          that this method has not changed! It is overridden so that we can catch
    305          the ValueError returned from the field2* converters in the class
    306          Converters.py
    307          """
    308          try:
    309              super(ZenPropertyManager, self)._updateProperty(id, value)
    310          except ValueError:
    311              msg = "Error Saving Property '%s'. New value '%s' is of invalid "
    312              msg += "type. It should be type '%s'."
    313              proptype = self.getPropertyType(id)
    314              args = (id, value, proptype)
    315              log.error(msg % args)
    316
    317
    318      _onlystars = re.compile("^\*+$").search
    319      security.declareProtected(ZEN_ZPROPERTIES_EDIT, 'manage_editProperties')
    320 -    def manage_editProperties(self, REQUEST):
    321          """
    322          Edit object properties via the web.
    323          The purpose of this method is to change all property values,
    324          even those not listed in REQUEST; otherwise checkboxes that
    325          get turned off will be ignored.  Use manage_changeProperties()
    326          instead for most situations.
    327          """
    328          for prop in self._propertyMap():
    329              name=prop['id']
    330              if 'w' in prop.get('mode', 'wd'):
    331                  value=REQUEST.get(name, '')
    332                  if self.zenPropIsPassword(name) and self._onlystars(value):
    333                      continue
    334                  self._updateProperty(name, value)
    335          if getattr(self, "index_object", False):
    336              self.index_object()
    337          if REQUEST:
    338              message="Saved changes."
    339              return self.manage_propertiesForm(self,REQUEST,
    340                                                manage_tabs_message=message)
    341
    342
    343 -    def getZenRootNode(self):
    344          """sub class must implement to use zenProperties."""
    345          raise NotImplementedError
    346
    347      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyIds')
    348 -    def zenPropertyIds(self, all=True, pfilt=iszprop):
    349          """
    350          Return list of device tree property names.
    351          If all use list from property root node.
    352          """
    353          if all:
    354              rootnode = self.getZenRootNode()
    355          else:
    356              if self.id == self.dmdRootName: return []
    357              rootnode = aq_base(self)
    358          props = []
    359          for prop in rootnode.propertyIds():
    360              if not pfilt(prop): continue
    361              props.append(prop)
    362          props.sort()
    363          return props
    364
    365      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyItems')
    366 -    def zenPropertyItems(self):
    367          """Return list of (id, value) tuples of zenProperties.
    368          """
    369          return map(lambda x: (x, getattr(self, x)), self.zenPropertyIds())
    370
    371      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyMap')
    372 -    def zenPropertyMap(self, pfilt=iszprop):
    373          """Return property mapping of device tree properties."""
    374          rootnode = self.getZenRootNode()
    375          pmap = []
    376          for pdict in rootnode.propertyMap():
    377              if pfilt(pdict['id']): pmap.append(pdict)
    378          pmap.sort(lambda x, y: cmp(x['id'], y['id']))
    379          return pmap
    380
    381      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyString')
    382 -    def zenPropertyString(self, id):
    383          """Return the value of a device tree property as a string"""
    384          def displayLines(lines):
    385              return '\n'.join([str(line) for line in lines])
    386          def displayPassword(password):
    387              return '*' * len(password)
    388          def displayOthers(other):
    389              return other
    390          displayFunctions = {'lines': displayLines,
    391                              'password': displayPassword}
    392          display = displayFunctions.get(self.getPropertyType(id),
    393                                         displayOthers)
    394          return display(self.getProperty(id, ''))
    395
    396      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropIsPassword')
    397 -    def zenPropIsPassword(self, id):
    398          """Is this field a password field.
    399          """
    400          return self.getPropertyType(id) == 'password'
    401
    402      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyPath')
    403 -    def zenPropertyPath(self, id):
    404          """Return the primaryId of where a device tree property is found."""
    405          ob = self._findParentWithProperty(id)
    406          if ob is None:
    407              path = None
    408          else:
    409              path = ob.getPrimaryId(self.getZenRootNode().getId())
    410          return path
    411
    412      security.declareProtected(ZEN_ZPROPERTIES_EDIT, 'setZenProperty')
    413 -    def setZenProperty(self, propname, propvalue, REQUEST=None):
    414          """
    415          Add or set the propvalue of the property propname on this node of
    416          the device Class tree.
    417          """
    418          ptype = self.getPropertyType(propname)
    419          if ptype == 'lines':
    420              dedupedList = []
    421              for x in propvalue:
    422                  if x not in dedupedList:
    423                      dedupedList.append(x)
    424              propvalue = dedupedList
    425          if getattr(aq_base(self), propname, zenmarker) != zenmarker:
    426              self._updateProperty(propname, propvalue)
    427          else:
    428              if ptype in ("selection", 'multiple selection'): ptype="string"
    429              if type_converters.has_key(ptype):
    430                  propvalue=type_converters[ptype](propvalue)
    431              if getattr(self, propname, None) != propvalue:
    432                  self._setProperty(propname, propvalue, type=ptype)
    433          if REQUEST: return self.callZenScreen(REQUEST)
    434
    435      security.declareProtected(ZEN_ZPROPERTIES_EDIT, 'saveZenProperties')
    436 -    def saveZenProperties(self, pfilt=iszprop, REQUEST=None):
    437          """Save all ZenProperties found in the REQUEST.form object.
    438          """
    439          for name, value in REQUEST.form.items():
    440              if pfilt(name):
    441                  if self.zenPropIsPassword(name) and self._onlystars(value):
    442                      continue
    443                  if name == 'zCollectorPlugins':
    444                      if tuple(getattr(self, name, ())) != tuple(value):
    445                          self.setZenProperty(name, value)
    446                  else:
    447                      self.setZenProperty(name, value)
    448
    449          if REQUEST:
    450              IMessageSender(self).sendToBrowser(
    451                  'Configuration Propeties Updated',
    452                  'Configuration properties have been updated.'
    453              )
    454
    455          return self.callZenScreen(REQUEST)
    456
    457      security.declareProtected(ZEN_ZPROPERTIES_EDIT, 'deleteZenProperty')
    458 -    def deleteZenProperty(self, propname=None, REQUEST=None):
    459          """
    460          Delete device tree properties from the this DeviceClass object.
    461          """
    462          if propname:
    463              try:
    464                  self._delProperty(propname)
    465              except AttributeError:
    466                  #occasional object corruption where the propName is in
    467                  #_properties but not set as an attribute. filter out the prop
    468                  #and create a new _properties tuple
    469                  newProps = [x for x in self._properties if x['id'] != propname]
    470                  self._properties=tuple(newProps)
    471              except exceptions.ValueError:
    472                  raise ZenPropertyDoesNotExist()
    473          if REQUEST: return self.callZenScreen(REQUEST)
    474
    475      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyOptions')
    476 -    def zenPropertyOptions(self, propname):
    477          "Provide a set of default options for a ZProperty"
    478          unused(propname)
    479          return []
    480
    481      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'isLocal')
    482 -    def isLocal(self, propname):
    483          """Check to see if a name is local to our current context.
    484          """
    485          v = getattr(aq_base(self), propname, zenmarker)
    486          return v != zenmarker
    487
    488      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'getOverriddenObjects')
    489 -    def getOverriddenObjects(self, propname, showDevices=False):
    490          """ Get the objects that override a property somewhere below in the tree
    491          """
    492          if showDevices:
    493              objects = []
    494              for inst in self.getSubInstances('devices'):
    495                  if inst.isLocal(propname) and inst not in objects:
    496                      objects.append(inst)
    497              for suborg in self.children():
    498                  if suborg.isLocal(propname):
    499                      objects.append(suborg)
    500                  for inst in suborg.getOverriddenObjects(propname, showDevices):
    501                      if inst not in objects:
    502                          objects.append(inst)
    503              return objects
    504
    505          return [ org for org in self.getSubOrganizers()
    506              if org.isLocal(propname) ]
    507
    508 -    def _findParentWithProperty(self, id):
    509          """
    510          Returns self or the first acquisition parent that has a property with
    511          the id.  Returns None if no parent had the id.
    512          """
    513          for ob in aq_chain(self):
    514              if isinstance(ob, ZenPropertyManager) and ob.hasProperty(id):
    515                  parentWithProperty = ob
    516                  break
    517          else:
    518              parentWithProperty = None
    519          return parentWithProperty
    520
    521 -    def hasProperty(self, id, useAcquisition=False):
    522          """
    523          Override method in PropertyManager to support acquisition.
    524          """
    525          if useAcquisition:
    526              hasProp = self._findParentWithProperty(id) is not None
    527          else:
    528              hasProp = PropertyManager.hasProperty(self, id)
    529          return hasProp
    530
    531 -    def getProperty(self, id, d=None):
    532          """
    533          Get property value and apply transformer.  Overrides method in Zope's
    534          PropertyManager class.  Acquire values from aquisiton parents if
    535          needed.
    536          """
    537          ob = self._findParentWithProperty(id)
    538          if ob is None:
    539              value = d
    540          else:
    541              value = PropertyManager.getProperty(ob, id, d)
    542          return value
    543
    544      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'getPropertyType')
    545 -    def getPropertyType(self, id):
    546          """
    547          Overrides methods from PropertyManager to support acquistion.
    548          """
    549          ob = self._findParentWithProperty(id)
    550          if ob is None:
    551              type = None
    552          else:
    553              type = PropertyManager.getPropertyType(ob, id)
    554          return type
    555
    556      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'getZ')
    557 -    def getZ(self, id):
    558          """
    559          Return the value of a zProperty on this object.  This method is used to
    560          lookup zProperties for a user with a role that doesn't have direct
    561          access to an attribute further up the acquisition path.  If the
    562          requested property is a password, then None is returned.
    563
    564          @param id: id of zProperty
    565          @type id: string
    566          @return: Value of zProperty
    567          @permission: ZEN_ZPROPERTIES_VIEW
    568
    569          >>> dmd.Devices.getZ('zSnmpPort')
    570          161
    571          >>> dmd.Devices.getZ('zWinPassword')
    572          >>>
    573          """
    574          if self.hasProperty(id, useAcquisition=True) \
    575                  and not self.zenPropIsPassword(id):
    576              returnValue = self.getProperty(id)
    577          else:
    578              returnValue = None
    579          return returnValue
    580
    581  InitializeClass(ZenPropertyManager)
    582
    583 -class IdentityTransformer(object):
    584      "A do-nothing transformer to use as the default"
    585
    586 -    def transformForGet(self, value):
    587          return value
    588
    589 -    def transformForSet(self, value):
    590          return value
    591
    592 -def monkeypatchDescriptors(zprops, transformerFactories):
    593      """
    594      monkeypatch ZenPropertyManager adding an instance of the descriptor class
    595      for each of the zProperties
    596      """
    597      for id, value, type in zprops:
    598          factory = transformerFactories.get(type, IdentityTransformer)
    599          descriptor = PropertyDescriptor(id, type, factory())
    600          setattr(ZenPropertyManager, id, descriptor)
    601
    602 -def setDescriptors(transformerFactories):
    603      """
    604      Set the property descriptors on the ZenPropertyManager class.  The
    605      transformerFactories parameter is a dictionary that maps a property type
    606      to a callable factory that produces instances with transformForGet and
    607      transformForSet methods.
    608      """
    609      # copy the core zProps
    610      zprops = Z_PROPERTIES[:]
    611
    612      # add zProps from zenpacks
    613      from Products.ZenUtils.PkgResources import pkg_resources
    614      for zpkg in pkg_resources.iter_entry_points('zenoss.zenpacks'):
    615          # fromlist is typically ZenPacks.zenoss
    616          fromlist = zpkg.module_name.split('.')[:-1]
    617          module = __import__(zpkg.module_name, globals(), locals(), fromlist)
    618          if hasattr(module, 'ZenPack'):
    619              zprops.extend(module.ZenPack.packZProperties)
    620
    621      monkeypatchDescriptors(zprops, transformerFactories)
    622
    623 -def updateDescriptors(type, transformer):
    624      """
    625      Update all descriptors with the specified type to use the specified
    626      transformer.
    627      """
    628      for var in vars(ZenPropertyManager):
    629          attr = getattr(ZenPropertyManager, var)
    630          if isinstance(attr, PropertyDescriptor) and attr.type == type:
    631              attr.transformer = transformer
    632