1
2
3
4
5
6
7
8
9
10
11 __doc__="""ZenModelRM
12
13 $Id: ZenModelRM.py,v 1.50 2004/05/10 20:49:09 edahl Exp $"""
14
15 __version__ = "$Revision: 1.50 $"[11:-2]
16
17 import os
18 import time
19
20 from DateTime import DateTime
21 from OFS.History import Historical
22 from Acquisition import aq_base
23 from AccessControl import ClassSecurityInfo
24 from ZPublisher.Converters import type_converters
25 from zope.interface import implements
26 from OFS.interfaces import IItem
27
28 from ZenModelBase import ZenModelBase, iscustprop
29 from ZenPacker import ZenPacker
30 from Products.ZenWidgets import messaging
31 from Products.ZenUtils.Utils import getSubObjects, zenPath
32 from Products.ZenRelations.ImportRM import ImportRM
33 from Products.ZenRelations.RelationshipManager import RelationshipManager
34 from Products.ZenModel.ZenossSecurity import *
35
36 -class ZenModelRM(ZenModelBase, RelationshipManager, Historical, ZenPacker):
37 """
38 Base class for all Persistent classes that have relationships.
39 Provides RelationshipManagement, Customized PropertyManagement,
40 Catalog Indexing, and Historical change tracking.
41 """
42 implements(IItem)
43 meta_type = 'ZenModelRM'
44
45 default_catalog = ''
46
47 isInTree = 0
48
49 security = ClassSecurityInfo()
50
51 - def __init__(self, id, title=None, buildRelations=True):
54
59
60 security.declareProtected('Manage DMD', 'rename')
61 - def rename(self, newId, REQUEST=None):
77
78
79 security.declareProtected('Manage DMD', 'zmanage_editProperties')
87
88
91 """Add a new property via the web.
92 Sets a new property with the given id, type, and value.
93 Id must start with a 'c' for custom attributes added via the
94 Custom Schema tab.
95 """
96 if type in type_converters and value:
97 value=type_converters[type](value)
98 id = id.strip()
99 if prefix and not id.startswith(prefix):
100 id = prefix + id
101 if not iscustprop(id):
102 if REQUEST:
103 messaging.IMessageSender(self).sendToBrowser(
104 'Error',
105 "Custom property name should be in this format: cProperty",
106 priority=messaging.WARNING
107 )
108 return self.callZenScreen(REQUEST)
109 elif self.hasProperty(id):
110 if REQUEST:
111 messaging.IMessageSender(self).sendToBrowser(
112 'Error',
113 "Custom property: %s already exists" % id,
114 priority=messaging.WARNING
115 )
116 return self.callZenScreen(REQUEST)
117 else:
118 self._setProperty(id, value, type, label, visible)
119 if REQUEST:
120 messaging.IMessageSender(self).sendToBrowser(
121 'Property Added',
122 "Custom property: %s added" % id
123 )
124 return self.callZenScreen(REQUEST)
125
127 """Export objects to specific locations.
128 """
129 if not context:
130 context = self
131 redirect = False
132 dest = 'filesystem'
133 if REQUEST:
134 dest = REQUEST.form.get('dest')
135 fileBase = '%s_%s.xml' % (context.getNodeName(), context.id)
136 if dest == 'filesystem':
137 filename = zenPath('export', fileBase)
138 msg = "Item has been exported to: %s at " % filename
139 elif dest == 'zenossdotnet':
140
141 filename = ''
142
143 url = 'https://%s:%[email protected]/'
144
145 import xmlrpclib
146 server = xmlrpclib.ProxyServer(url)
147 msg = "Item has been exported to: %s. Note that you will need to "
148 msg += "login at Zenoss.net and publish this template in order to "
149 msg += "share it with others. Exported at "
150 msg %= url
151
152 exportFile = open(filename, 'w+')
153
154 context.exportXml(exportFile)
155
156 exportFile.close()
157 if dest == 'zenossdotnet':
158
159 exportFile = open(filename)
160 dataToSend = exportFile.read()
161 exportFile.close()
162
163 server.postUserTemplate(dataToSend)
164 if REQUEST:
165 messaging.IMessageSender(self).sendToBrowser(
166 'Export Object', msg)
167 return self.callZenScreen(REQUEST, redirect)
168
169
171 """Import an XML file as the Zenoss objects and properties it
172 represents.
173 """
174
175
176
177
178 if not context:
179 context = self.getPhysicalRoot()
180
181 filenames = REQUEST.form.get('filenames')
182 urlnames = REQUEST.form.get('urlnames')
183 doDelete = REQUEST.form.get('dodelete')
184 xmlfiles = []
185 for collection in [filenames, urlnames]:
186 if collection:
187 if isinstance(collection, list):
188 xmlfiles.extend(collection)
189 else:
190 xmlfiles.append(collection)
191
192 im = ImportRM(noopts=True, app=self.getPhysicalRoot())
193 for xmlfile in xmlfiles:
194 im.loadObjectFromXML(context, xmlfile)
195 if doDelete and xmlfile in filenames:
196 os.unlink(xmlfile)
197 if REQUEST:
198 messaging.IMessageSender(self).sendToBrowser(
199 'Import Objects', 'Objects imported')
200 return self.callZenScreen(REQUEST)
201
202
204 """Import objects into Zenoss.
205 """
206 pass
207
219
220
234
235
236 security.declareProtected('View', 'getDmdKey')
238 """
239 Hook to get the name of an object. Usually its self.getId() but is
240 overridden by Organizer to be getOrganizerName.
241
242 >>> dmd.Manufacturers.createManufacturer('Cisco').getDmdKey()
243 'Cisco'
244 >>> dmd.Devices.Server.getDmdKey()
245 '/Server'
246 """
247 return self.getId()
248
249
250 security.declareProtected('View', 'primarySortKey')
252 """
253 Hook for the value used to sort this object. Defaults to self.getId().
254 """
255 return self.titleOrId()
256
257
258 security.declareProtected('View', 'viewName')
261
262
263
265 nodes = []
266 for item in self.objectValues():
267 if hasattr(aq_base(item), "isInTree") and item.isInTree:
268 nodes.append(item)
269 return nodes
270
271
272 - def getSubObjects(self, filter=None, decend=None, retobjs=None):
274
275
277 """return the creation time as a string"""
278 return self.createdTime.strftime('%Y/%m/%d %H:%M:%S')
279
280
282 """return the modification time as a string"""
283 return self.bobobase_modification_time().strftime('%Y/%m/%d %H:%M:%S')
284
285
287 """change the python class of a persistent object"""
288 id = self.id
289 nobj = newPythonClass(id)
290 nobj = nobj.__of__(container)
291 nobj.oldid = self.id
292 nobj.setPrimaryPath()
293
294 nrelations = self.ZenSchemaManager.getRelations(nobj).keys()
295 for sobj in self.objectValues():
296 RelationshipManager._delObject(self,sobj.getId())
297 if not hasattr(nobj, sobj.id) and sobj.id in nrelations:
298
299 setObject = RelationshipManager._setObject
300 setObject(nobj, sobj.id, sobj)
301 nobj.buildRelations()
302
303 noprop = getattr(nobj, 'zNoPropertiesCopy', [])
304 for name in nobj.getPropertyNames():
305 if (getattr(self, name, None) and name not in noprop and
306 hasattr(nobj, "_updateProperty")):
307 val = getattr(self, name)
308 nobj._updateProperty(name, val)
309 return aq_base(nobj)
310
311
315
316
318 """
319 Return true if user has Manager role and self has a deviceList.
320 """
321 if not getattr(aq_base(self), "deviceMoveTargets", False):
322 return False
323
324 if self.isManager() or \
325 self.checkRemotePerm(ZEN_CHANGE_DEVICE_PRODSTATE, self):
326 return True
327
328 return False
329
330
332 """
333 Method needed for CatalogAwarnessInterface. Implemented here so that
334 Subclasses (who would have the same implementation) don't need to.
335 Other methods (except reindex_all) are implemented on the concreate
336 class.
337 """
338 users=[]
339 for user, roles in self.get_local_roles():
340 if 'Owner' in roles:
341 users.append(user)
342 return ', '.join(users)
343
344
346 """A common method to allow Findables to index themselves."""
347 cat = getattr(self, self.default_catalog, None)
348 if cat != None:
349 cat.catalog_object(self, self.getPrimaryId(), idxs=idxs)
350
351
352
354 """A common method to allow Findables to unindex themselves."""
355 cat = getattr(self, self.default_catalog, None)
356 if cat != None:
357 cat.uncatalog_object(self.getPrimaryId())
358
359
361 """
362 Called for in the CataLogAwarenessInterface not sure this is needed.
363 """
364 if obj is None: obj=self
365 if hasattr(aq_base(obj), 'index_object'):
366 obj.index_object()
367 if hasattr(aq_base(obj), 'objectValues'):
368 sub=obj.objectValues()
369 for item in sub:
370 self.reindex_all(item)
371 return 'done!'
372
374 """
375 Find child using the ids found in path. Path separator is '/'. This
376 is similar to using attributes, but doesn't use acquisition. For
377 example, if 'Devices/Server/Linux' exists, but
378 'Devices/Server/SSH/Linux' does not, then the two methods will behave
379 differently. dmd.Devices.Server.SSH.Linux will return
380 'Devices/Server/Linux', whereas this method will throw an exception.
381 """
382 child = self
383 for id in path.split('/'):
384 child = child._getOb(id)
385 return child
386