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

Source Code for Module ZenModel.ZenossInfo

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2007, 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  import os 
 15  import re 
 16  import time 
 17  import logging 
 18  log = logging.getLogger("zen.ZenossInfo") 
 19   
 20  from Globals import InitializeClass 
 21  from OFS.SimpleItem import SimpleItem 
 22  from AccessControl import ClassSecurityInfo 
 23   
 24  from Products.ZenModel.ZenModelItem import ZenModelItem 
 25  from Products.ZenUtils import Time 
 26  from Products.ZenUtils.Version import * 
 27  from Products.ZenUtils.Utils import zenPath 
 28   
 29  from Products.ZenEvents.UpdateCheck import UpdateCheck, parseVersion 
 30   
31 -def manage_addZenossInfo(context, id='About', REQUEST=None):
32 """ 33 Provide an instance of ZenossInfo for the portal. 34 """ 35 about = ZenossInfo(id) 36 context._setObject(id, about) 37 if REQUEST is not None: 38 REQUEST.RESPONSE.redirect(context.absolute_url() +'/manage_main')
39
40 -class ZenossInfo(ZenModelItem, SimpleItem):
41 42 portal_type = meta_type = 'ZenossInfo' 43 44 security = ClassSecurityInfo() 45 46 _properties = ( 47 {'id':'id', 'type':'string'}, 48 {'id':'title', 'type':'string'}, 49 ) 50 51 factory_type_information = ( 52 { 53 'immediate_view' : 'zenossInfo', 54 'actions' : 55 ( 56 { 'id' : 'settings' 57 , 'name' : 'Settings' 58 , 'action' : '../dmd/editSettings' 59 , 'permissions' : ( "Manage DMD", ) 60 }, 61 { 'id' : 'manage' 62 , 'name' : 'Commands' 63 , 'action' : '../dmd/dataRootManage' 64 , 'permissions' : ('Manage DMD',) 65 }, 66 { 'id' : 'users' 67 , 'name' : 'Users' 68 , 'action' : '../dmd/ZenUsers/manageUserFolder' 69 , 'permissions' : ( 'Manage DMD', ) 70 }, 71 { 'id' : 'packs' 72 , 'name' : 'ZenPacks' 73 , 'action' : '../dmd/viewZenPacks' 74 , 'permissions' : ( "Manage DMD", ) 75 }, 76 { 'id' : 'menus' 77 , 'name' : 'Menus' 78 , 'action' : '../dmd/editMenus' 79 , 'permissions' : ( "Manage DMD", ) 80 }, 81 { 'id' : 'portlets' 82 , 'name' : 'Portlets' 83 , 'action' : '../dmd/editPortletPerms' 84 , 'permissions' : ( "Manage DMD", ) 85 }, 86 { 'id' : 'daemons' 87 , 'name' : 'Daemons' 88 , 'action' : 'zenossInfo' 89 , 'permissions' : ( "Manage DMD", ) 90 }, 91 { 'id' : 'versions' 92 , 'name' : 'Versions' 93 , 'action' : 'zenossVersions' 94 , 'permissions' : ( "Manage DMD", ) 95 }, 96 ) 97 }, 98 ) 99 100 101 security.declarePublic('getZenossVersion')
102 - def getZenossVersion(self):
103 from Products.ZenModel.ZVersion import VERSION 104 return Version.parse("Zenoss %s %s" % 105 (VERSION, self.getZenossRevision()))
106 107 108 security.declarePublic('getZenossVersionShort')
109 - def getZenossVersionShort(self):
110 return self.getZenossVersion().short()
111 112
113 - def getOSVersion(self):
114 """ 115 This function returns a Version-ready tuple. For use with the Version 116 object, use extended call syntax: 117 118 v = Version(*getOSVersion()) 119 v.full() 120 """ 121 if os.name == 'posix': 122 sysname, nodename, version, build, arch = os.uname() 123 name = "%s (%s)" % (sysname, arch) 124 major, minor, micro = getVersionTupleFromString(version) 125 comment = ' '.join(os.uname()) 126 elif os.name == 'nt': 127 from win32api import GetVersionEx 128 major, minor, micro, platformID, additional = GetVersionEx() 129 name = 'Windows %s (%s)' % (os.name.upper(), additional) 130 comment = '' 131 else: 132 raise VersionNotSupported 133 return Version(name, major, minor, micro, 0, comment)
134 135
136 - def getPythonVersion(self):
137 """ 138 This function returns a Version-ready tuple. For use with the Version 139 object, use extended call syntax: 140 141 v = Version(*getPythonVersion()) 142 v.full() 143 """ 144 name = 'Python' 145 major, minor, micro, releaselevel, serial = sys.version_info 146 return Version(name, major, minor, micro)
147 148
149 - def getMySQLVersion(self):
150 """ 151 This function returns a Version-ready tuple. For use with the Version 152 object, use extended call syntax: 153 154 v = Version(*getMySQLVersion()) 155 v.full() 156 157 The regex was tested against the following output strings: 158 mysql Ver 14.12 Distrib 5.0.24, for apple-darwin8.5.1 (i686) using readline 5.0 159 mysql Ver 12.22 Distrib 4.0.24, for pc-linux-gnu (i486) 160 mysql Ver 14.12 Distrib 5.0.24a, for Win32 (ia32) 161 """ 162 cmd = 'mysql --version' 163 fd = os.popen(cmd) 164 output = fd.readlines() 165 version = "0" 166 if fd.close() is None and len(output) > 0: 167 output = output[0].strip() 168 regexString = '(mysql).*Ver [0-9]{2}\.[0-9]{2} ' 169 regexString += 'Distrib ([0-9]+.[0-9]+.[0-9]+)(.*), for (.*\(.*\))' 170 regex = re.match(regexString, output) 171 if regex: 172 name, version, release, info = regex.groups() 173 comment = 'Ver %s' % version 174 # the name returned in the output is all lower case, so we'll make our own 175 name = 'MySQL' 176 major, minor, micro = getVersionTupleFromString(version) 177 return Version(name, major, minor, micro, 0, comment)
178 179
180 - def getRRDToolVersion(self):
181 """ 182 This function returns a Version-ready tuple. For use with the Version 183 object, use extended call syntax: 184 185 v = Version(*getRRDToolVersion()) 186 v.full() 187 """ 188 cmd = zenPath('bin', 'rrdtool') 189 if not os.path.exists(cmd): 190 cmd = 'rrdtool' 191 fd = os.popen(cmd) 192 output = fd.readlines()[0].strip() 193 fd.close() 194 name, version = output.split()[:2] 195 major, minor, micro = getVersionTupleFromString(version) 196 return Version(name, major, minor, micro)
197 198
199 - def getTwistedVersion(self):
200 """ 201 This function returns a Version-ready tuple. For use with the Version 202 object, use extended call syntax: 203 204 v = Version(*getTwistedVersion()) 205 v.full() 206 """ 207 from twisted._version import version as v 208 209 return Version('Twisted', v.major, v.minor, v.micro)
210 211
212 - def getPySNMPVersion(self):
213 """ 214 This function returns a Version-ready tuple. For use with the Version 215 object, use extended call syntax: 216 217 v = Version(*getpySNMPVersion()) 218 v.full() 219 """ 220 from pysnmp.version import getVersion 221 return Version('PySNMP', *getVersion())
222 223
224 - def getTwistedSNMPVersion(self):
225 """ 226 This function returns a Version-ready tuple. For use with the Version 227 object, use extended call syntax: 228 229 v = Version(*getTwistedSNMPVersion()) 230 v.full() 231 """ 232 from twistedsnmp.version import version 233 return Version('TwistedSNMP', *version)
234 235
236 - def getZopeVersion(self):
237 """ 238 This function returns a Version-ready tuple. For use with the Version 239 object, use extended call syntax: 240 241 v = Version(*getZopeVersion()) 242 v.full() 243 """ 244 from App import version_txt as version 245 246 name = 'Zope' 247 major, minor, micro, status, release = version.getZopeVersion() 248 return Version(name, major, minor, micro)
249 250
251 - def getZenossRevision(self):
252 try: 253 os.chdir(zenPath('Products')) 254 fd = os.popen("svn info 2>/dev/null | grep Revision | awk '{print $2}'") 255 return fd.readlines()[0].strip() 256 except: 257 return ''
258 259
260 - def getNetSnmpVersion(self):
261 from pynetsnmp.netsnmp import lib 262 return Version.parse('NetSnmp %s ' % lib.netsnmp_get_version())
263 264
265 - def getAllVersions(self):
266 """ 267 Return a list of version numbers for currently tracked component 268 software. 269 """ 270 versions = ( 271 {'header': 'Zenoss', 'data': self.getZenossVersion().full()}, 272 {'header': 'OS', 'data': self.getOSVersion().full()}, 273 {'header': 'Zope', 'data': self.getZopeVersion().full()}, 274 {'header': 'Python', 'data': self.getPythonVersion().full()}, 275 {'header': 'Database', 'data': self.getMySQLVersion().full()}, 276 {'header': 'RRD', 'data': self.getRRDToolVersion().full()}, 277 {'header': 'Twisted', 'data': self.getTwistedVersion().full()}, 278 {'header': 'SNMP', 'data': self.getPySNMPVersion().full()}, 279 {'header': 'Twisted SNMP', 'data': self.getTwistedSNMPVersion().full()}, 280 ) 281 try: 282 versions += ( 283 {'header': 'NetSnmp', 'data': self.getNetSnmpVersion().full() }, 284 ) 285 except: 286 pass 287 return versions
288 289 security.declareProtected('View','getAllVersions') 290 291
292 - def getAllUptimes(self):
293 """ 294 Return a list of daemons with their uptimes. 295 """ 296 app = self.getPhysicalRoot() 297 uptimes = [] 298 zope = { 299 'header': 'Zope', 300 'data': app.Control_Panel.process_time(), 301 } 302 uptimes.append(zope) 303 return uptimes
304 security.declareProtected('View','getAllUptimes') 305 306
307 - def getZenossDaemonStates(self):
308 """ 309 Return a data structures representing the states of the supported 310 Zenoss daemons. 311 """ 312 states = [] 313 activeButtons = {'button1': 'Restart', 'button2': 'Stop', 'button2state': True} 314 inactiveButtons = {'button1': 'Start', 'button2': 'Stop', 'button2state': False} 315 for daemon in self._getDaemonList(): 316 pid = self._getDaemonPID(daemon) 317 if pid: 318 buttons = activeButtons 319 msg = 'Up' 320 color = '#0F0' 321 else: 322 buttons = inactiveButtons 323 msg = 'Down' 324 color = '#F00' 325 states.append({ 326 'name': daemon, 327 'pid': pid, 328 'msg': msg, 329 'color': color, 330 'buttons': buttons}) 331 return states
332 333
334 - def _pidRunning(self, pid):
335 try: 336 os.kill(pid, 0) 337 return pid 338 except OSError, ex: 339 import errno 340 errnum, msg = ex.args 341 if errnum == errno.EPERM: 342 return pid
343 344
345 - def _getDaemonPID(self, name):
346 """ 347 For a given daemon name, return its PID from a .pid file. 348 """ 349 if name == 'zopectl': 350 name = 'Z2' 351 elif name == 'zeoctl': 352 name = 'ZEO' 353 else: 354 name = "%s.py" % name 355 pidFile = zenPath('var', '%s.pid' % name) 356 if os.path.exists(pidFile): 357 pid = open(pidFile).read() 358 try: 359 pid = int(pid) 360 except ValueError: 361 return None 362 return self._pidRunning(int(pid)) 363 else: 364 pid = None 365 return pid
366 367
368 - def _getDaemonList(self):
369 """ 370 Get the list of supported Zenoss daemons. 371 """ 372 masterScript = zenPath('bin', 'zenoss') 373 daemons = [] 374 for line in os.popen("%s list" % masterScript).readlines(): 375 daemons.append(line.strip()) 376 return daemons
377 378
379 - def getZenossDaemonConfigs(self):
380 """ 381 Return a data structures representing the config infor for the 382 supported Zenoss daemons. 383 """ 384 return [ dict(name=x) for x in self._getDaemonList() ]
385
386 - def _readLogFile(self, filename, maxBytes):
387 fh = open(filename) 388 try: 389 size = os.path.getsize(filename) 390 if size > maxBytes: 391 fh.seek(-maxBytes, 2) 392 # the first line could be a partial line, so skip it 393 fh.readline() 394 return fh.read() 395 finally: 396 fh.close()
397
398 - def getLogData(self, daemon, kb=500):
399 """ 400 Get the last kb kilobytes of a daemon's log file contents. 401 """ 402 maxBytes = 1024 * int(kb) 403 if daemon == 'zopectl': 404 daemon = 'event' 405 elif daemon == 'zeoctl': 406 daemon = 'zeo' 407 if daemon == 'zopectl': 408 daemon = 'event' 409 elif daemon == 'zeoctl': 410 daemon = 'zeo' 411 filename = zenPath('log', "%s.log" % daemon) 412 # if there is no data read, we don't want to return something that can 413 # be interptreted as "None", so we make the default a single white 414 # space 415 data = ' ' 416 try: 417 data = self._readLogFile(filename, maxBytes) or ' ' 418 except IOError: 419 data = 'Error reading log file' 420 return data
421 422
423 - def _getConfigFilename(self, daemon):
424 if daemon == 'zopectl': 425 daemon = 'zope' 426 elif daemon == 'zeoctl': 427 daemon = 'zeo' 428 return zenPath('etc', "%s.conf" % daemon)
429
430 - def _readConfigFile(self, filename):
431 fh = open(filename) 432 try: 433 return fh.read() 434 finally: 435 fh.close()
436
437 - def getConfigData(self, daemon):
438 """ 439 Return the contents of the daemon's config file. 440 """ 441 filename = self._getConfigFilename(daemon) 442 # if there is no data read, we don't want to return something that can 443 # be interptreted as "None", so we make the default a single white 444 # space 445 data = ' ' 446 try: 447 data = self._readConfigFile(filename) or ' ' 448 except IOError: 449 data = 'Unable to read config file' 450 return data
451 452
453 - def manage_saveConfigData(self, REQUEST):
454 """ 455 Save config data from REQUEST to the daemon's config file. 456 """ 457 daemon = REQUEST.form.get('daemon') 458 filename = self._getConfigFilename(daemon) 459 try: 460 fh = open(filename, 'w+') 461 data = REQUEST.form.get('data') 462 fh.write(data) 463 finally: 464 fh.close() 465 return self.callZenScreen(REQUEST, redirect=True)
466 467
468 - def manage_daemonAction(self, REQUEST):
469 """ 470 Start, stop, or restart Zenoss daemons from a web interface. 471 """ 472 legalValues = ['start', 'restart', 'stop'] 473 action = (REQUEST.form.get('action') or '').lower() 474 if action not in legalValues: 475 return self.callZenScreen(REQUEST) 476 daemon = zenPath('bin', REQUEST.form.get('daemon')) 477 # we actually want to block here, so that the page doesn't refresh 478 # until the action has completed 479 log.info("Processing a '%s' for '%s' through the web..." % (action, daemon)) 480 os.system("%s %s" % (daemon, action)) 481 if action == 'stop': time.sleep(2) 482 return self.callZenScreen(REQUEST)
483 security.declareProtected('Manage DMD','manage_daemonAction') 484 485
486 - def manage_checkVersion(self, optInOut=False, optInOutMetrics=False, REQUEST=None):
487 "Check for Zenoss updates on the Zenoss website" 488 self.dmd.versionCheckOptIn = optInOut 489 self.dmd.reportMetricsOptIn = optInOutMetrics 490 # There is a hidden field for manage_checkVersions in the form so that 491 # the javascript submit() calls will end up calling this method. 492 # That means that when user hits the Check Now button we will receive 493 # 2 values for that field. (button is that same field name.) 494 # We want to initiate only when the button is pressed. 495 if self.dmd.versionCheckOptIn \ 496 and REQUEST \ 497 and isinstance(REQUEST.form['manage_checkVersion'], list): 498 uc = UpdateCheck() 499 uc.check(self.dmd, self.dmd.ZenEventManager, manual=True) 500 return self.callZenScreen(REQUEST)
501 security.declareProtected('Manage DMD','manage_checkVersion') 502 503
504 - def lastVersionCheckedString(self):
505 if not self.dmd.lastVersionCheck: 506 return "Never" 507 return Time.LocalDateTime(self.dmd.lastVersionCheck)
508 509
510 - def versionBehind(self):
511 if self.dmd.availableVersion is None: 512 return False 513 if parseVersion(self.dmd.availableVersion) > self.getZenossVersion(): 514 return True 515 return False
516 517 518 InitializeClass(ZenossInfo) 519