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
59 security.declareProtected('Manage DMD', 'rename')
60 - def rename(self, newId, REQUEST=None):
76
77
78 security.declareProtected('Manage DMD', 'zmanage_editProperties')
86
87
90 """Add a new property via the web.
91 Sets a new property with the given id, type, and value.
92 Id must start with a 'c' for custom attributes added via the
93 Custom Schema tab.
94 """
95 if type_converters.has_key(type) and value:
96 value=type_converters[type](value)
97 id = id.strip()
98 if prefix and not id.startswith(prefix):
99 id = prefix + id
100 if not iscustprop(id):
101 if REQUEST:
102 messaging.IMessageSender(self).sendToBrowser(
103 'Error',
104 "Custom property name should be in this format: cProperty",
105 priority=messaging.WARNING
106 )
107 return self.callZenScreen(REQUEST)
108 elif self.hasProperty(id):
109 if REQUEST:
110 messaging.IMessageSender(self).sendToBrowser(
111 'Error',
112 "Custom property: %s already exists" % id,
113 priority=messaging.WARNING
114 )
115 return self.callZenScreen(REQUEST)
116 else:
117 self._setProperty(id, value, type, label, visible)
118 if REQUEST:
119 messaging.IMessageSender(self).sendToBrowser(
120 'Property Added',
121 "Custom property: %s added" % id
122 )
123 return self.callZenScreen(REQUEST)
124
126 """Export objects to specific locations.
127 """
128 if not context:
129 context = self
130 redirect = False
131 dest = 'filesystem'
132 if REQUEST:
133 dest = REQUEST.form.get('dest')
134 fileBase = '%s_%s.xml' % (context.getNodeName(), context.id)
135 if dest == 'filesystem':
136 filename = zenPath('export', fileBase)
137 msg = "Item has been exported to: %s at " % filename
138 elif dest == 'zenossdotnet':
139
140 filename = ''
141
142 url = 'https://%s:%[email protected]/'
143
144 import xmlrpclib
145 server = xmlrpclib.ProxyServer(url)
146 msg = "Item has been exported to: %s. Note that you will need to "
147 msg += "login at Zenoss.net and publish this template in order to "
148 msg += "share it with others. Exported at "
149 msg %= url
150
151 exportFile = open(filename, 'w+')
152
153 context.exportXml(exportFile)
154
155 exportFile.close()
156 if dest == 'zenossdotnet':
157
158 exportFile = open(filename)
159 dataToSend = exportFile.read()
160 exportFile.close()
161
162 server.postUserTemplate(dataToSend)
163 if REQUEST:
164 messaging.IMessageSender(self).sendToBrowser(
165 'Export Object', msg)
166 return self.callZenScreen(REQUEST, redirect)
167
168
170 """Import an XML file as the Zenoss objects and properties it
171 represents.
172 """
173
174
175
176
177 if not context:
178 context = self.getPhysicalRoot()
179
180 filenames = REQUEST.form.get('filenames')
181 urlnames = REQUEST.form.get('urlnames')
182 doDelete = REQUEST.form.get('dodelete')
183 xmlfiles = []
184 for collection in [filenames, urlnames]:
185 if collection:
186 if isinstance(collection, list):
187 xmlfiles.extend(collection)
188 else:
189 xmlfiles.append(collection)
190
191 im = ImportRM(noopts=True, app=self.getPhysicalRoot())
192 for xmlfile in xmlfiles:
193 im.loadObjectFromXML(context, xmlfile)
194 if doDelete and xmlfile in filenames:
195 os.unlink(xmlfile)
196 if REQUEST:
197 messaging.IMessageSender(self).sendToBrowser(
198 'Import Objects', 'Objects imported')
199 return self.callZenScreen(REQUEST)
200
201
203 """Import objects into Zenoss.
204 """
205 pass
206
218
219
233
234
235 security.declareProtected('View', 'getDmdKey')
237 """
238 Hook to get the name of an object. Usually its self.getId() but is
239 overridden by Organizer to be getOrganizerName.
240
241 >>> dmd.Manufacturers.createManufacturer('Cisco').getDmdKey()
242 'Cisco'
243 >>> dmd.Devices.Server.getDmdKey()
244 '/Server'
245 """
246 return self.getId()
247
248
249 security.declareProtected('View', 'primarySortKey')
251 """
252 Hook for the value used to sort this object. Defaults to self.getId().
253 IpNetwork for instance overrides to allow it to sort by the IP numeric
254 value not its string value.
255
256 >>> n = dmd.Networks.createNet('1.2.3.0', 24)
257 >>> n.primarySortKey()
258 16909056L
259 """
260 return self.titleOrId()
261
262
263 security.declareProtected('View', 'viewName')
266
267
268
270 nodes = []
271 for item in self.objectValues():
272 if hasattr(aq_base(item), "isInTree") and item.isInTree:
273 nodes.append(item)
274 return nodes
275
276
277 - def getSubObjects(self, filter=None, decend=None, retobjs=None):
279
280
282 """return the creation time as a string"""
283 return self.createdTime.strftime('%Y/%m/%d %H:%M:%S')
284
285
287 """return the modification time as a string"""
288 return self.bobobase_modification_time().strftime('%Y/%m/%d %H:%M:%S')
289
290
292 """change the python class of a persistent object"""
293 id = self.id
294 nobj = newPythonClass(id)
295 nobj = nobj.__of__(container)
296 nobj.oldid = self.id
297 nobj.setPrimaryPath()
298
299 nrelations = self.ZenSchemaManager.getRelations(nobj).keys()
300 for sobj in self.objectValues():
301 RelationshipManager._delObject(self,sobj.getId())
302 if not hasattr(nobj, sobj.id) and sobj.id in nrelations:
303
304 setObject = RelationshipManager._setObject
305 setObject(nobj, sobj.id, sobj)
306 nobj.buildRelations()
307
308 noprop = getattr(nobj, 'zNoPropertiesCopy', [])
309 for name in nobj.getPropertyNames():
310 if (getattr(self, name, None) and name not in noprop and
311 hasattr(nobj, "_updateProperty")):
312 val = getattr(self, name)
313 nobj._updateProperty(name, val)
314 return aq_base(nobj)
315
316
320
321
323 """
324 Return true if user has Manager role and self has a deviceList.
325 """
326 if not getattr(aq_base(self), "deviceMoveTargets", False):
327 return False
328
329 if self.isManager() or \
330 self.checkRemotePerm(ZEN_CHANGE_DEVICE_PRODSTATE, self):
331 return True
332
333 return False
334
335
337 """
338 Method needed for CatalogAwarnessInterface. Implemented here so that
339 Subclasses (who would have the same implementation) don't need to.
340 Other methods (except reindex_all) are implemented on the concreate
341 class.
342 """
343 users=[]
344 for user, roles in self.get_local_roles():
345 if 'Owner' in roles:
346 users.append(user)
347 return ', '.join(users)
348
349
351 """A common method to allow Findables to index themselves."""
352 cat = getattr(self, self.default_catalog, None)
353 if cat != None:
354 cat.catalog_object(self, self.getPrimaryId())
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