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