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 from xml.dom import minidom
23 import logging
24 log = logging.getLogger('zen')
25
26 from Globals import DTMLFile
27 from Globals import InitializeClass
28 from Acquisition import aq_base
29 from AccessControl import ClassSecurityInfo
30 from AccessControl import Permissions as permissions
31 from Products.ZenModel.ZenossSecurity import *
32
33 from Products.ZenUtils.IpUtil import *
34 from Products.ZenRelations.RelSchema import *
35 from Products.ZenUtils.Search import makeCaseInsensitiveFieldIndex
36
37 from IpAddress import IpAddress
38 from DeviceOrganizer import DeviceOrganizer
39
40 from Products.ZenModel.Exceptions import *
41
42 from Products.ZenUtils.Utils import isXmlRpc, setupLoggingHeader, executeCommand
43 from Products.ZenUtils.Utils import binPath, clearWebLoggingStream
44 from Products.ZenUtils import NetworkTree
45 from Products.ZenUtils.Utils import edgesToXML
46 from Products.ZenUtils.Utils import unused
47 from Products.Jobber.jobs import ShellCommandJob, JobMessenger
48 from Products.Jobber.status import SUCCESS, FAILURE
49 from Products.ZenWidgets import messaging
50
62
63
64 addIpNetwork = DTMLFile('dtml/addIpNetwork',globals())
65
66
67
68
69 defaultNetworkTree = (32,)
70
72 """IpNetwork object"""
73
74 isInTree = True
75
76 buildLinks = True
77
78
79 dmdRootName = "Networks"
80
81
82 default_catalog = 'ipSearch'
83
84 portal_type = meta_type = 'IpNetwork'
85
86 _properties = (
87 {'id':'netmask', 'type':'int', 'mode':'w'},
88 {'id':'description', 'type':'text', 'mode':'w'},
89 )
90
91 _relations = DeviceOrganizer._relations + (
92 ("ipaddresses", ToManyCont(ToOne, "Products.ZenModel.IpAddress", "network")),
93 ("clientroutes", ToMany(ToOne,"Products.ZenModel.IpRouteEntry","target")),
94 ("location", ToOne(ToMany, "Products.ZenModel.Location", "networks")),
95 )
96
97
98 factory_type_information = (
99 {
100 'id' : 'IpNetwork',
101 'meta_type' : 'IpNetwork',
102 'description' : """Arbitrary device grouping class""",
103 'icon' : 'IpNetwork_icon.gif',
104 'product' : 'ZenModel',
105 'factory' : 'manage_addIpNetwork',
106 'immediate_view' : 'viewNetworkOverview',
107 'actions' :
108 (
109 { 'id' : 'overview'
110 , 'name' : 'Overview'
111 , 'action' : 'viewNetworkOverview'
112 , 'permissions' : (
113 permissions.view, )
114 },
115 { 'id' : 'zProperties'
116 , 'name' : 'Configuration Properties'
117 , 'action' : 'zPropertyEdit'
118 , 'permissions' : ("Manage DMD",)
119 },
120 { 'id' : 'viewHistory'
121 , 'name' : 'Modifications'
122 , 'action' : 'viewHistory'
123 , 'permissions' : (ZEN_VIEW_MODIFICATIONS,)
124 },
125 )
126 },
127 )
128
129 security = ClassSecurityInfo()
130
131
132 - def __init__(self, id, netmask=24, description=''):
139
140 security.declareProtected('Change Network', 'manage_addIpNetwork')
142 """
143 From the GUI, create a new subnet (if necessary)
144 """
145 net = self.createNet(newPath)
146 if REQUEST is not None:
147 REQUEST['RESPONSE'].redirect(net.absolute_url())
148
154
155
157 """This is a hook method do not remove!"""
158 return self.dmd.getDmdRoot("Networks")
159
160
162 """
163 Return and create if necessary network. netip is in the form
164 1.1.1.0/24 or with netmask passed as parameter. Subnetworks created
165 based on the zParameter zDefaulNetworkTree.
166 Called by IpNetwork.createIp and IpRouteEntry.setTarget
167 If the netmask is invalid, then a netmask of 24 is assumed.
168
169 @param netip: network IP address start
170 @type netip: string
171 @param netmask: network mask
172 @type netmask: integer
173 @todo: investigate IPv6 issues
174 """
175 if netip.find("/") > -1: netip, netmask = netip.split("/",1)
176 try:
177 netmask = int(netmask)
178 except (TypeError, ValueError):
179 netmask = 24
180 if netmask < 0 or netmask >= 64:
181 netmask = 24
182
183
184 netroot = self.getNetworkRoot()
185 netobj = netroot.getNet(netip)
186 if netmask == 0:
187 raise ValueError("netip '%s' without netmask" % netip)
188 if netobj and netobj.netmask >= netmask:
189 return netobj
190
191 netip = getnetstr(netip,netmask)
192 netTree = getattr(self, 'zDefaultNetworkTree', defaultNetworkTree)
193 netTree = map(int, netTree)
194 if netobj:
195
196 netTree = [ m for m in netTree if m > netobj.netmask ]
197 else:
198
199 netobj = netroot
200
201 for treemask in netTree:
202 if treemask >= netmask:
203 netobjParent = netobj
204 netobj = netobj.addSubNetwork(netip, netmask)
205 self.rebalance(netobjParent, netobj)
206 break
207 else:
208 supnetip = getnetstr(netip, treemask)
209 netobjParent = netobj
210 netobj = netobj.addSubNetwork(supnetip, treemask)
211 self.rebalance(netobjParent, netobj)
212
213 return netobj
214
215
217 """
218 Look for children of the netobj at this level and move them to the
219 right spot.
220 """
221 moveList = []
222 for subnetOrIp in netobjParent.children():
223 if subnetOrIp == netobj:
224 continue
225 if netobj.hasIp(subnetOrIp.id):
226 moveList.append(subnetOrIp.id)
227 if moveList:
228 netobjPath = netobj.getOrganizerName()[1:]
229 netobjParent.moveOrganizer(netobjPath, moveList)
230
231 - def findNet(self, netip, netmask=0):
232 """
233 Find and return the subnet of this IpNetwork that matches the requested
234 netip and netmask.
235 """
236 if netip.find("/") >= 0:
237 netip, netmask = netip.split("/", 1)
238 netmask = int(netmask)
239 for subnet in [self] + self.getSubNetworks():
240 if netmask == 0 and subnet.id == netip:
241 return subnet
242 if subnet.id == netip and subnet.netmask == netmask:
243 return subnet
244 return None
245
246
248 """Return the net starting form the Networks root for ip.
249 """
250 return self._getNet(ip)
251
252
254 """Recurse down the network tree to find the net of ip.
255 """
256
257
258 brains = self.ipSearch(id=ip)
259 path = self.getPrimaryUrlPath()
260 for brain in brains:
261 bp = brain.getPath()
262 if bp.startswith(path):
263 return self.unrestrictedTraverse('/'.join(bp.split('/')[:-2]))
264
265
266 for net in self.children():
267 if net.hasIp(ip):
268 if len(net.children()):
269 subnet = net._getNet(ip)
270 if subnet:
271 return subnet
272 else:
273 return net
274 else:
275 return net
276
277
279 """Return an ip and create if nessesary in a hierarchy of
280 subnetworks based on the zParameter zDefaulNetworkTree.
281 """
282 ipobj = self.findIp(ip)
283 if ipobj: return ipobj
284 netobj = self.createNet(ip, netmask)
285 ipobj = netobj.addIpAddress(ip,netmask)
286 return ipobj
287
288
290 """Number of free Ips left in this network.
291 """
292 freeips = int(math.pow(2,32-self.netmask)-(self.countIpAddresses()))
293 if self.netmask >= 31:
294 return freeips
295 return freeips - 2
296
297
304
305
307 """Return a list of all ips in this network.
308 """
309 if (self.netmask == 32): return [self.id]
310 ipnumb = numbip(self.id)
311 maxip = math.pow(2,32-self.netmask)
312 start = int(ipnumb+1)
313 end = int(ipnumb+maxip-1)
314 return map(strip, range(start,end))
315
316
323
324
326 """Return the ip of the default router for this network.
327 It is based on zDefaultRouterNumber which specifies the sequence
328 number that locates the router in this network. If:
329 zDefaultRouterNumber==1 for 10.2.1.0/24 -> 10.2.1.1
330 zDefaultRouterNumber==254 for 10.2.1.0/24 -> 10.2.1.254
331 zDefaultRouterNumber==1 for 10.2.2.128/25 -> 10.2.2.129
332 zDefaultRouterNumber==126 for 10.2.2.128/25 -> 10.2.2.254
333 """
334 roffset = getattr(self, "zDefaultRouterNumber", 1)
335 return strip((numbip(self.id) + roffset))
336
337
339 """return the full network name of this network"""
340 return "%s/%d" % (self.id, self.netmask)
341
342
343 security.declareProtected('View', 'primarySortKey')
345 """
346 Sort by the IP numeric
347
348 >>> net = dmd.Networks.addSubNetwork('1.2.3.0', 24)
349 >>> net.primarySortKey()
350 16909056L
351 """
352 return numbip(self.id)
353
354
355 security.declareProtected('Change Network', 'addSubNetwork')
364
365
366 security.declareProtected('View', 'getSubNetwork')
368 """get an ip on this network"""
369 return self._getOb(ip, None)
370
371
373 """Return all network objects below this one.
374 """
375 nets = self.children()
376 for subgroup in self.children():
377 nets.extend(subgroup.getSubNetworks())
378 return nets
379
380 security.declareProtected('Change Network', 'addIpAddress')
386
387
388 security.declareProtected('View', 'getIpAddress')
392
393 security.declareProtected('Change Network', 'manage_deleteIpAddresses')
402
403
404 security.declareProtected('View', 'countIpAddresses')
419
420 security.declareProtected('View', 'countDevices')
421 countDevices = countIpAddresses
422
423
425 """Count all devices within a device group and get the
426 ping and snmp counts as well"""
427 unused(devrel)
428 counts = [
429 self.ipaddresses.countObjects(),
430 self._status("Ping", "ipaddresses"),
431 self._status("Snmp", "ipaddresses"),
432 ]
433 for group in self.children():
434 sc = group.getAllCounts()
435 for i in range(3): counts[i] += sc[i]
436 return counts
437
438
443
444
449
450
454
455
457 """Find an ipAddress.
458 """
459 searchCatalog = self.getDmdRoot("Networks").ipSearch
460 ret = searchCatalog(dict(id=ip))
461 if not ret: return None
462 if len(ret) > 1:
463 raise IpAddressConflict( "IP address conflict for IP: %s" % ip )
464 return ret[0].getObject()
465
466
468 nets = self.getDmdRoot("Networks")
469 if getattr(aq_base(nets), "zDefaultNetworkTree", False):
470 return
471 nets._setProperty("zDefaultNetworkTree", (24,32), type="lines")
472 nets._setProperty("zDrawMapLinks", True, type="boolean")
473 nets._setProperty("zAutoDiscover", True, type="boolean")
474 nets._setProperty("zPingFailThresh", 168, type="int")
475 nets._setProperty("zIcon", "/zport/dmd/img/icons/network.png")
476 nets._setProperty("zPreferSnmpNaming", False, type="boolean")
477 nets._setProperty("zSnmpStrictDiscovery", False, type="boolean")
478
479
481 """Go through all ips in this tree and reindex them."""
482 zcat = self._getCatalog()
483 zcat.manage_catalogClear()
484 transaction.savepoint()
485 for net in self.getSubNetworks():
486 for ip in net.ipaddresses():
487 ip.index_object()
488 transaction.savepoint()
489
490
502
503
509
511 """
512 Load a device into the database connecting its major relations
513 and collecting its configuration.
514 """
515 xmlrpc = isXmlRpc(REQUEST)
516
517 if not organizerPaths:
518 if xmlrpc: return 1
519 return self.callZenScreen(REQUEST)
520
521 zDiscCommand = "empty"
522
523 from Products.ZenUtils.ZenTales import talesEval
524
525 orgroot = self.getNetworkRoot()
526 for organizerName in organizerPaths:
527 organizer = orgroot.getOrganizer(organizerName)
528 if organizer is None:
529 if xmlrpc: return 1
530 log.error("Couldn't obtain a network entry for '%s' "
531 "-- does it exist?" % organizerName)
532 continue
533
534 zDiscCommand = getattr(organizer, "zZenDiscCommand", None)
535 if zDiscCommand:
536 cmd = talesEval('string:' + zDiscCommand, organizer).split(" ")
537 else:
538 cmd = ["zendisc", "run", "--net", organizer.getNetworkName()]
539 if getattr(organizer, "zSnmpStrictDiscovery", False):
540 cmd += ["--snmp-strict-discovery"]
541 if getattr(organizer, "zPreferSnmpNaming", False):
542 cmd += ["--prefer-snmp-naming"]
543 zd = binPath('zendisc')
544 zendiscCmd = [zd] + cmd[1:]
545 status = self.dmd.JobManager.addJob(ShellCommandJob, zendiscCmd)
546
547 log.info('Done')
548
549 if REQUEST and not xmlrpc:
550 REQUEST.RESPONSE.redirect('/zport/dmd/JobManager/joblist')
551
552 if xmlrpc: return 0
553
554
556 """setup logging package to send to browser"""
557 from logging import StreamHandler, Formatter
558 root = logging.getLogger()
559 self._v_handler = StreamHandler(response)
560 fmt = Formatter("""<tr class="tablevalues">
561 <td>%(asctime)s</td><td>%(levelname)s</td>
562 <td>%(name)s</td><td>%(message)s</td></tr>
563 """, "%Y-%m-%d %H:%M:%S")
564 self._v_handler.setFormatter(fmt)
565 root.addHandler(self._v_handler)
566 root.setLevel(10)
567
568
570 alog = logging.getLogger()
571 if getattr(self, "_v_handler", False):
572 alog.removeHandler(self._v_handler)
573
574
581
582 security.declareProtected('View', 'getXMLEdges')
589
591 """ gets icon """
592 try:
593 return self.primaryAq().zIcon
594 except AttributeError:
595 return '/zport/dmd/img/icons/noicon.png'
596
597
598 - def urlLink(self, text=None, url=None, attrs={}):
599 """
600 Return an anchor tag if the user has access to the remote object.
601 @param text: the text to place within the anchor tag or string.
602 Defaults to the id of this object.
603 @param url: url for the href. Default is getPrimaryUrlPath
604 @type attrs: dict
605 @param attrs: any other attributes to be place in the in the tag.
606 @return: An HTML link to this object
607 @rtype: string
608 """
609 if not text:
610 text = "%s/%d" % (self.id, self.netmask)
611 if not self.checkRemotePerm("View", self):
612 return text
613 if not url:
614 url = self.getPrimaryUrlPath()
615 if len(attrs):
616 return '<a href="%s" %s>%s</a>' % (url,
617 ' '.join(['%s="%s"' % (x,y) for x,y in attrs.items()]),
618 text)
619 else:
620 return '<a href="%s">%s</a>' % (url, text)
621
622 InitializeClass(IpNetwork)
623
624
625
627 """
628 Job encapsulating autodiscovery over a set of IP addresses.
629
630 Accepts a list of strings describing networks OR a list of strings
631 specifying IP ranges, not both. Also accepts a set of zProperties to be
632 set on devices that are discovered.
633 """
634 - def __init__(self, jobid, nets=(), ranges=(), zProperties=()):
635
636 self.nets = nets
637 self.ranges = ranges
638 self.zProperties = zProperties
639
640
641 super(AutoDiscoveryJob, self).__init__(jobid, '')
642
644 transaction.commit()
645 log = self.getStatus().getLog()
646
647
648 if self.zProperties:
649 self.getStatus().setZProperties(**self.zProperties)
650 transaction.commit()
651
652
653 cmd = [binPath('zendisc')]
654 cmd.extend(['run', '--now',
655 '--monitor', 'localhost',
656 '--deviceclass', '/Discovered',
657 '--parallel', '8',
658 '--job', self.getUid()
659 ])
660 if not self.nets and not self.ranges:
661
662 log.write("ERROR: Must pass in a network or a range.")
663 self.finished(FAILURE)
664 elif self.nets and self.ranges:
665
666 log.write("ERROR: Must pass in either networks or ranges, "
667 "not both.")
668 self.finished(FAILURE)
669 else:
670 if self.nets:
671 for net in self.nets:
672 cmd.extend(['--net', net])
673 elif self.ranges:
674 for iprange in self.ranges:
675 cmd.extend(['--range', iprange])
676 self.cmd = cmd
677 super(AutoDiscoveryJob, self).run(r)
678
680 if self.nets:
681 details = 'networks %s' % ', '.join(self.nets)
682 elif self.ranges:
683 details = 'IP ranges %s' % ', '.join(self.ranges)
684 if r==SUCCESS:
685 JobMessenger(self).sendToUser(
686 'Discovery Complete',
687 'Discovery of %s has completed successfully.' % details
688 )
689 elif r==FAILURE:
690 JobMessenger(self).sendToUser(
691 'Discovery Failed',
692 'An error occurred discovering %s.' % details,
693 priority=messaging.WARNING
694 )
695 super(AutoDiscoveryJob, self).finished(r)
696
697
699
701 """out is the output stream to print to"""
702 self._out = out
703
704
705 -class TextIpNetworkPrinter(IpNetworkPrinter):
706 """
707 Prints out IpNetwork hierarchy as text with indented lines.
708 """
709
710 - def printIpNetwork(self, net):
711 """
712 Print out the IpNetwork and IpAddress hierarchy under net.
713 """
714 self._printIpNetworkLine(net)
715 self._printTree(net)
716
717 - def _printTree(self, net, indent=" "):
718 for child in net.children():
719 self._printIpNetworkLine(child, indent)
720 self._printTree(child, indent + " ")
721 for ipaddress in net.ipaddresses():
722 args = (indent, ipaddress, ipaddress.__class__.__name__)
723 self._out.write("%s%s (%s)\n" % args)
724
725 - def _printIpNetworkLine(self, net, indent=""):
726 args = (indent, net.id, net.netmask, net.__class__.__name__)
727 self._out.write("%s%s/%s (%s)\n" % args)
728
729
731 """
732 Prints out the IpNetwork hierarchy as a python dictionary.
733 """
734
736 """
737 Print out the IpNetwork and IpAddress hierarchy under net.
738 """
739 tree = {}
740 self._createTree(net, tree)
741 from pprint import pformat
742 self._out.write("%s\n" % pformat(tree))
743
750
756
757
759 """
760 Prints out the IpNetwork hierarchy as XML.
761 """
762
764 """
765 Print out the IpNetwork and IpAddress hierarchy under net.
766 """
767 self._doc = minidom.parseString('<root/>')
768 root = self._doc.documentElement
769 self._createTree(net, root)
770 self._out.write(self._doc.toprettyxml())
771
777
781
783 node = self._doc.createElement(child.__class__.__name__)
784 node.setAttribute("id", child.id)
785 node.setAttribute("netmask", str(child.netmask))
786 tree.appendChild(node)
787 return node
788
789
791
796
798 if format in self._printerFactories:
799 factory = self._printerFactories[format]
800 return factory(out)
801 else:
802 args = (format, self._printerFactories.keys())
803 raise Exception("Invalid format '%s' must be one of %s" % args)
804