1
2
3
4
5
6
7
8
9
10
11
12
13
14 __doc__ = """IpNetwork
15
16 IpNetwork represents an IP network which contains
17 many IP addresses.
18 """
19
20 import math
21 import transaction
22 import logging
23 log = logging.getLogger('zen')
24
25 from Globals import DTMLFile
26 from Globals import InitializeClass
27 from Acquisition import aq_base
28 from AccessControl import ClassSecurityInfo
29 from AccessControl import Permissions as permissions
30 from Products.ZenModel.ZenossSecurity import *
31
32 from Products.ZenUtils.IpUtil import *
33 from Products.ZenRelations.RelSchema import *
34 from Products.ZenUtils.Search import makeCaseInsensitiveFieldIndex
35
36 from IpAddress import IpAddress
37 from DeviceOrganizer import DeviceOrganizer
38
39 from Products.ZenModel.Exceptions import *
40
41 from Products.ZenUtils.Utils import isXmlRpc, setupLoggingHeader, executeCommand
42 from Products.ZenUtils.Utils import binPath, clearWebLoggingStream
43 from Products.ZenUtils import NetworkTree
44 from Products.ZenUtils.Utils import edgesToXML
45 from Products.ZenUtils.Utils import unused
46 from Products.Jobber.jobs import ShellCommandJob, JobMessenger
47 from Products.Jobber.status import SUCCESS, FAILURE
48 from Products.ZenWidgets import messaging
49
61
62
63 addIpNetwork = DTMLFile('dtml/addIpNetwork',globals())
64
65
66
67
68 defaultNetworkTree = (32,)
69
71 """IpNetwork object"""
72
73 isInTree = True
74
75 buildLinks = True
76
77
78 dmdRootName = "Networks"
79
80
81 default_catalog = 'ipSearch'
82
83 portal_type = meta_type = 'IpNetwork'
84
85 _properties = (
86 {'id':'netmask', 'type':'int', 'mode':'w'},
87 {'id':'description', 'type':'text', 'mode':'w'},
88 )
89
90 _relations = DeviceOrganizer._relations + (
91 ("ipaddresses", ToManyCont(ToOne, "Products.ZenModel.IpAddress", "network")),
92 ("clientroutes", ToMany(ToOne,"Products.ZenModel.IpRouteEntry","target")),
93 ("location", ToOne(ToMany, "Products.ZenModel.Location", "networks")),
94 )
95
96
97 factory_type_information = (
98 {
99 'id' : 'IpNetwork',
100 'meta_type' : 'IpNetwork',
101 'description' : """Arbitrary device grouping class""",
102 'icon' : 'IpNetwork_icon.gif',
103 'product' : 'ZenModel',
104 'factory' : 'manage_addIpNetwork',
105 'immediate_view' : 'viewNetworkOverview',
106 'actions' :
107 (
108 { 'id' : 'overview'
109 , 'name' : 'Overview'
110 , 'action' : 'viewNetworkOverview'
111 , 'permissions' : (
112 permissions.view, )
113 },
114 { 'id' : 'zProperties'
115 , 'name' : 'zProperties'
116 , 'action' : 'zPropertyEdit'
117 , 'permissions' : ("Manage DMD",)
118 },
119 { 'id' : 'viewHistory'
120 , 'name' : 'Modifications'
121 , 'action' : 'viewHistory'
122 , 'permissions' : (ZEN_VIEW_MODIFICATIONS,)
123 },
124 )
125 },
126 )
127
128 security = ClassSecurityInfo()
129
130
131 - def __init__(self, id, netmask=24, description=''):
138
139
145
146
148 """This is a hook method do not remove!"""
149 return self.dmd.getDmdRoot("Networks")
150
151
153 """
154 Return and create if necessary network. netip is in the form
155 1.1.1.0/24 or with netmask passed as parameter. Subnetworks created
156 based on the zParameter zDefaulNetworkTree.
157 Called by IpNetwork.createIp and IpRouteEntry.setTarget
158 If the netmask is invalid, then a netmask of 24 is assumed.
159
160 @param netip: network IP address start
161 @type netip: string
162 @param netmask: network mask
163 @type netmask: integer
164 @todo: investigate IPv6 issues
165 """
166 if netip.find("/") > -1: netip, netmask = netip.split("/",1)
167 try:
168 netmask = int(netmask)
169 except (TypeError, ValueError):
170 netmask = 24
171 if netmask < 0 or netmask >= 64:
172 netmask = 24
173
174
175 netroot = self.getNetworkRoot()
176 netobj = netroot.getNet(netip)
177 if netobj and netobj.netmask == netmask:
178 return netobj
179 if netmask == 0:
180 raise ValueError("netip '%s' without netmask" % netip)
181 netip = getnetstr(netip,netmask)
182 netTree = getattr(self, 'zDefaultNetworkTree', defaultNetworkTree)
183 netTree = map(int, netTree)
184 if netobj:
185
186 netTree = [ m for m in netTree if m > netobj.netmask ]
187 else:
188
189 netobj = netroot
190
191 for treemask in netTree:
192 if treemask >= netmask:
193 netobj = netobj.addSubNetwork(netip, netmask)
194 break
195 else:
196 supnetip = getnetstr(netip, treemask)
197 netobj = netobj.addSubNetwork(supnetip, treemask)
198 return netobj
199
200
201 - def findNet(self, netip, netmask=0):
202 """
203 Find and return the subnet of this IpNetwork that matches the requested
204 netip and netmask.
205 """
206 if netip.find("/") >= 0:
207 netip, netmask = netip.split("/", 1)
208 netmask = int(netmask)
209 for subnet in [self] + self.getSubNetworks():
210 if netmask == 0 and subnet.id == netip:
211 return subnet
212 if subnet.id == netip and subnet.netmask == netmask:
213 return subnet
214 return None
215
216
218 """Return the net starting form the Networks root for ip.
219 """
220 return self._getNet(ip)
221
222
224 """Recurse down the network tree to find the net of ip.
225 """
226 for net in self.children():
227 if net.hasIp(ip):
228 if len(net.children()):
229 subnet = net._getNet(ip)
230 if subnet:
231 return subnet
232 else:
233 return net
234 else:
235 return net
236
237
239 """Return an ip and create if nessesary in a hierarchy of
240 subnetworks based on the zParameter zDefaulNetworkTree.
241 """
242 ipobj = self.findIp(ip)
243 if ipobj: return ipobj
244 netobj = self.createNet(ip, netmask)
245 ipobj = netobj.addIpAddress(ip,netmask)
246 return ipobj
247
248
250 """Number of free Ips left in this network.
251 """
252 freeips = int(math.pow(2,32-self.netmask)-(self.countIpAddresses()))
253 if self.netmask >= 31:
254 return freeips
255 return freeips - 2
256
257
264
265
267 """Return a list of all ips in this network.
268 """
269 if (self.netmask == 32): return [self.id]
270 ipnumb = numbip(self.id)
271 maxip = math.pow(2,32-self.netmask)
272 start = int(ipnumb+1)
273 end = int(ipnumb+maxip-1)
274 return map(strip, range(start,end))
275
276
283
284
286 """Return the ip of the default router for this network.
287 It is based on zDefaultRouterNumber which specifies the sequence
288 number that locates the router in this network. If:
289 zDefaultRouterNumber==1 for 10.2.1.0/24 -> 10.2.1.1
290 zDefaultRouterNumber==254 for 10.2.1.0/24 -> 10.2.1.254
291 zDefaultRouterNumber==1 for 10.2.2.128/25 -> 10.2.2.129
292 zDefaultRouterNumber==126 for 10.2.2.128/25 -> 10.2.2.254
293 """
294 roffset = getattr(self, "zDefaultRouterNumber", 1)
295 return strip((numbip(self.id) + roffset))
296
297
299 """return the full network name of this network"""
300 return "%s/%d" % (self.id, self.netmask)
301
302
303 security.declareProtected('View', 'primarySortKey')
305 """make sure that networks sort correctly"""
306 return numbip(self.id)
307
308
309 security.declareProtected('Change Network', 'addSubNetwork')
318
319
320 security.declareProtected('View', 'getSubNetwork')
322 """get an ip on this network"""
323 return self._getOb(ip, None)
324
325
327 """Return all network objects below this one.
328 """
329 nets = self.children()
330 for subgroup in self.children():
331 nets.extend(subgroup.getSubNetworks())
332 return nets
333
334 security.declareProtected('Change Network', 'addIpAddress')
340
341
342 security.declareProtected('View', 'getIpAddress')
346
347 security.declareProtected('Change Network', 'manage_deleteIpAddresses')
356
357
358 security.declareProtected('View', 'countIpAddresses')
373
374 security.declareProtected('View', 'countDevices')
375 countDevices = countIpAddresses
376
377
379 """Count all devices within a device group and get the
380 ping and snmp counts as well"""
381 unused(devrel)
382 counts = [
383 self.ipaddresses.countObjects(),
384 self._status("Ping", "ipaddresses"),
385 self._status("Snmp", "ipaddresses"),
386 ]
387 for group in self.children():
388 sc = group.getAllCounts()
389 for i in range(3): counts[i] += sc[i]
390 return counts
391
392
397
398
403
404
408
409
411 """Find an ipAddress.
412 """
413 searchCatalog = self.getDmdRoot("Networks").ipSearch
414 ret = searchCatalog(dict(id=ip))
415 if not ret: return None
416 if len(ret) > 1:
417 raise IpAddressConflict( "IP address conflict for IP: %s" % ip )
418 return ret[0].getObject()
419
420
422 nets = self.getDmdRoot("Networks")
423 if getattr(aq_base(nets), "zDefaultNetworkTree", False):
424 return
425 nets._setProperty("zDefaultNetworkTree", (24,32), type="lines")
426 nets._setProperty("zDrawMapLinks", True, type="boolean")
427 nets._setProperty("zAutoDiscover", True, type="boolean")
428 nets._setProperty("zPingFailThresh", 168, type="int")
429 nets._setProperty("zIcon", "/zport/dmd/img/icons/network.png")
430
431
433 """Go through all ips in this tree and reindex them."""
434 zcat = self._getCatalog()
435 zcat.manage_catalogClear()
436 transaction.savepoint()
437 for net in self.getSubNetworks():
438 for ip in net.ipaddresses():
439 ip.index_object()
440 transaction.savepoint()
441
442
454
455
461
463 """
464 Load a device into the database connecting its major relations
465 and collecting its configuration.
466 """
467 xmlrpc = isXmlRpc(REQUEST)
468
469 if not organizerPaths:
470 if xmlrpc: return 1
471 return self.callZenScreen(REQUEST)
472
473 zDiscCommand = "empty"
474
475 from Products.ZenUtils.ZenTales import talesEval
476
477 orgroot = self.getNetworkRoot()
478 for organizerName in organizerPaths:
479 organizer = orgroot.getOrganizer(organizerName)
480 if organizer is None:
481 if xmlrpc: return 1
482 log.error("Couldn't obtain a network entry for '%s' "
483 "-- does it exist?" % organizerName)
484 continue
485
486 zDiscCommand = getattr(organizer, "zZenDiscCommand", None)
487 if zDiscCommand:
488 cmd = talesEval('string:' + zDiscCommand, organizer).split(" ")
489 else:
490 cmd = ["zendisc", "run", "--net", organizer.getNetworkName()]
491 if getattr(organizer, "zSnmpStrictDiscovery", False):
492 cmd += ["--snmp-strict-discovery"]
493 if getattr(organizer, "zPreferSnmpNaming", False):
494 cmd += ["--prefer-snmp-naming"]
495 zd = binPath('zendisc')
496 zendiscCmd = [zd] + cmd[1:]
497 status = self.dmd.JobManager.addJob(ShellCommandJob, zendiscCmd)
498
499 log.info('Done')
500
501 if REQUEST and not xmlrpc:
502 REQUEST.RESPONSE.redirect('/zport/dmd/JobManager/joblist')
503
504 if xmlrpc: return 0
505
506
508 """setup logging package to send to browser"""
509 from logging import StreamHandler, Formatter
510 root = logging.getLogger()
511 self._v_handler = StreamHandler(response)
512 fmt = Formatter("""<tr class="tablevalues">
513 <td>%(asctime)s</td><td>%(levelname)s</td>
514 <td>%(name)s</td><td>%(message)s</td></tr>
515 """, "%Y-%m-%d %H:%M:%S")
516 self._v_handler.setFormatter(fmt)
517 root.addHandler(self._v_handler)
518 root.setLevel(10)
519
520
522 alog = logging.getLogger()
523 if getattr(self, "_v_handler", False):
524 alog.removeHandler(self._v_handler)
525
526
533
534 security.declareProtected('View', 'getXMLEdges')
541
543 """ gets icon """
544 try:
545 return self.primaryAq().zIcon
546 except AttributeError:
547 return '/zport/dmd/img/icons/noicon.png'
548
549
550 - def urlLink(self, text=None, url=None, attrs={}):
551 """
552 Return an anchor tag if the user has access to the remote object.
553 @param text: the text to place within the anchor tag or string.
554 Defaults to the id of this object.
555 @param url: url for the href. Default is getPrimaryUrlPath
556 @type attrs: dict
557 @param attrs: any other attributes to be place in the in the tag.
558 @return: An HTML link to this object
559 @rtype: string
560 """
561 if not text:
562 text = "%s/%d" % (self.id, self.netmask)
563 if not self.checkRemotePerm("View", self):
564 return text
565 if not url:
566 url = self.getPrimaryUrlPath()
567 if len(attrs):
568 return '<a href="%s" %s>%s</a>' % (url,
569 ' '.join(['%s="%s"' % (x,y) for x,y in attrs.items()]),
570 text)
571 else:
572 return '<a href="%s">%s</a>' % (url, text)
573
574 InitializeClass(IpNetwork)
575
576
577
579 """
580 Job encapsulating autodiscovery over a set of IP addresses.
581
582 Accepts a list of strings describing networks OR a list of strings
583 specifying IP ranges, not both. Also accepts a set of zProperties to be
584 set on devices that are discovered.
585 """
586 - def __init__(self, jobid, nets=(), ranges=(), zProperties=()):
587
588 self.nets = nets
589 self.ranges = ranges
590 self.zProperties = zProperties
591
592
593 super(AutoDiscoveryJob, self).__init__(jobid, '')
594
596 transaction.commit()
597 log = self.getStatus().getLog()
598
599
600 if self.zProperties:
601 self.getStatus().setZProperties(**self.zProperties)
602 transaction.commit()
603
604
605 cmd = [binPath('zendisc')]
606 cmd.extend(['run', '--now',
607 '--monitor', 'localhost',
608 '--deviceclass', '/Discovered',
609 '--parallel', '8',
610 '--job', self.getUid()
611 ])
612 if not self.nets and not self.ranges:
613
614 log.write("ERROR: Must pass in a network or a range.")
615 self.finished(FAILURE)
616 elif self.nets and self.ranges:
617
618 log.write("ERROR: Must pass in either networks or ranges, "
619 "not both.")
620 self.finished(FAILURE)
621 else:
622 if self.nets:
623 for net in self.nets:
624 cmd.extend(['--net', net])
625 elif self.ranges:
626 for iprange in self.ranges:
627 cmd.extend(['--range', iprange])
628 self.cmd = cmd
629 super(AutoDiscoveryJob, self).run(r)
630
648