1
2
3
4
5
6
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
33
34
35 Z_PROPERTIES = [
36
37
38
39 ('zPythonClass', '', 'string'),
40
41
42
43 ('zProdStateThreshold', 300, 'int'),
44
45
46
47 ('zIfDescription', False, 'boolean'),
48
49
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
80 ('zSnmpMonitorIgnore', False, 'boolean'),
81 ('zPingMonitorIgnore', False, 'boolean'),
82 ('zStatusConnectTimeout', 15.0, 'float'),
83
84
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
109 ('zLinks', '', 'string'),
110
111
112 ('zIcon', '/zport/dmd/img/icons/noicon.png', 'string'),
113
114
115 ('zCollectorLogChanges', True, 'boolean'),
116
117
118 ('zEnablePassword', '', 'password'),
119
120
121 ('zNmapPortscanOptions', '-p 1-1024 -sT -oG -', 'string'),
122
123
124 ('zSshConcurrentSessions', 10, 'int'),
125
126 ]
127
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):
163
170
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
204
207
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
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
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')
348
349
351 """sub class must implement to use zenProperties."""
352 raise NotImplementedError
353
354 security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyIds')
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')
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')
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')
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')
398 """Is this field a password field.
399 """
400 return self.getPropertyType(id) == 'password'
401
402 security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'zenPropertyPath')
411
412 security.declareProtected(ZEN_ZPROPERTIES_EDIT, 'setZenProperty')
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')
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')
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
472
473
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')
485 "Provide a set of default options for a ZProperty"
486 unused(propname)
487 return []
488
489 security.declareProtected(ZEN_ZPROPERTIES_VIEW, 'isLocal')
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')
515
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
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
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')
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
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
634
644
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
655 for prop_id, propt_default_value, prop_type in Z_PROPERTIES:
656 zprops.add((prop_id, prop_type))
657
658
659 from Products.ZenUtils.PkgResources import pkg_resources
660 for zpkg in pkg_resources.iter_entry_points('zenoss.zenpacks'):
661
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
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
686