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