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

# Source Code for Module Products.ZenRelations.ZenPropertyManager

      1  ##############################################################################
      2  #
      3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved.
      4  #
      5  # This content is made available according to terms specified in
      6  # License.zenoss under the directory where your Zenoss product is installed.
      7  #
      8  ##############################################################################
      9
     10
     11  import re
     12  import logging
     13
     14  from OFS.PropertyManager import PropertyManager
     15  from zExceptions import BadRequest
     16  from Globals import DTMLFile
     17  from Globals import InitializeClass
     18  from Acquisition import aq_base, aq_chain
     19  from ZPublisher.Converters import type_converters
     20  from Products.ZenMessaging.audit import audit
     21  from Products.ZenModel.ZenossSecurity import *
     22  from AccessControl import ClassSecurityInfo
     23  from Exceptions import zenmarker
     24  from Products.ZenWidgets.interfaces import IMessageSender
     25  from Products.ZenRelations.zPropertyCategory import getzPropertyCategory
     26  from Products.ZenUtils.Utils import unused, getDisplayType
     27
     28  iszprop = re.compile("z[A-Z]").match
     29
     30  log = logging.getLogger('zen.PropertyManager')
     31
     32  # Z_PROPERTIES is a list of (id, type, value) pairs that define all the
     33  # zProperties.  The values are set on dmd.Devices in the
     34  # buildDeviceTreeProperties of DeviceClass
     35  Z_PROPERTIES = [
     36
     37      # zPythonClass maps device class to python classs (separate from device
     38      # class name)
     39      ('zPythonClass', '', 'string'),
     40
     41      # zProdStateThreshold is the production state threshold at which to start
     42      # monitoring boxes
     43      ('zProdStateThreshold', 300, 'int'),
     44
     45      # zIfDescription determines whether or not the ifdescripion field is
     46      # displayed
     47      ('zIfDescription', False, 'boolean'),
     48
     49      # Snmp collection properties
     50      ('zSnmpCommunities', ['public', 'private'], 'lines'),
     51      ('zSnmpCommunity', 'public', 'string'),
     52      ('zSnmpPort', 161, 'int'),
     53      ('zSnmpVer', 'v2c', 'string'),
     54      ('zSnmpTries', 6, 'int'),
     55      ('zSnmpTimeout', 1, 'float'),
     56      ('zSnmpEngineId', '', 'string'),
     57      ('zSnmpSecurityName', '', 'string'),
     58      ('zSnmpAuthPassword', '', 'password'),
     59      ('zSnmpPrivPassword', '', 'password'),
     60      ('zSnmpAuthType', '', 'string'),
     61      ('zSnmpPrivType', '', 'string'),
     62      ('zSnmpCollectionInterval', 300, 'int'),
     63      ('zRouteMapCollectOnlyLocal', False, 'boolean'),
     64      ('zRouteMapCollectOnlyIndirect', False, 'boolean'),
     65      ('zRouteMapMaxRoutes', 500, 'int'),
     66      ('zInterfaceMapIgnoreTypes', '', 'string'),
     67      ('zInterfaceMapIgnoreNames', '', 'string'),
     68      ('zInterfaceMapIgnoreDescriptions', '', '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      ('zStatusConnectTimeout', 15.0, 'float'),
     83
     84      # DataCollector properties
     85      ('zCollectorPlugins', [], 'lines'),
     86      ('zCollectorClientTimeout', 180, 'int'),
     87      ('zCollectorDecoding', 'latin-1', 'string'),
     88      ('zCommandUsername', '', 'string'),
     89      ('zCommandPassword', '', 'password'),
     90      ('zCommandProtocol', 'ssh', 'string'),
     91      ('zCommandPort', 22, 'int'),
     92      ('zCommandLoginTries', 1, 'int'),
     93      ('zCommandLoginTimeout', 10.0, 'float'),
     94      ('zCommandCommandTimeout', 10.0, 'float'),
     95      ('zCommandSearchPath', [], 'lines'),
     96      ('zCommandExistanceTest', 'test -f %s', 'string'),
     97      ('zCommandPath', '/usr/local/zenoss/libexec', 'string'),
     98      ('zTelnetLoginRegex', 'ogin:.$', 'string'),
     99      ('zTelnetPasswordRegex', 'assword:', 'string'),
    100      ('zTelnetSuccessRegexList', ['\\$.$', '\\#.$'], 'lines'),
    101      ('zTelnetEnable', False, 'boolean'),
    102      ('zTelnetEnableRegex', 'assword:', 'string'),
    103      ('zTelnetTermLength', True, 'boolean'),
    104      ('zTelnetPromptTimeout', 10.0, 'float'),
    105      ('zKeyPath', '~/.ssh/id_dsa', 'string'),
    106      ('zMaxOIDPerRequest', 40, 'int'),
    107
    108      # Extra stuff for users
    109      ('zLinks', '', 'string'),
    110
    111      # zIcon is the icon path
    112      ('zIcon', '/zport/dmd/img/icons/noicon.png', 'string'),
    113
    114      # used in ApplyDataMap
    115      ('zCollectorLogChanges', True, 'boolean'),
    116
    117      # enable password for Cisco routers
    118      ('zEnablePassword', '', 'password'),
    119
    120      # used in zenoss.nmap.IpServiceMap
    121      ('zNmapPortscanOptions', '-p 1-1024 -sT -oG -', 'string'),
    122
    123      # how many SSH sessions to open up to one device (some SSH servers have a limit)
    124      ('zSshConcurrentSessions', 10, 'int'),
    125
    126      ]
    127
    128 -class PropertyDescriptor(object):
    129      """
    130      Transforms the property value based on its type.
    131
    132      Follows the Descriptor protocol defined at
    133      http://docs.python.org/reference/datamodel.html#descriptors
    134      """
    135
    136 -    def __init__(self, id, type, transformer):
    137          self.id = id
    138          self.type = type
    139          self.transformer = transformer
    140
    141 -    def __get__(self, instance, owner):
    142          """
    143          Returns self for class attribute access.  Returns the transformed
    144          value for instance attribute access.
    145          """
    146          try:
    147              if instance is None:
    148                  retval = self
    149              else:
    150                  self._migrate(instance)
    151                  value = instance._propertyValues[self.id]
    152                  retval = self._transform(instance, value, 'transformForGet')
    153              return retval
    154          except:
    155              raise AttributeError
    156
    157 -    def __set__(self, instance, value):
    158          """
    159          Transforms the value and sets it.
    160          """
    161          self._migrate(instance)
    162          self._set(instance, value)
    163
    164 -    def __delete__(self, instance):
    165          """
    166          Delete the property.
    167          """
    168          self._migrate(instance)
    169          del instance._propertyValues[self.id]
    170
    171 -    def _migrate(self, instance):
    172          """
    173          If the id is in __dict__ then move the value to the _propertyValues
    174          dictionary. Check to make sure that the type of this descriptor class
    175          and the type in the Zope OFS PropertyManager metadata are the same.
    176          """
    177          if not hasattr(instance, '_propertyValues'):
    178              instance._propertyValues = {}
    179          if self.id in vars(instance):
    180              self._set(instance, vars(instance)[self.id])
    181              del instance.__dict__[self.id]
    182              instance._p_changed = True
    183          for dct in instance._properties:
    184              if dct['id'] == self.id:
    185                  if dct['type'] != self.type:
    186                      dct['type'] = self.type
    187                      instance._p_changed = True
    188                  break
    189
    190 -    def _set(self, instance, value):
    191          """
    192          Transform and set the value in the _propertyValues dictionary.
    193          """
    194          valueToSet = self._transform(instance, value, 'transformForSet')
    195          instance._propertyValues[self.id] = valueToSet
    196
    197 -    def _transform(self, instance, value, method):
    198          """
    199          Lookup the transformer for the type and transform the value. The
    200          method parameter can be 'transformForGet' or 'transformForSet' and
    201          determines the transformer method that is called.
    202          """
    203          return getattr(self.transformer, method)(value)
    204
    205 -class ZenPropertyDoesNotExist(ValueError):
    206      pass
    207
    208 -class ZenPropertyManager(object, PropertyManager):
    209      """
    210
    211      ZenPropertyManager adds keyedselection type to PropertyManager.
    212      A keyedselection displayes a different name in the popup then
    213      the actual value the popup will have.
    214
    215      It also has management for zenProperties which are properties that can be
    216      inherited long the acquision chain.  All properties are for a branch are
    217      defined on a "root node" specified by the function which must be returned
    218      by the function getZenRootNode that should be over ridden in a sub class.
    219      Prperties can then be added further "down" the aq_chain by calling
    220      setZenProperty on any contained node.
    221
    222      ZenProperties all have the same prefix which is defined by iszprop
    223      this can be overridden in a subclass.
    224
    225      ZenPropertyManager overrides getProperty and getPropertyType from
    226      PropertyManager to support acquisition. If you want to query an object
    227      about a property, but do not want it to search the acquistion chain then
    228      use the super classes method or aq_base.  Example:
    229
    230          # acquires property from dmd.Devices
    231          dmd.Devices.Server.getProperty('zCollectorPlugins')
    232
    233          # does not acquire property from dmd.Devices
    234          PropertyManager.getProperty(dmd.Devices.Server, 'zCollectorPlugins')
    235
    236          # also does not acquire property from dmd.Devices
    237          aq_base(dmd.Devices.Server).getProperty('zSnmpCommunity')
    238
    239      The properties are stored as attributes which is convenient, but can be
    240      confusing.  Attribute access always uses acquistion.  Setting an
    241      attribute, will not add it to the list of properties, so subsquent calls
    242      to hasProperty or getProperty won't return it.
    243
    244      Property Transformers are stored at dmd.propertyTransformers and transform
    245      the property based on type during calls to the _setProperty,
    246      _updateProperty, and getProperty methods. Adding a property using
    247      _setProperty applies the appropriate transformer and adds its value as an
    248      attribute, but when you access it as an attribute the property transformer
    249      is again applied, but this time using its transformForGet method.
    250      """
    251      __pychecker__='no-override'
    252
    253      security = ClassSecurityInfo()
    254
    255      manage_propertiesForm=DTMLFile('dtml/properties', globals(),
    256                                     property_extensible_schema__=1)
    257
    258 -    def _setPropValue(self, id, value):
    259          """override from PerpertyManager to handle checks and ip creation"""
    260          self._wrapperCheck(value)
    261          propType = self.getPropertyType(id)
    262          if  propType == 'keyedselection':
    263              value = int(value)
    264          if not getattr(self,'_v_propdict',False):
    265              self._v_propdict = self.propdict()
    266          if 'setter' in self._v_propdict:
    267              settername = self._v_propdict['setter']
    268              setter = getattr(aq_base(self), settername, None)
    269              if not setter:
    270                  raise ValueError("setter %s for property %s doesn't exist"
    271                                      % (settername, id))
    272              if not callable(setter):
    273                  raise TypeError("setter %s for property %s not callable"
    274                                      % (settername, id))
    275              setter(value)
    276          else:
    277              setattr(self, id, value)
    278
    279
    280 -    def _setProperty(self, id, value, type='string', label=None,
    281                      visible=True, setter=None):
    282          """for selection and multiple selection properties
    283          the value argument indicates the select variable
    284          of the property
    285          """
    286          self._wrapperCheck(value)
    287          if not self.valid_property_id(id):
    288              raise BadRequest, 'Id %s is invalid or duplicate' % id
    289
    290          def setprops(**pschema):
    291              self._properties=self._properties+(pschema,)
    292              if setter: pschema['setter'] = setter
    293              if label: pschema['label'] = label
    294
    295          if type in ('selection', 'multiple selection'):
    296              if not hasattr(self, value):
    297                  raise BadRequest, 'No select variable %s' % value
    298              setprops(id=id,type=type, visible=visible,
    299                       select_variable=value)
    300              if type=='selection':
    301                  self._setPropValue(id, '')
    302              else:
    303                  self._setPropValue(id, [])
    304          else:
    305              setprops(id=id, type=type, visible=visible)
    306              self._setPropValue(id, value)
    307
    308 -    def _updateProperty(self, id, value):
    309          """ This method sets a property on a zope object. It overrides the
    310          method in PropertyManager. If Zope is upgraded you will need to check
    311          that this method has not changed! It is overridden so that we can catch
    312          the ValueError returned from the field2* converters in the class
    313          Converters.py
    314          """
    315          try:
    316              super(ZenPropertyManager, self)._updateProperty(id, value)
    317          except ValueError:
    318              msg = "Error Saving Property '%s'. New value '%s' is of invalid "
    319              msg += "type. It should be type '%s'."
    320              proptype = self.getPropertyType(id)
    321              args = (id, value, proptype)
    322              log.error(msg % args)
    323
    324
    325      _onlystars = re.compile("^\*+$").search
    326      security.declareProtected(ZEN_ZPROPERTIES_EDIT, 'manage_editProperties')
    327 -    def manage_editProperties(self, REQUEST):
    328          """
    329          Edit object properties via the web.
    330          The purpose of this method is to change all property values,
    331          even those not listed in REQUEST; otherwise checkboxes that
    332          get turned off will be ignored.  Use manage_changeProperties()
    333          instead for most situations.
    334          """
    335          for prop in self._propertyMap():
    336              name=prop['id']
    337              if 'w' in prop.get('mode', 'wd'):
    338                  value=REQUEST.get(name, '')
    339                  if self.zenPropIsPassword(name) and self._onlystars(value):
    340                      continue
    341                  self._updateProperty(name, value)
    342          if getattr(self, "index_object", False):
    343              self.index_object()
    344          if REQUEST:
    345              message="Saved changes."
    346              return self.manage_propertiesForm(self,REQUEST,
    347                                                manage_tabs_message=message)
    348
    349
    350 -    def getZenRootNode(self):
    351          """sub class must implement to use zenProperties."""
    352          raise NotImplementedError
    353
    354      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyIds')
    355 -    def zenPropertyIds(self, all=True, pfilt=iszprop):
    356          """
    357          Return list of device tree property names.
    358          If all use list from property root node.
    359          """
    360          if all:
    361              rootnode = self.getZenRootNode()
    362          else:
    363              if self.id == self.dmdRootName: return []
    364              rootnode = aq_base(self)
    365          return sorted(prop for prop in rootnode.propertyIds() if pfilt(prop))
    366
    367      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyItems')
    368 -    def zenPropertyItems(self):
    369          """Return list of (id, value) tuples of zenProperties.
    370          """
    371          return map(lambda x: (x, getattr(self, x)), self.zenPropertyIds())
    372
    373      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyMap')
    374 -    def zenPropertyMap(self, pfilt=iszprop):
    375          """Return property mapping of device tree properties."""
    376          rootnode = self.getZenRootNode()
    377          return sorted((pdict for pdict in rootnode.propertyMap()
    378                           if pfilt(pdict['id'])),
    379                          key=lambda x : x['id'])
    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 ptype in type_converters:
    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          maskFields=[]
    440          for name, value in REQUEST.form.items():
    441              if pfilt(name):
    442                  if self.zenPropIsPassword(name):
    443                      maskFields.append(name)
    444                      if self._onlystars(value):
    445                          continue
    446                  if name == 'zCollectorPlugins':
    447                      if tuple(getattr(self, name, ())) != tuple(value):
    448                          self.setZenProperty(name, value)
    449                  else:
    450                      self.setZenProperty(name, value)
    451
    452          if REQUEST:
    453              audit(('UI', getDisplayType(self), 'EditProperties'), self, data_=REQUEST.form,
    454                      skipFields_=['savezenproperties','zenscreenname'], maskFields_=maskFields)
    455              IMessageSender(self).sendToBrowser(
    456                  'Configuration Propeties Updated',
    457                  'Configuration properties have been updated.'
    458              )
    459
    460          return self.callZenScreen(REQUEST)
    461
    462      security.declareProtected(ZEN_ZPROPERTIES_EDIT, 'deleteZenProperty')
    463 -    def deleteZenProperty(self, propname=None, REQUEST=None):
    464          """
    465          Delete device tree properties from the this DeviceClass object.
    466          """
    467          if propname:
    468              try:
    469                  self._delProperty(propname)
    470              except AttributeError:
    471                  #occasional object corruption where the propName is in
    472                  #_properties but not set as an attribute. filter out the prop
    473                  #and create a new _properties tuple
    474                  newProps = [x for x in self._properties if x['id'] != propname]
    475                  self._properties=tuple(newProps)
    476              except ValueError:
    477                  raise ZenPropertyDoesNotExist()
    478          if REQUEST:
    479              if propname:
    480                  audit(('UI',getDisplayType(self),'DeleteZProperty'), self, property=propname)
    481              return self.callZenScreen(REQUEST)
    482
    483      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyOptions')
    484 -    def zenPropertyOptions(self, propname):
    485          "Provide a set of default options for a ZProperty"
    486          unused(propname)
    487          return []
    488
    489      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'isLocal')
    490 -    def isLocal(self, propname):
    491          """Check to see if a name is local to our current context.
    492          """
    493          v = getattr(aq_base(self), propname, zenmarker)
    494          return v != zenmarker
    495
    496      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'getOverriddenObjects')
    497 -    def getOverriddenObjects(self, propname, showDevices=False):
    498          """ Get the objects that override a property somewhere below in the tree
    499          """
    500          if showDevices:
    501              objects = []
    502              for inst in self.getSubInstances('devices'):
    503                  if inst.isLocal(propname) and inst not in objects:
    504                      objects.append(inst)
    505              for suborg in self.children():
    506                  if suborg.isLocal(propname):
    507                      objects.append(suborg)
    508                  for inst in suborg.getOverriddenObjects(propname, showDevices):
    509                      if inst not in objects:
    510                          objects.append(inst)
    511              return objects
    512
    513          return [ org for org in self.getSubOrganizers()
    514              if org.isLocal(propname) ]
    515
    516 -    def _findParentWithProperty(self, id):
    517          """
    518          Returns self or the first acquisition parent that has a property with
    519          the id.  Returns None if no parent had the id.
    520          """
    521          for ob in aq_chain(self):
    522              if isinstance(ob, ZenPropertyManager) and ob.hasProperty(id):
    523                  parentWithProperty = ob
    524                  break
    525          else:
    526              parentWithProperty = None
    527          return parentWithProperty
    528
    529 -    def hasProperty(self, id, useAcquisition=False):
    530          """
    531          Override method in PropertyManager to support acquisition.
    532          """
    533          if useAcquisition:
    534              hasProp = self._findParentWithProperty(id) is not None
    535          else:
    536              hasProp = PropertyManager.hasProperty(self, id)
    537          return hasProp
    538
    539 -    def getProperty(self, id, d=None):
    540          """
    541          Get property value and apply transformer.  Overrides method in Zope's
    542          PropertyManager class.  Acquire values from aquisiton parents if
    543          needed.
    544          """
    545          ob = self._findParentWithProperty(id)
    546          if ob is None:
    547              value = d
    548          else:
    549              value = PropertyManager.getProperty(ob, id, d)
    550          return value
    551
    552      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'getPropertyType')
    553 -    def getPropertyType(self, id):
    554          """
    555          Overrides methods from PropertyManager to support acquistion.
    556          """
    557          ob = self._findParentWithProperty(id)
    558          if ob is None:
    559              type = None
    560          else:
    561              type = PropertyManager.getPropertyType(ob, id)
    562          return type
    563
    564      security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'getZ')
    565 -    def getZ(self, id, default=None):
    566          """
    567          Return the value of a zProperty on this object.  This method is used to
    568          lookup zProperties for a user with a role that doesn't have direct
    569          access to an attribute further up the acquisition path.  If the
    570          requested property is a password, then None is returned.
    571
    572          @param id: id of zProperty
    573          @type id: string
    574          @return: Value of zProperty
    575          @permission: ZEN_ZPROPERTIES_VIEW
    576
    577          >>> dmd.Devices.getZ('zSnmpPort')
    578          161
    579          >>> dmd.Devices.getZ('zSnmpAuthPassword')
    580          >>>
    581          """
    582          if self.hasProperty(id, useAcquisition=True) \
    583                  and not self.zenPropIsPassword(id):
    584              returnValue = self.getProperty(id)
    585          else:
    586              returnValue = default
    587          return returnValue
    588
    589 -    def exportZProperties(self, exclusionList=()):
    590          """
    591          @param exclusionList: list of zproperties we do not want to export
    592          @type exclusionList: collection
    593          For this manager will return the following about each zProperty
    594          Will return the following about each Zen Property
    595          - id - identifier
    596          - islocal - if this object has a local definition
    597          - value - value for this object
    598          - valueAsString - string representation of the property
    599          - type - int string lines etc
    600          - path - where it is defined
    601          - options - acceptable values of this zProperty
    602          """
    603          props = []
    604          for zId in self.zenPropertyIds():
    605              if zId in exclusionList:
    606                  continue
    607              prop = dict(
    608                      id=zId,
    609                      islocal=self.hasProperty(zId),
    610                      type=self.getPropertyType(zId),
    611                      path=self.zenPropertyPath(zId),
    612                      options=self.zenPropertyOptions(zId),
    613                      category=getzPropertyCategory(zId),
    614                      value=None,
    615                      valueAsString=self.zenPropertyString(zId)
    616                      )
    617              if not self.zenPropIsPassword(zId):
    618                  prop['value'] = self.getZ(zId)
    619              else:
    620                  prop['value'] = self.zenPropertyString(zId)
    621              props.append(prop)
    622          return props
    623
    624  InitializeClass(ZenPropertyManager)
    625
    626 -class IdentityTransformer(object):
    627      "A do-nothing transformer to use as the default"
    628
    629 -    def transformForGet(self, value):
    630          return value
    631
    632 -    def transformForSet(self, value):
    633          return value
    634
    635 -def monkeypatchDescriptors(zprops, transformerFactories):
    636      """
    637      monkeypatch ZenPropertyManager adding an instance of the descriptor class
    638      for each of the zProperties
    639      """
    640      for id, type in zprops:
    641          factory = transformerFactories.get(type, IdentityTransformer)
    642          descriptor = PropertyDescriptor(id, type, factory())
    643          setattr(ZenPropertyManager, id, descriptor)
    644
    645 -def setDescriptors(dmd):
    646      """
    647      Set the property descriptors on the ZenPropertyManager class.  The
    648      transformerFactories parameter is a dictionary that maps a property type
    649      to a callable factory that produces instances with transformForGet and
    650      transformForSet methods.
    651      """
    652      zprops = set()
    653
    654      # copy the core zProps
    655      for prop_id, propt_default_value, prop_type in Z_PROPERTIES:
    656          zprops.add((prop_id, prop_type))
    657
    658      # add zProps from zenpacks
    659      from Products.ZenUtils.PkgResources import pkg_resources
    660      for zpkg in pkg_resources.iter_entry_points('zenoss.zenpacks'):
    661          # fromlist is typically ZenPacks.zenoss
    662          fromlist = zpkg.module_name.split('.')[:-1]
    663          module = __import__(zpkg.module_name, globals(), locals(), fromlist)
    664          if hasattr(module, 'ZenPack'):
    665              for prop_id, propt_default_value, prop_type in module.ZenPack.packZProperties:
    666                  zprops.add((prop_id, prop_type))
    667
    668      # add zProps from dmd.Devices to catch any that are undefined elsewhere
    669      for prop_id in dmd.Devices.zenPropertyIds():
    670          prop_type = dmd.Devices.getPropertyType(prop_id)
    671          if (prop_id, prop_type) not in zprops:
    672              log.debug('Property {prop_id} is deprecated. It should be removed from the system.'.format(prop_id=prop_id))
    673              zprops.add((prop_id, prop_type))
    674
    675      monkeypatchDescriptors(zprops, dmd.propertyTransformers)
    676
    677 -def updateDescriptors(type, transformer):
    678      """
    679      Update all descriptors with the specified type to use the specified
    680      transformer.
    681      """
    682      for var in vars(ZenPropertyManager):
    683          attr = getattr(ZenPropertyManager, var)
    684          if isinstance(attr, PropertyDescriptor) and attr.type == type:
    685              attr.transformer = transformer
    686