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