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