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