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