Package ZenModel :: Module IpNetwork
[hide private]
[frames] | no frames]

Source Code for Module ZenModel.IpNetwork

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2008, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 as published by 
  8  # the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 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   
50 -def manage_addIpNetwork(context, id, netmask=24, REQUEST = None):
51 """make a IpNetwork""" 52 net = IpNetwork(id, netmask=netmask) 53 context._setObject(net.id, net) 54 if id == "Networks": 55 net = context._getOb(net.id) 56 net.buildZProperties() 57 net.createCatalog() 58 #manage_addZDeviceDiscoverer(context) 59 if REQUEST is not None: 60 REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main')
61 62 63 addIpNetwork = DTMLFile('dtml/addIpNetwork',globals()) 64 65 66 # when an ip is added the defaul location will be 67 # into class A->B->C network tree 68 defaultNetworkTree = (32,) 69
70 -class IpNetwork(DeviceOrganizer):
71 """IpNetwork object""" 72 73 isInTree = True 74 75 buildLinks = True 76 77 # Organizer configuration 78 dmdRootName = "Networks" 79 80 # Index name for IP addresses 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 # Screen action bindings (and tab definitions) 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=''):
132 if id.find("/") > -1: id, netmask = id.split("/",1) 133 DeviceOrganizer.__init__(self, id, description) 134 if id != "Networks": 135 checkip(id) 136 self.netmask = maskToBits(netmask) 137 self.description = description
138 139
140 - def checkValidId(self, id, prep_id = False):
141 """Checks a valid id 142 """ 143 if id.find("/") > -1: id, netmask = id.split("/",1) 144 return super(IpNetwork, self).checkValidId(id, prep_id)
145 146
147 - def getNetworkRoot(self):
148 """This is a hook method do not remove!""" 149 return self.dmd.getDmdRoot("Networks")
150 151
152 - def createNet(self, netip, netmask=24):
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 #hook method do not remove! 175 netroot = self.getNetworkRoot() 176 netobj = netroot.getNet(netip) 177 if netobj and netobj.netmask == netmask: # Network already exists. 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 # strip irrelevant values from netTree if we're not starting at /0 186 netTree = [ m for m in netTree if m > netobj.netmask ] 187 else: 188 # start at /Networks if no containing network was found 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
217 - def getNet(self, ip):
218 """Return the net starting form the Networks root for ip. 219 """ 220 return self._getNet(ip)
221 222
223 - def _getNet(self, ip):
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
238 - def createIp(self, ip, netmask=24):
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
249 - def freeIps(self):
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
258 - def hasIp(self, ip):
259 """Does network have (contain) this ip. 260 """ 261 start = numbip(self.id) 262 end = start + math.pow(2,32-self.netmask) 263 return start <= numbip(ip) < end
264 265
266 - def fullIpList(self):
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
277 - def deleteUnusedIps(self):
278 """Delete ips that are unused in this network. 279 """ 280 for ip in self.ipaddresses(): 281 if ip.device(): continue 282 self.ipaddresses.removeRelation(ip)
283 284
285 - def defaultRouterIp(self):
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
298 - def getNetworkName(self):
299 """return the full network name of this network""" 300 return "%s/%d" % (self.id, self.netmask)
301 302 303 security.declareProtected('View', 'primarySortKey')
304 - def primarySortKey(self):
305 """make sure that networks sort correctly""" 306 return numbip(self.id)
307 308 309 security.declareProtected('Change Network', 'addSubNetwork')
310 - def addSubNetwork(self, ip, netmask=24):
311 """Return and add if nessesary subnetwork to this network. 312 """ 313 netobj = self.getSubNetwork(ip) 314 if not netobj: 315 net = IpNetwork(ip, netmask) 316 self._setObject(ip, net) 317 return self.getSubNetwork(ip)
318 319 320 security.declareProtected('View', 'getSubNetwork')
321 - def getSubNetwork(self, ip):
322 """get an ip on this network""" 323 return self._getOb(ip, None)
324 325
326 - def getSubNetworks(self):
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')
335 - def addIpAddress(self, ip, netmask=24):
336 """add ip to this network and return it""" 337 ipobj = IpAddress(ip,netmask) 338 self.ipaddresses._setObject(ip, ipobj) 339 return self.getIpAddress(ip)
340 341 342 security.declareProtected('View', 'getIpAddress')
343 - def getIpAddress(self, ip):
344 """get an ip on this network""" 345 return self.ipaddresses._getOb(ip, None)
346 347 security.declareProtected('Change Network', 'manage_deleteIpAddresses')
348 - def manage_deleteIpAddresses(self, ipaddresses=(), REQUEST=None):
349 """Delete ipaddresses by id from this network. 350 """ 351 for ipaddress in ipaddresses: 352 ip = self.getIpAddress(ipaddress) 353 self.ipaddresses.removeRelation(ip) 354 if REQUEST: 355 return self.callZenScreen(REQUEST)
356 357 358 security.declareProtected('View', 'countIpAddresses')
359 - def countIpAddresses(self, inuse=False):
360 """get an ip on this network""" 361 if inuse: 362 # When there are a large number of IPs this code is too slow 363 # we either need to cache all /Status/Ping events before hand 364 # and then integrate them with the list of IPs 365 # or blow off the whole feature. For now we just set the 366 # default to not use this code. -EAD 367 count = len(filter(lambda x: x.getStatus() == 0,self.ipaddresses())) 368 else: 369 count = self.ipaddresses.countObjects() 370 for net in self.children(): 371 count += net.countIpAddresses(inuse) 372 return count
373 374 security.declareProtected('View', 'countDevices') 375 countDevices = countIpAddresses 376 377
378 - def getAllCounts(self, devrel=None):
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
393 - def pingStatus(self, devrel=None):
394 """aggregate ping status for all devices in this group and below""" 395 unused(devrel) 396 return DeviceOrganizer.pingStatus(self, "ipaddresses")
397 398
399 - def snmpStatus(self, devrel=None):
400 """aggregate snmp status for all devices in this group and below""" 401 unused(devrel) 402 return DeviceOrganizer.snmpStatus(self, "ipaddresses")
403 404
405 - def getSubDevices(self, filter=None):
406 """get all the devices under and instance of a DeviceGroup""" 407 return DeviceOrganizer.getSubDevices(self, filter, "ipaddresses")
408 409
410 - def findIp(self, ip):
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
421 - def buildZProperties(self):
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
432 - def reIndex(self):
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
443 - def createCatalog(self):
444 """make the catalog for device searching""" 445 from Products.ZCatalog.ZCatalog import manage_addZCatalog 446 447 # XXX convert to ManagableIndex 448 manage_addZCatalog(self, self.default_catalog, 449 self.default_catalog) 450 zcat = self._getOb(self.default_catalog) 451 cat = zcat._catalog 452 cat.addIndex('id', makeCaseInsensitiveFieldIndex('id')) 453 zcat.addColumn('getPrimaryId')
454 455
456 - def discoverNetwork(self, REQUEST=None):
457 """ 458 """ 459 path = '/'.join(self.getPrimaryPath()[4:]) 460 return self.discoverDevices([path], REQUEST=REQUEST)
461
462 - def discoverDevices(self, organizerPaths=None, REQUEST = None):
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 # XML-RPC error 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
507 - def setupLog(self, response):
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
521 - def clearLog(self):
522 alog = logging.getLogger() 523 if getattr(self, "_v_handler", False): 524 alog.removeHandler(self._v_handler)
525 526
527 - def loaderFooter(self, response):
528 """add navigation links to the end of the loader output""" 529 response.write("""<tr class="tableheader"><td colspan="4"> 530 Navigate to network <a href=%s>%s</a></td></tr>""" 531 % (self.absolute_url(), self.id)) 532 response.write("</table></body></html>")
533 534 security.declareProtected('View', 'getXMLEdges')
535 - def getXMLEdges(self, depth=1, filter='/', start=()):
536 """ Gets XML """ 537 if not start: start=self.id 538 edges = NetworkTree.get_edges(self, depth, 539 withIcons=True, filter=filter) 540 return edgesToXML(edges, start)
541
542 - def getIconPath(self):
543 """ gets icon """ 544 try: 545 return self.primaryAq().zIcon 546 except AttributeError: 547 return '/zport/dmd/img/icons/noicon.png'
548 549
573 574 InitializeClass(IpNetwork) 575 576 577
578 -class AutoDiscoveryJob(ShellCommandJob):
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 # Store the nets and ranges 588 self.nets = nets 589 self.ranges = ranges 590 self.zProperties = zProperties 591 592 # Set up the job, passing in a blank command (gets set later) 593 super(AutoDiscoveryJob, self).__init__(jobid, '')
594
595 - def run(self, r):
596 transaction.commit() 597 log = self.getStatus().getLog() 598 599 # Store zProperties on the job 600 if self.zProperties: 601 self.getStatus().setZProperties(**self.zProperties) 602 transaction.commit() 603 604 # Build the zendisc command 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 # Gotta have something 614 log.write("ERROR: Must pass in a network or a range.") 615 self.finished(FAILURE) 616 elif self.nets and self.ranges: 617 # Can't have both 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
631 - def finished(self, r):
632 if self.nets: 633 details = 'networks %s' % ', '.join(self.nets) 634 elif self.ranges: 635 details = 'IP ranges %s' % ', '.join(self.ranges) 636 if r==SUCCESS: 637 JobMessenger(self).sendToUser( 638 'Discovery Complete', 639 'Discovery of %s has completed successfully.' % details 640 ) 641 elif r==FAILURE: 642 JobMessenger(self).sendToUser( 643 'Discovery Failed', 644 'An error occurred discovering %s.' % details, 645 priority=messaging.WARNING 646 ) 647 super(AutoDiscoveryJob, self).finished(r)
648