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

Source Code for Module Products.ZenModel.ZenossInfo

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2007, 2009 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 os.path 
 16  import sys 
 17  import re 
 18  from urllib import unquote 
 19  from subprocess import Popen, PIPE, call 
 20  from xml.dom.minidom import parse 
 21  import shutil 
 22  import traceback 
 23  import logging 
 24  log = logging.getLogger("zen.ZenossInfo") 
 25   
 26  from Globals import InitializeClass 
 27  from OFS.SimpleItem import SimpleItem 
 28  from AccessControl import ClassSecurityInfo 
 29   
 30  from Products.ZenModel.ZenModelItem import ZenModelItem 
 31  from Products.ZenUtils import Time 
 32  from Products.ZenUtils.Version import * 
 33  from Products.ZenUtils.Utils import zenPath, binPath 
 34  from Products.ZenWidgets import messaging 
 35   
 36  from Products.ZenEvents.UpdateCheck import UpdateCheck, parseVersion 
 37   
38 -def manage_addZenossInfo(context, id='About', REQUEST=None):
39 """ 40 Provide an instance of ZenossInfo for the portal. 41 """ 42 about = ZenossInfo(id) 43 context._setObject(id, about) 44 if REQUEST is not None: 45 REQUEST.RESPONSE.redirect(context.absolute_url() +'/manage_main')
46
47 -class ZenossInfo(ZenModelItem, SimpleItem):
48 49 portal_type = meta_type = 'ZenossInfo' 50 51 security = ClassSecurityInfo() 52 53 _properties = ( 54 {'id':'id', 'type':'string'}, 55 {'id':'title', 'type':'string'}, 56 ) 57 58 factory_type_information = ( 59 { 60 'immediate_view' : 'zenossInfo', 61 'actions' : 62 ( 63 { 'id' : 'settings' 64 , 'name' : 'Settings' 65 , 'action' : '../dmd/editSettings' 66 , 'permissions' : ( "Manage DMD", ) 67 }, 68 { 'id' : 'manage' 69 , 'name' : 'Commands' 70 , 'action' : '../dmd/dataRootManage' 71 , 'permissions' : ('Manage DMD',) 72 }, 73 { 'id' : 'users' 74 , 'name' : 'Users' 75 , 'action' : '../dmd/ZenUsers/manageUserFolder' 76 , 'permissions' : ( 'Manage DMD', ) 77 }, 78 { 'id' : 'packs' 79 , 'name' : 'ZenPacks' 80 , 'action' : '../dmd/ZenPackManager/viewZenPacks' 81 , 'permissions' : ( "Manage DMD", ) 82 }, 83 { 'id' : 'jobs' 84 , 'name' : 'Jobs' 85 , 'action' : '../dmd/joblist' 86 , 'permissions' : ( "Manage DMD", ) 87 }, 88 { 'id' : 'menus' 89 , 'name' : 'Menus' 90 , 'action' : '../dmd/editMenus' 91 , 'permissions' : ( "Manage DMD", ) 92 }, 93 { 'id' : 'portlets' 94 , 'name' : 'Portlets' 95 , 'action' : '../dmd/editPortletPerms' 96 , 'permissions' : ( "Manage DMD", ) 97 }, 98 { 'id' : 'daemons' 99 , 'name' : 'Daemons' 100 , 'action' : 'zenossInfo' 101 , 'permissions' : ( "Manage DMD", ) 102 }, 103 { 'id' : 'versions' 104 , 'name' : 'Versions' 105 , 'action' : 'zenossVersions' 106 , 'permissions' : ( "Manage DMD", ) 107 }, 108 { 'id' : 'backups' 109 , 'name' : 'Backups' 110 , 'action' : '../dmd/backupInfo' 111 , 'permissions' : ( "Manage DMD", ) 112 }, 113 ) 114 }, 115 ) 116 117
118 - def titleOrId(self):
119 return self.title or self.id
120 121 122 security.declarePublic('getZenossVersion')
123 - def getZenossVersion(self):
124 from Products.ZenModel.ZVersion import VERSION 125 return Version.parse("Zenoss %s %s" % 126 (VERSION, self.getZenossRevision()))
127 128 129 security.declarePublic('getZenossVersionShort')
130 - def getZenossVersionShort(self):
131 return self.getZenossVersion().short()
132 133
134 - def getOSVersion(self):
135 """ 136 This function returns a Version-ready tuple. For use with the Version 137 object, use extended call syntax: 138 139 v = Version(*getOSVersion()) 140 v.full() 141 """ 142 if os.name == 'posix': 143 sysname, nodename, version, build, arch = os.uname() 144 name = "%s (%s)" % (sysname, arch) 145 major, minor, micro = getVersionTupleFromString(version) 146 comment = ' '.join(os.uname()) 147 elif os.name == 'nt': 148 from win32api import GetVersionEx 149 major, minor, micro, platformID, additional = GetVersionEx() 150 name = 'Windows %s (%s)' % (os.name.upper(), additional) 151 comment = '' 152 else: 153 raise VersionNotSupported 154 return Version(name, major, minor, micro, 0, comment)
155 156
157 - def getPythonVersion(self):
158 """ 159 This function returns a Version-ready tuple. For use with the Version 160 object, use extended call syntax: 161 162 v = Version(*getPythonVersion()) 163 v.full() 164 """ 165 name = 'Python' 166 major, minor, micro, releaselevel, serial = sys.version_info 167 return Version(name, major, minor, micro)
168 169
170 - def getMySQLVersion(self):
171 """ 172 This function returns a Version-ready tuple. For use with the Version 173 object, use extended call syntax: 174 175 v = Version(*getMySQLVersion()) 176 v.full() 177 178 The regex was tested against the following output strings: 179 mysql Ver 14.12 Distrib 5.0.24, for apple-darwin8.5.1 (i686) using readline 5.0 180 mysql Ver 12.22 Distrib 4.0.24, for pc-linux-gnu (i486) 181 mysql Ver 14.12 Distrib 5.0.24a, for Win32 (ia32) 182 /usr/local/zenoss/mysql/bin/mysql.bin Ver 14.12 Distrib 5.0.45, for unknown-linux-gnu (x86_64) using readline 5.0 183 """ 184 cmd = 'mysql --version' 185 fd = os.popen(cmd) 186 output = fd.readlines() 187 version = "0" 188 if fd.close() is None and len(output) > 0: 189 output = output[0].strip() 190 regexString = '.*(mysql).*Ver [0-9]{2}\.[0-9]{2} ' 191 regexString += 'Distrib ([0-9]+.[0-9]+.[0-9]+)(.*), for (.*\(.*\))' 192 regex = re.match(regexString, output) 193 if regex: 194 name, version, release, info = regex.groups() 195 comment = 'Ver %s' % version 196 # the name returned in the output is all lower case, so we'll make our own 197 name = 'MySQL' 198 major, minor, micro = getVersionTupleFromString(version) 199 return Version(name, major, minor, micro, 0, comment)
200 201
202 - def getRRDToolVersion(self):
203 """ 204 This function returns a Version-ready tuple. For use with the Version 205 object, use extended call syntax: 206 207 v = Version(*getRRDToolVersion()) 208 v.full() 209 """ 210 cmd = binPath('rrdtool') 211 if not os.path.exists(cmd): 212 cmd = 'rrdtool' 213 fd = os.popen(cmd) 214 output = fd.readlines()[0].strip() 215 fd.close() 216 name, version = output.split()[:2] 217 major, minor, micro = getVersionTupleFromString(version) 218 return Version(name, major, minor, micro)
219 220
221 - def getTwistedVersion(self):
222 """ 223 This function returns a Version-ready tuple. For use with the Version 224 object, use extended call syntax: 225 226 v = Version(*getTwistedVersion()) 227 v.full() 228 """ 229 from twisted._version import version as v 230 231 return Version('Twisted', v.major, v.minor, v.micro)
232 233
234 - def getZopeVersion(self):
235 """ 236 This function returns a Version-ready tuple. For use with the Version 237 object, use extended call syntax: 238 239 v = Version(*getZopeVersion()) 240 v.full() 241 """ 242 from App import version_txt as version 243 244 name = 'Zope' 245 major, minor, micro, status, release = version.getZopeVersion() 246 return Version(name, major, minor, micro)
247 248
249 - def getZenossRevision(self):
250 """ 251 Determine the Zenoss version number 252 253 @return: version number or '' 254 @rtype: string 255 """ 256 try: 257 products = zenPath("Products") 258 cmd = "svn info '%s' 2>/dev/null | awk '/Revision/ {print $2}'" % products 259 fd = os.popen(cmd) 260 return fd.readlines()[0].strip() 261 except: 262 return ''
263 264
265 - def getNetSnmpVersion(self):
266 from pynetsnmp.netsnmp import lib 267 return Version.parse('NetSnmp %s ' % lib.netsnmp_get_version())
268 269
270 - def getPyNetSnmpVersion(self):
271 from pynetsnmp.version import VERSION 272 return Version.parse('PyNetSnmp %s ' % VERSION)
273 274
275 - def getWmiVersion(self):
276 from pysamba.version import VERSION 277 return Version.parse('Wmi %s ' % VERSION)
278 279
280 - def getAllVersions(self):
281 """ 282 Return a list of version numbers for currently tracked component 283 software. 284 """ 285 versions = ( 286 {'header': 'Zenoss', 'data': self.getZenossVersion().full(), 287 'href': "http://www.zenoss.com" }, 288 {'header': 'OS', 'data': self.getOSVersion().full(), 289 'href': "http://www.tldp.org" }, 290 {'header': 'Zope', 'data': self.getZopeVersion().full(), 291 'href': "http://www.zope.org" }, 292 {'header': 'Python', 'data': self.getPythonVersion().full(), 293 'href': "http://www.python.org" }, 294 {'header': 'Database', 'data': self.getMySQLVersion().full(), 295 'href': "http://www.mysql.com" }, 296 {'header': 'RRD', 'data': self.getRRDToolVersion().full(), 297 'href': "http://oss.oetiker.ch/rrdtool" }, 298 {'header': 'Twisted', 'data': self.getTwistedVersion().full(), 299 'href': "http:///twistedmatrix.com/trac" }, 300 ) 301 try: 302 versions += ( 303 {'header': 'NetSnmp', 'data': self.getNetSnmpVersion().full(), 304 'href': "http://net-snmp.sourceforge.net" }, 305 ) 306 except: 307 pass 308 try: 309 versions += ( 310 {'header': 'PyNetSnmp', 'data': self.getPyNetSnmpVersion().full(), 311 'href': "http://www.zenoss.com" }, 312 ) 313 except: 314 pass 315 try: 316 versions += ( 317 {'header': 'WMI', 'data': self.getWmiVersion().full(), 318 'href': "http://www.zenoss.com" }, 319 ) 320 except: 321 pass 322 return versions
323 324 security.declareProtected('View','getAllVersions') 325 326
327 - def getAllUptimes(self):
328 """ 329 Return a list of daemons with their uptimes. 330 """ 331 app = self.getPhysicalRoot() 332 uptimes = [] 333 zope = { 334 'header': 'Zope', 335 'data': app.Control_Panel.process_time(), 336 } 337 uptimes.append(zope) 338 return uptimes
339 security.declareProtected('View','getAllUptimes') 340 341 342 343 daemon_tooltips= { 344 "zeoctl": "Zope Enterprise Objects server (shares database between Zope instances)", 345 "zopectl": "The Zope open source web application server", 346 "zenhub": "Broker between the data layer and the collection daemons", 347 "zenping": "ICMP ping status monitoring", 348 "zensyslog": "Collection of and classification of syslog events", 349 "zenstatus": "Active TCP connection testing of remote daemons", 350 "zenactions": "Alerts (SMTP, SNPP and Maintenance Windows)", 351 "zentrap": "Receives SNMP traps and turns them into events", 352 "zenmodeler": "Configuration collection and configuration", 353 "zenperfsnmp": "High performance asynchronous SNMP performance collection", 354 "zencommand": "Runs plug-ins on the local box or on remote boxes through SSH", 355 "zenprocess": "Process monitoring using SNMP host resources MIB", 356 "zenwin": "Windows Service Monitoring (WMI)", 357 "zeneventlog": "Collect (WMI) event log events (aka NT Eventlog)", 358 "zenwinmodeler": "MS Windows configuration collection and configuration", 359 "zendisc": "Discover the network topology to find active IPs and devices", 360 "zenperfxmlrpc": "XML RPC data collection", 361 } 362 363
364 - def getZenossDaemonStates(self):
365 """ 366 Return a data structures representing the states of the supported 367 Zenoss daemons. 368 """ 369 states = [] 370 activeButtons = {'button1': 'Restart', 'button2': 'Stop', 'button2state': True} 371 inactiveButtons = {'button1': 'Start', 'button2': 'Stop', 'button2state': False} 372 for daemon in self._getDaemonList(): 373 pid = self._getDaemonPID(daemon) 374 if pid: 375 buttons = activeButtons 376 msg = 'Up' 377 color = '#0F0' 378 else: 379 buttons = inactiveButtons 380 msg = 'Down' 381 color = '#F00' 382 383 if daemon in self.daemon_tooltips: 384 tooltip= self.daemon_tooltips[ daemon ] 385 else: 386 tooltip= '' 387 388 states.append({ 389 'name': daemon, 390 'pid': pid, 391 'msg': msg, 392 'tooltip': tooltip, 393 'color': color, 394 'buttons': buttons}) 395 396 return states
397 398
399 - def _pidRunning(self, pid):
400 try: 401 os.kill(pid, 0) 402 return pid 403 except OSError, ex: 404 import errno 405 errnum, msg = ex.args 406 if errnum == errno.EPERM: 407 return pid
408 409
410 - def _getDaemonPID(self, name):
411 """ 412 For a given daemon name, return its PID from a .pid file. 413 """ 414 if name == 'zopectl': 415 name = 'Z2' 416 elif name == 'zeoctl': 417 name = 'ZEO' 418 elif '_' in name: 419 collector, daemon = name.split('_', 1) 420 name = '%s-%s' % (daemon, collector) 421 else: 422 name = "%s-localhost" % name 423 pidFile = zenPath('var', '%s.pid' % name) 424 if os.path.exists(pidFile): 425 pid = open(pidFile).read() 426 try: 427 pid = int(pid) 428 except ValueError: 429 return None 430 return self._pidRunning(int(pid)) 431 else: 432 pid = None 433 return pid
434 435
436 - def _getDaemonList(self):
437 """ 438 Get the list of supported Zenoss daemons. 439 """ 440 masterScript = binPath('zenoss') 441 daemons = [] 442 for line in os.popen("%s list" % masterScript).readlines(): 443 daemons.append(line.strip()) 444 return daemons
445 446
447 - def getZenossDaemonConfigs(self):
448 """ 449 Return a data structures representing the config infor for the 450 supported Zenoss daemons. 451 """ 452 return [ dict(name=x) for x in self._getDaemonList() ]
453
454 - def _readLogFile(self, filename, maxBytes):
455 fh = open(filename) 456 try: 457 size = os.path.getsize(filename) 458 if size > maxBytes: 459 fh.seek(-maxBytes, 2) 460 # the first line could be a partial line, so skip it 461 fh.readline() 462 return fh.read() 463 finally: 464 fh.close()
465
466 - def _getLogPath(self, daemon):
467 """ 468 Returns the path the log file for the daemon this is monkey-patched 469 in the distributed collector zenpack to support the localhost 470 subdirectory. 471 """ 472 return zenPath('log', "%s.log" % daemon)
473
474 - def getLogData(self, daemon, kb=500):
475 """ 476 Get the last kb kilobytes of a daemon's log file contents. 477 """ 478 maxBytes = 1024 * int(kb) 479 if daemon == 'zopectl': 480 daemon = 'event' 481 elif daemon == 'zeoctl': 482 daemon = 'zeo' 483 if daemon == 'zopectl': 484 daemon = 'event' 485 elif daemon == 'zeoctl': 486 daemon = 'zeo' 487 filename = self._getLogPath(daemon) 488 # if there is no data read, we don't want to return something that can 489 # be interptreted as "None", so we make the default a single white 490 # space 491 data = ' ' 492 try: 493 data = self._readLogFile(filename, maxBytes) or ' ' 494 except IOError: 495 data = 'Error reading log file' 496 return data
497 498
499 - def _getConfigFilename(self, daemon):
500 if daemon == 'zopectl': 501 daemon = 'zope' 502 elif daemon == 'zeoctl': 503 daemon = 'zeo' 504 return zenPath('etc', "%s.conf" % daemon)
505
506 - def _readConfigFile(self, filename):
507 fh = open(filename) 508 try: 509 return fh.read() 510 finally: 511 fh.close()
512
513 - def getConfigData(self, daemon):
514 """ 515 Return the contents of the daemon's config file. 516 """ 517 filename = self._getConfigFilename(daemon) 518 # if there is no data read, we don't want to return something that can 519 # be interptreted as "None", so we make the default a single white 520 # space 521 data = ' ' 522 try: 523 data = self._readConfigFile(filename) or ' ' 524 except IOError: 525 data = 'Unable to read config file' 526 return data
527 528
529 - def manage_saveConfigData(self, REQUEST):
530 """ 531 Save config data from REQUEST to the daemon's config file. 532 """ 533 daemon = REQUEST.form.get('daemon') 534 filename = self._getConfigFilename(daemon) 535 try: 536 fh = open(filename, 'w+') 537 data = REQUEST.form.get('data') 538 fh.write(data) 539 finally: 540 fh.close() 541 return self.callZenScreen(REQUEST, redirect=True)
542
543 - def parseconfig(self, filename=""):
544 """ 545 From the given configuration file construct a configuration object 546 """ 547 configs = {} 548 549 config_file = open(filename) 550 try: 551 for line in config_file: 552 line = line.strip() 553 if line.startswith('#'): continue 554 if line == '': continue 555 556 try: 557 key, value = line.split(None, 1) 558 except ValueError: 559 # Ignore errors 560 continue 561 configs[key] = value 562 finally: 563 config_file.close() 564 565 return configs
566
567 - def show_daemon_xml_configs(self, daemon, REQUEST=None ):
568 """ 569 Display the daemon configuration options in an XML format. 570 Merges the defaults with options in the config file. 571 """ 572 # Sanity check 573 if not daemon or daemon == '': 574 messaging.IMessageSender(self).sendToBrowser( 575 'Internal Error', 576 'Called without a daemon name', 577 priority=messaging.WARNING 578 ) 579 return [] 580 581 if daemon in [ 'zeoctl', 'zopectl' ]: 582 return [] 583 584 xml_default_name = zenPath( "etc", daemon + ".xml" ) 585 try: 586 # Always recreate the defaults file in order to avoid caching issues 587 log.debug("Creating XML config file for %s" % daemon) 588 make_xml = ' '.join([daemon, "genxmlconfigs", ">", xml_default_name]) 589 proc = Popen(make_xml, shell=True, stdout=PIPE, stderr=PIPE) 590 output, errors = proc.communicate() 591 proc.wait() 592 if proc.returncode != 0: 593 log.error(errors) 594 messaging.IMessageSender(self).sendToBrowser( 595 'Internal Error', errors, 596 priority=messaging.CRITICAL 597 ) 598 return [["Output", output, errors, make_xml, "string"]] 599 except Exception, ex: 600 msg = "Unable to execute '%s'\noutput='%s'\nerrors='%s'\nex=%s" % ( 601 make_xml, output, errors, ex) 602 log.error(msg) 603 messaging.IMessageSender(self).sendToBrowser( 604 'Internal Error', msg, 605 priority=messaging.CRITICAL 606 ) 607 return [["Error in command", output, errors, make_xml, "string"]] 608 609 try: 610 xml_defaults = parse( xml_default_name ) 611 except: 612 info = traceback.format_exc() 613 msg = "Unable to parse XML file %s because %s" % ( 614 xml_default_name, info) 615 log.error(msg) 616 messaging.IMessageSender(self).sendToBrowser( 617 'Internal Error', msg, 618 priority=messaging.CRITICAL 619 ) 620 return [["Error parsing XML file", xml_default_name, "XML", info, "string"]] 621 622 configfile = self._getConfigFilename(daemon) 623 try: 624 # Grab the current configs 625 current_configs = self.parseconfig( configfile ) 626 except: 627 info = traceback.format_exc() 628 msg = "Unable to obtain current configuration from %s because %s" % ( 629 configfile, info) 630 log.error(msg) 631 messaging.IMessageSender(self).sendToBrowser( 632 'Internal Error', msg, 633 priority=messaging.CRITICAL 634 ) 635 return [["Configuration file issue", configfile, configfile, info, "string"]] 636 637 all_options = {} 638 ignore_options = ['configfile', 'cycle', 'daemon', 'weblog'] 639 try: 640 for option in xml_defaults.getElementsByTagName('option'): 641 id = option.attributes['id'].nodeValue 642 if id in ignore_options: 643 continue 644 try: 645 help = unquote(option.attributes['help'].nodeValue) 646 except: 647 help = '' 648 649 try: 650 default = unquote(option.attributes['default'].nodeValue) 651 except: 652 default = '' 653 if default == '[]': # Attempt at a list argument -- ignore 654 continue 655 656 all_options[id] = [ 657 id, 658 current_configs.get(id, default), 659 default, 660 help, 661 option.attributes['type'].nodeValue, 662 ] 663 664 except: 665 info = traceback.format_exc() 666 msg = "Unable to merge XML defaults with config file" \ 667 " %s because %s" % (configfile, info) 668 log.error(msg) 669 messaging.IMessageSender(self).sendToBrowser( 670 'Internal Error', msg, 671 priority=messaging.CRITICAL 672 ) 673 return [["XML file issue", daemon, xml_default_name, info, "string"]] 674 675 return [all_options[name] for name in sorted(all_options.keys())]
676 677
678 - def save_daemon_configs( self, REQUEST=None, **kwargs ):
679 """ 680 Save the updated daemon configuration to disk. 681 """ 682 if not REQUEST: 683 return 684 elif not hasattr(REQUEST, 'form'): 685 return 686 687 # Sanity check 688 formdata = REQUEST.form 689 ignore_names = ['save_daemon_configs', 'zenScreenName', 'daemon_name'] 690 691 daemon = formdata.get('daemon_name', '') 692 if not daemon or daemon in ['zeoctl', 'zopectl']: 693 return 694 for item in ignore_names: 695 del formdata[item] 696 697 if not formdata: # If empty, don't overwrite -- assume an error 698 msg = "Received empty form data for %s config -- ignoring" % ( 699 daemon) 700 log.error(msg) 701 messaging.IMessageSender(self).sendToBrowser( 702 'Internal Error', msg, 703 priority=messaging.CRITICAL 704 ) 705 return 706 707 configfile = self._getConfigFilename(daemon) 708 config_file_pre = configfile + ".pre" 709 try: 710 config = open( config_file_pre, 'w' ) 711 config.write("# Config file written out from GUI\n") 712 for key, value in formdata.items(): 713 if value == '': 714 continue 715 if key == value: # Yes, this is strange 716 value = True 717 config.write('%s %s\n' % (key, value)) 718 config.close() 719 except Exception, ex: 720 msg = "Couldn't write to %s because %s" % (config_file_pre, ex) 721 log.error(msg) 722 messaging.IMessageSender(self).sendToBrowser( 723 'Internal Error', msg, 724 priority=messaging.CRITICAL 725 ) 726 config.close() 727 try: 728 os.unlink(config_file_pre) 729 except: 730 pass 731 return 732 733 # If we got here things succeeded 734 config_file_save = configfile + ".save" 735 try: 736 shutil.copy(configfile, config_file_save) 737 except: 738 log.error("Unable to make backup copy of %s" % configfile) 739 # Don't bother telling the user 740 try: 741 shutil.move(config_file_pre, configfile) 742 except: 743 msg = "Unable to save contents to %s" % configfile 744 log.error(msg) 745 messaging.IMessageSender(self).sendToBrowser( 746 'Internal Error', msg, 747 priority=messaging.CRITICAL 748 )
749 750
751 - def manage_daemonAction(self, REQUEST):
752 """ 753 Start, stop, or restart Zenoss daemons from a web interface. 754 """ 755 legalValues = ['start', 'restart', 'stop'] 756 action = (REQUEST.form.get('action') or '').lower() 757 if action not in legalValues: 758 return self.callZenScreen(REQUEST) 759 daemonName = REQUEST.form.get('daemon') 760 self.doDaemonAction(daemonName, action) 761 return self.callZenScreen(REQUEST)
762 security.declareProtected('Manage DMD','manage_daemonAction') 763 764
765 - def doDaemonAction(self, daemonName, action):
766 """ 767 Do the given action (start, stop, restart) or the given daemon. 768 Block until the action is completed. 769 No return value. 770 """ 771 import time 772 import subprocess 773 daemonPath = binPath(daemonName) 774 if not os.path.isfile(daemonPath): 775 return 776 log.info('Telling %s to %s' % (daemonName, action)) 777 proc = subprocess.Popen([daemonPath, action], stdout=subprocess.PIPE, 778 stderr=subprocess.STDOUT) 779 output, _ = proc.communicate() 780 code = proc.wait() 781 if code: 782 log.info('Error from %s: %s (%s)' % (daemonName, output, code)) 783 if action in ('stop', 'restart'): 784 time.sleep(2)
785 786
787 - def manage_checkVersion(self, optInOut=False, optInOutMetrics=False, REQUEST=None):
788 "Check for Zenoss updates on the Zenoss website" 789 self.dmd.versionCheckOptIn = optInOut 790 self.dmd.reportMetricsOptIn = optInOutMetrics 791 # There is a hidden field for manage_checkVersions in the form so that 792 # the javascript submit() calls will end up calling this method. 793 # That means that when user hits the Check Now button we will receive 794 # 2 values for that field. (button is that same field name.) 795 # We want to initiate only when the button is pressed. 796 if self.dmd.versionCheckOptIn \ 797 and REQUEST \ 798 and isinstance(REQUEST.form['manage_checkVersion'], list): 799 uc = UpdateCheck() 800 uc.check(self.dmd, self.dmd.ZenEventManager, manual=True) 801 return self.callZenScreen(REQUEST)
802 security.declareProtected('Manage DMD','manage_checkVersion') 803 804
805 - def lastVersionCheckedString(self):
806 if not self.dmd.lastVersionCheck: 807 return "Never" 808 return Time.LocalDateTime(self.dmd.lastVersionCheck)
809 810
811 - def versionBehind(self):
812 if self.dmd.availableVersion is None: 813 return False 814 if parseVersion(self.dmd.availableVersion) > self.getZenossVersion(): 815 return True 816 return False
817 818 819 InitializeClass(ZenossInfo) 820