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

# Source Code for Module Products.ZenModel.DataRoot

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 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  __doc__="""DataRoot
     15
     16  DataRoot is the object manager which contains all confmon
     17  data objects.  It can be used as a global acquisition
     18  name space.
     19  """
     20
     21  import re
     22  from zope.interface import implements
     23  from AccessControl import ClassSecurityInfo
     24  from AccessControl import getSecurityManager
     25  from OFS.OrderedFolder import OrderedFolder
     26  from Globals import DTMLFile
     27  from Globals import InitializeClass
     28  from Globals import DevelopmentMode
     29  from Products.ZenModel.SiteError import SiteError
     30  from Products.ZenModel.ZenModelBase import ZenModelBase
     31  from Products.ZenModel.ZenMenuable import ZenMenuable
     32  from Products.ZenRelations.RelSchema import *
     33  from Products.ZenUtils.IpUtil import IpAddressError
     34  from Products.ZenWidgets import messaging
     35  from Products.ZenUtils.Security import activateSessionBasedAuthentication, activateCookieBasedAuthentication
     36  from Commandable import Commandable
     37  import socket
     38  import os
     39  import sys
     40  from sets import Set
     41  import string
     42
     43  from Products.ZenUtils.Utils import zenPath, binPath
     44  from Products.ZenUtils.Utils import extractPostContent
     45  from Products.ZenUtils.jsonutils import json
     46
     47  from Products.ZenEvents.Exceptions import *
     48
     49  from ZenModelRM import ZenModelRM
     50  from ZenossSecurity import ZEN_COMMON, ZEN_MANAGE_DMD, ZEN_VIEW
     51  from interfaces import IDataRoot
     52
     53 -def manage_addDataRoot(context, id, title = None, REQUEST = None):
     54      """make a device"""
     55      dr = DataRoot(id, title)
     56      context._setObject(id, dr)
     57
     58      if REQUEST is not None:
     59          REQUEST['RESPONSE'].redirect(context.absolute_url() + '/manage_main')
     60
     61
     62  addDataRoot = DTMLFile('dtml/addDataRoot',globals())
     63
     64  __pychecker__='no-override'
     65
     66 -class DataRoot(ZenModelRM, OrderedFolder, Commandable, ZenMenuable):
     67      implements(IDataRoot)
     68
     69      meta_type = portal_type = 'DataRoot'
     70
     71      manage_main = OrderedFolder.manage_main
     72
     73      manage_options = OrderedFolder.manage_options
     74
     75      #setTitle = DTMLFile('dtml/setTitle',globals())
     76
     77      _rq = True
     78      uuid = None
     79      availableVersion = None
     80      lastVersionCheck = 0
     81      lastVersionCheckAttempt = 0
     82      versionCheckOptIn = True
     83      reportMetricsOptIn = True
     84      acceptedTerms = True
     85      instanceIdentifier = 'Zenoss'
     86      smtpHost = 'localhost'
     87      pageCommand = '$ZENHOME/bin/zensnpp localhost 444 $RECIPIENT'
     88      smtpPort = 25
     89      smtpUser = ''
     90      smtpPass = ''
     91      smtpUseTLS = 0
     92      emailFrom = ''
     93      iconMap = {}
     94      geomapapikey = ''
     95      geocache = ''
     96      version = ""
     97      enableLiveSearch = True
     98      # how we should store our user credentials
     99      AUTH_TYPE_SESSION = "session"
    100      AUTH_TYPE_COOKIE = "cookie"
    101      userAuthType = AUTH_TYPE_SESSION
    102      pauseHubNotifications = False
    103      # Setting applied to the User Interface to determine if we should load the
    104      # infrastructure trees all at once or incrementally as expanded
    105      incrementalTreeLoad = False
    106
    107      _properties=(
    108          {'id':'title', 'type': 'string', 'mode':'w'},
    109          {'id':'prodStateDashboardThresh','type':'int','mode':'w'},
    110          {'id':'prodStateConversions','type':'lines','mode':'w'},
    111          {'id':'priorityConversions','type':'lines','mode':'w'},
    112          {'id':'priorityDashboardThresh','type':'int','mode':'w'},
    113          {'id':'statusConversions','type':'lines','mode':'w'},
    114          {'id':'interfaceStateConversions','type':'lines','mode':'w'},
    115          {'id':'administrativeRoles','type':'lines','mode':'w'},
    116          {'id':'uuid', 'type': 'string', 'mode':'w'},
    117          {'id':'availableVersion', 'type': 'string', 'mode':'w'},
    118          {'id':'lastVersionCheck', 'type': 'long', 'mode':'w'},
    119          {'id':'lastVersionCheckAttempt', 'type': 'long', 'mode':'w'},
    120          {'id':'versionCheckOptIn', 'type': 'boolean', 'mode':'w'},
    121          {'id':'reportMetricsOptIn', 'type': 'boolean', 'mode':'w'},
    122          {'id':'instanceIdentifier', 'type': 'string', 'mode':'w'},
    123          {'id':'smtpHost', 'type': 'string', 'mode':'w'},
    124          {'id':'smtpPort', 'type': 'int', 'mode':'w'},
    125          {'id':'pageCommand', 'type': 'string', 'mode':'w'},
    126          {'id':'smtpUser', 'type': 'string', 'mode':'w'},
    127          {'id':'smtpPass', 'type': 'string', 'mode':'w'},
    128          {'id':'smtpUseTLS', 'type': 'int', 'mode':'w'},
    129          {'id':'emailFrom', 'type': 'string', 'mode':'w'},
    130          {'id':'geomapapikey', 'type': 'string', 'mode':'w'},
    131          {'id':'userAuthType', 'type': 'string', 'mode':'w'},
    132          {'id':'geocache', 'type': 'string', 'mode':'w'},
    133          {'id':'enableLiveSearch', 'type': 'boolean', 'mode':'w'},
    134          {'id':'incrementalTreeLoad', 'type': 'boolean', 'mode':'w'},
    135          {'id':'pauseHubNotifications', 'type': 'boolean', 'mode':'w'},
    136          )
    137
    138      _relations =  (
    139          ('userCommands', ToManyCont(ToOne, 'Products.ZenModel.UserCommand', 'commandable')),
    140          # packs is depracated.  Has been moved to dmd.ZenPackManager.packs
    141          # Should be removed post Zenoss 2.2
    142          # TODO
    143          ('packs',        ToManyCont(ToOne, 'Products.ZenModel.ZenPack',     'root')),
    144          ('zenMenus', ToManyCont(
    145              ToOne, 'Products.ZenModel.ZenMenu', 'menuable')),
    146         )
    147
    148      # Screen action bindings (and tab definitions)
    149      factory_type_information = (
    150          {
    151              'id'             : 'DataRoot',
    152              'meta_type'      : 'DataRoot',
    153              'description'    : """Arbitrary device grouping class""",
    154              'icon'           : 'DataRoot_icon.gif',
    155              'product'        : 'ZenModel',
    156              'factory'        : 'manage_addStatusMonitorconf',
    157              'immediate_view' : 'Dashboard',
    158              'actions'        :
    159              (
    160                  { 'id'            : 'settings'
    161                  , 'name'          : 'Settings'
    162                  , 'action'        : 'editSettings'
    163                  , 'permissions'   : ( "Manage DMD", )
    164                  },
    165                  { 'id'            : 'manage'
    166                  , 'name'          : 'Commands'
    167                  , 'action'        : 'dataRootManage'
    168                  , 'permissions'   : ('Manage DMD',)
    169                  },
    170                  { 'id'            : 'users'
    171                  , 'name'          : 'Users'
    172                  , 'action'        : 'ZenUsers/manageUserFolder'
    173                  , 'permissions'   : ( 'Manage DMD', )
    174                  },
    175                  { 'id'            : 'packs'
    176                  , 'name'          : 'ZenPacks'
    177                  , 'action'        : 'ZenPackManager/viewZenPacks'
    178                  , 'permissions'   : ( "Manage DMD", )
    179                  },
    180                  { 'id'            : 'jobs'
    181                  , 'name'          : 'Jobs'
    182                  , 'action'        : 'joblist'
    183                  , 'permissions'   : ( "Manage DMD", )
    184                  },
    185                  { 'id'            : 'portlets'
    186                  , 'name'          : 'Portlets'
    187                  , 'action'        : 'editPortletPerms'
    188                  , 'permissions'   : ( "Manage DMD", )
    189                  },
    190                  { 'id'            : 'daemons'
    191                  , 'name'          : 'Daemons'
    192                  , 'action'        : '../About/zenossInfo'
    193                  , 'permissions'   : ( "Manage DMD", )
    194                  },
    195                  { 'id'            : 'versions'
    196                  , 'name'          : 'Versions'
    197                  , 'action'        : '../About/zenossVersions'
    198                  , 'permissions'   : ( "Manage DMD", )
    199                  },
    200                  { 'id'            : 'backups'
    201                  , 'name'          : 'Backups'
    202                  , 'action'        : 'backupInfo'
    203                  , 'permissions'   : ( "Manage DMD", )
    204                  },
    205                  { 'id'            : 'eventConfig'
    206                  , 'name'          : 'Events'
    207                  , 'action'        : 'eventConfig'
    208                  , 'permissions'   : ( "Manage DMD", )
    209                  },
    210              )
    211            },
    212          )
    213
    214      security = ClassSecurityInfo()
    215
    216      # production state threshold at which devices show on dashboard
    217      prodStateDashboardThresh = 1000
    218
    219      # priority threshold at which devices show on dashboard
    220      priorityDashboardThresh = 2
    221
    222      prodStateConversions = [
    223                  'Production:1000',
    224                  'Pre-Production:500',
    225                  'Test:400',
    226                  'Maintenance:300',
    227                  'Decommissioned:-1',
    228                  ]
    229
    230      priorityConversions = [
    231                  'Highest:5',
    232                  'High:4',
    233                  'Normal:3',
    234                  'Low:2',
    235                  'Lowest:1',
    236                  'Trivial:0',
    237                  ]
    238
    239      statusConversions = [
    240                  'Up:0',
    241                  'None:-1',
    242                  'No DNS:-2',
    243                  ]
    244
    245      interfaceStateConversions = [
    246                  'up:1',
    247                  'down:2',
    248                  'testing:3',
    249                  'unknown:4',
    250                  'dormant:5',
    251                  'notPresent:6',
    252                  'lowerLayerDown:7',
    253                  ]
    254
    255      administrativeRoles = (
    256          "Administrator",
    257          "Analyst",
    258          "Engineer",
    259          "Tester",
    260      )
    261
    262      defaultDateRange = 129600
    263      performanceDateRanges = [
    264          ('Hourly',129600,),
    265          ('Daily',864000,),
    266          ('Weekly',3628800,),
    267          ('Monthly',41472000,),
    268          ('Yearly',62208000,)
    269      ]
    270
    271
    272      # when calculating the primary path this will be its root
    273      zPrimaryBasePath = ("", "zport")
    274
    275
    276 -    def __init__(self, id, title=None):
    277          ZenModelRM.__init__(self, id, title)
    278          from ZVersion import VERSION
    279          self.version = "Zenoss " + VERSION
    280
    281 -    def index_html(self):
    282          """
    283          Override to force redirection to quickstart.
    284          """
    285          if not self._rq:
    286              return self.unrestrictedTraverse('quickstart')()
    287          return self()
    288
    289 -    def getEventCount(self, **kwargs):
    290          """Return the current event list for this managed entity.
    291          """
    292          return self.ZenEventManager.getEventCount(**kwargs)
    293
    294      security.declareProtected(ZEN_COMMON, 'getEventClassNames')
    295 -    def getEventClassNames(self):
    296          """
    297          Get a list of all event class names within the permission scope.
    298          """
    299          return self.Events.getOrganizerNames()
    300
    301
    302 -    def getDmdRoots(self):
    303          return filter(lambda o: o.isInTree, self.objectValues())
    304
    305
    306 -    def exportXmlHook(self,ofile, ignorerels):
    307          map(lambda x: x.exportXml(ofile, ignorerels), self.getDmdRoots())
    308
    309
    310      security.declareProtected(ZEN_COMMON, 'getProdStateConversions')
    311 -    def getProdStateConversions(self):
    312          """getProdStateConversions() -> return a list of tuples
    313          for prodstat select edit box"""
    314          return self.getConversions(self.prodStateConversions)
    315
    316
    317      security.declareProtected(ZEN_COMMON, 'convertProdState')
    318 -    def convertProdState(self, prodState):
    319          '''convert a numeric production state to a
    320          textual representation using the prodStateConversions
    321          map'''
    322          return self.convertAttribute(prodState, self.prodStateConversions)
    323
    324
    325      security.declareProtected(ZEN_COMMON, 'getStatusConversions')
    326 -    def getStatusConversions(self):
    327          """get text strings for status field"""
    328          return self.getConversions(self.statusConversions)
    329
    330
    331      security.declareProtected(ZEN_COMMON, 'convertStatus')
    332 -    def convertStatus(self, status):
    333          """get text strings for status field"""
    334          return self.convertAttribute(status, self.statusConversions)
    335
    336      security.declareProtected(ZEN_COMMON, 'getPriorityConversions')
    337 -    def getPriorityConversions(self):
    338          return self.getConversions(self.priorityConversions)
    339
    340      security.declareProtected(ZEN_COMMON, 'convertPriority')
    341 -    def convertPriority(self, priority):
    342          return self.convertAttribute(priority, self.priorityConversions)
    343
    344      security.declareProtected(ZEN_COMMON, 'getInterfaceStateConversions')
    345 -    def getInterfaceStateConversions(self):
    346          """get text strings for interface status"""
    347          if hasattr(self, 'interfaceStateConversions'):
    348              return self.getConversions(self.interfaceStateConversions)
    349
    350
    351      security.declareProtected(ZEN_COMMON, 'convertAttribute')
    352 -    def convertAttribute(self, numbValue, conversions):
    353          '''convert a numeric production state to a
    354          textual representation using the prodStateConversions
    355          map'''
    356          numbValue = int(numbValue)
    357          for line in conversions:
    358              line = line.rstrip()
    359              (name, number) = line.split(':')
    360              if int(number) == numbValue:
    361                  return name
    362          return numbValue
    363
    364      security.declareProtected(ZEN_COMMON, 'convertStatusToDot')
    365 -    def convertStatusToDot(self, status):
    366          colors = ['green', 'yellow', 'orange', 'red']
    367          try:
    368              return colors[status]
    369          except IndexError:
    370              return 'grey'
    371
    372      security.declareProtected(ZEN_COMMON, 'getConversions')
    373 -    def getConversions(self, attribute):
    374          """get the text list of itmes that convert to ints"""
    375          convs = []
    376          for item in attribute:
    377              tup = item.split(':')
    378              try:
    379                  tup[1] = int(tup[1])
    380              except (IndexError, ValueError):
    381                  continue
    382              convs.append(tup)
    383          return convs
    384
    385      security.declarePublic('filterObjectsRegex')
    386 -    def filterObjectsRegex(self, filter, objects,
    387                              filteratt='id', negatefilter=0):
    388          """filter a list of objects based on a regex"""
    389          filter = re.compile(filter).search
    390          filteredObjects = []
    391          for obj in objects:
    392              value = getattr(obj, filteratt, None)
    393              if callable(value):
    394                  value = value()
    395              fvalue =  filter(value)
    396              if (fvalue and not negatefilter) or (not fvalue and negatefilter):
    397                  filteredObjects.append(obj)
    398          return filteredObjects
    399
    400
    401      security.declareProtected('View', 'myUserGroups')
    402 -    def myUserGroups(self):
    403          user = self.REQUEST.get('AUTHENTICATED_USER')
    404          if hasattr(user, 'getGroups'):
    405              return user.getGroups()
    406          else:
    407              return ()
    408
    409
    410      security.declareProtected('View', 'getAllUserGroups')
    411 -    def getAllUserGroups(self):
    412          return self.acl_users.getGroups()
    413
    414
    415      security.declareProtected(ZEN_VIEW, 'zenoss_error_message')
    416 -    def zenoss_error_message(self,error_type,error_value,
    417                              error_traceback,error_message):
    418          """Return an error page that is more friendly then the standard stack
    419          trace + feedback page for ConflictErrors and MySQL errors (we need to
    420          add out of disk space errors). If one of these is not found we return
    421          the old stacktrace page
    422          """
    423          from ZODB.POSException import ConflictError
    424          from Products.ZenEvents.Exceptions import MySQLConnectionError
    425          from _mysql_exceptions import MySQLError
    426          if isinstance(error_value, ConflictError):
    427              return self.zenoss_conflict_error_message()
    428          elif isinstance(error_value, MySQLConnectionError) \
    429                  or isinstance(error_value, MySQLError):
    430              return self.zenoss_mysql_error_message(error_value=error_value)
    431
    432          from traceback import format_exception
    433          error_formatted = ''.join(format_exception(error_type, error_value, error_traceback))
    434          return self.zenoss_feedback_error_message(error_type=error_type,
    435                                          error_value=error_value,
    436                                          error_traceback=error_traceback,
    437                                          error_formatted=error_formatted)
    438
    439
    440 -    def reportError(self):
    441          ''' send an email to the zenoss error email address
    442              then send user to a thankyou page or an email error page.
    443          '''
    444          if self.smtpHost: host = self.smtpHost
    445          else: host = None
    446          port = self.smtpPort and self.smtpPort or 25
    447          usetls = self.smtpUseTLS
    448          usr = self.smtpUser
    449          pwd = self.smtpPass
    450
    451          mailSent = SiteError.sendErrorEmail(
    452                      self.REQUEST.errorType,
    453                      self.REQUEST.errorValue,
    454                      self.REQUEST.errorTrace,
    455                      self.REQUEST.errorUrl,
    456                      self.About.getZenossRevision(),
    457                      self.About.getZenossVersionShort(),
    458                      self.REQUEST.contactName,
    459                      self.REQUEST.contactEmail,
    460                      self.REQUEST.comments,
    461                      host, port, usetls, usr, pwd)
    462          if not mailSent:
    463              body = SiteError.createReport(
    464                                  self.REQUEST.errorType,
    465                                  self.REQUEST.errorValue,
    466                                  self.REQUEST.errorTrace,
    467                                  self.REQUEST.errorUrl,
    468                                  self.About.getZenossRevision(),
    469                                  self.About.getZenossVersionShort(),
    470                                  True,
    471                                  self.REQUEST.contactName,
    472                                  self.REQUEST.contactEmail,
    473                                  self.REQUEST.comments)
    474              return self.errorEmailFailure(toAddress=SiteError.ERRORS_ADDRESS,
    475                                            body=body)
    476          return self.errorEmailThankYou()
    477
    478
    479      #security.declareProtected('View', 'writeExportRows')
    480 -    def writeExportRows(self, fieldsAndLabels, objects, out=None):
    481          '''Write out csv rows with the given objects and fields.
    482          If out is not None then call out.write() with the result and return None
    483          otherwise return the result.
    484          Each item in fieldsAndLabels is either a string representing a
    485           field/key/index (see getDataField) or it is a tuple of (field, label)
    486           where label is the string to be used in the first row as label
    487           for that column.
    488          Objects can be either dicts, lists/tuples or other objects. Field
    489           is interpreted as a key, index or attribute depending on what
    490           object is.
    491          Method names can be passed instead of attribute/key/indices as field.
    492           In this case the method is called and the return value is used in
    493           the export.
    494          '''
    495          import csv
    496          import StringIO
    497          if out:
    498              buffer = out
    499          else:
    500              buffer = StringIO.StringIO()
    501          fields = []
    502          labels = []
    503          if not fieldsAndLabels:
    504              fieldsAndLabels = []
    505          if not objects:
    506              objects = []
    507          for p in fieldsAndLabels:
    508              if isinstance(p, tuple):
    509                  fields.append(p[0])
    510                  labels.append(p[1])
    511              else:
    512                  fields.append(p)
    513                  labels.append(p)
    514          writer = csv.writer(buffer)
    515          writer.writerow(labels)
    516          def getDataField(thing, field):
    517              if isinstance(thing, dict):
    518                  value = thing.get(field, '')
    519              elif isinstance(thing, list) or isinstance(thing, tuple):
    520                  value = thing[int(field)]
    521              else:
    522                  value = getattr(thing, field, '')
    523              if isinstance(value, ZenModelBase):
    524                  value = value.id
    525              elif callable(value):
    526                  value = value()
    527              if value == None:
    528                  value = ''
    529              return str(value)
    530          for o in objects:
    531              writer.writerow([getDataField(o,f) for f in fields])
    532          if out:
    533              result = None
    534          else:
    535              result = buffer.getvalue()
    536          return result
    537
    538
    539 -    def getUserCommandTargets(self):
    540          ''' Called by Commandable.doCommand() to ascertain objects on which
    541          a UserCommand should be executed.
    542          '''
    543          raise NotImplemented
    544
    545
    546 -    def getUrlForUserCommands(self):
    547          return self.getPrimaryUrlPath() + '/dataRootManage'
    548
    549
    550 -    def getEmailFrom(self):
    551          ''' Return self.emailFrom or a suitable default
    552          '''
    553          return self.emailFrom or 'zenossuser_%s@%s' % (
    554              getSecurityManager().getUser().getId(), socket.getfqdn())
    555
    556
    557 -    def checkValidId(self, id, prep_id = False):
    558          """Checks a valid id
    559          """
    560          if len(id) > 128:
    561              return 'ZenPack names can not be longer than 128 characters.'
    562          allowed = Set(list(string.ascii_letters)
    563                          + list(string.digits)
    564                          + ['_'])
    565          attempted = Set(list(id))
    566          if not attempted.issubset(allowed):
    567              return 'Only letters, digits and underscores are allowed' + \
    568                      ' in ZenPack names.'
    569          return ZenModelRM.checkValidId(self, id, prep_id)
    570
    571
    572 -    def setGeocodeCache(self, REQUEST=None):
    573          """ Store a JSON representation of
    574              the Google Maps geocode cache
    575          """
    576          cache = extractPostContent(REQUEST)
    577          try: cache = cache.decode('utf-8')
    578          except: pass
    579          self.geocache = cache
    580          return True
    581
    582 -    def clearGeocodeCache(self, REQUEST=None):
    583          """
    584          Clear the Google Maps cache.
    585          """
    586          self.geocache = ''
    587
    588      security.declareProtected(ZEN_COMMON, 'getGeoCache')
    589      @json
    590 -    def getGeoCache(self):
    591          cachestr = self.geocache
    592          for char in ('\\r', '\\n'):
    593              cachestr = cachestr.replace(char, ' ')
    594          return cachestr
    595
    596 -    def goToStatusPage(self, objid, REQUEST=None):
    597          """ Find a device or network and redirect
    598              to its status page.
    599          """
    600          import urllib
    601          objid = urllib.unquote(objid)
    602          try:
    603              devid = objid
    604              if not devid.endswith('*'): devid += '*'
    605              obj = self.Devices.findDevice(devid)
    606          except:
    607              obj=None
    608          if not obj:
    609              try:
    610                  obj = self.Networks.getNet(objid)
    611              except IpAddressError:
    612                  return None
    613          if not obj: return None
    614          if REQUEST is not None:
    615              REQUEST['RESPONSE'].redirect(obj.getPrimaryUrlPath())
    616
    617
    618 -    def getXMLEdges(self, objid, depth=1, filter="/"):
    619          """ Get the XML representation of network nodes
    620              and edges using the obj with objid as a root
    621          """
    622          import urllib
    623          objid = urllib.unquote(objid)
    624          try:
    625              devid = objid
    626              if not devid.endswith('*'): devid += '*'
    627              obj = self.Devices.findDevice(devid)
    628          except: obj=None
    629          if not obj:
    630              obj = self.Networks.getNet(objid)
    631          if not obj:
    632              return '<graph><Start name="%s"/></graph>' % objid
    633          return obj.getXMLEdges(int(depth), filter,
    634                                 start=(obj.id,obj.getPrimaryUrlPath()))
    635
    636
    637      security.declareProtected(ZEN_MANAGE_DMD, 'getBackupFilesInfo')
    638 -    def getBackupFilesInfo(self):
    639          """
    640          Retrieve a list of dictionaries describing the files in
    641          $ZENHOME/backups.
    642          """
    643          import stat
    644          import os
    645          import datetime
    646          import operator
    647
    648          def FmtFileSize(size):
    649              for power, units in ((3, 'GB'), (2, 'MB'), (1, 'KB')):
    650                  if size > pow(1024, power):
    651                      fmt = '%.2f %s' % ((size * 1.0)/pow(1024, power), units)
    652                      break
    653              else:
    654                  fmt = '%s bytes' % size
    655              return fmt
    656
    657          backupsDir = zenPath('backups')
    658          fileInfo = []
    659          if os.path.isdir(backupsDir):
    660              for dirPath, dirNames, fileNames in os.walk(backupsDir):
    661                  dirNames[:] = []
    662                  for fileName in fileNames:
    663                      filePath = os.path.join(backupsDir, fileName)
    664                      info = os.stat(filePath)
    665                      fileInfo.append({
    666                          'fileName': fileName,
    667                          'size': info[stat.ST_SIZE],
    668                          'sizeFormatted': FmtFileSize(info[stat.ST_SIZE]),
    669                          'modDate': info[stat.ST_MTIME],
    670                          'modDateFormatted': datetime.datetime.fromtimestamp(
    671                                  info[stat.ST_MTIME]).strftime(
    672                                  '%c'),
    673                          })
    674          fileInfo.sort(key=operator.itemgetter('modDate'))
    675          return fileInfo
    676
    677
    678      security.declareProtected(ZEN_MANAGE_DMD, 'manage_createBackup')
    679 -    def manage_createBackup(self, includeEvents=None, includeMysqlLogin=None,
    680              timeout=120, REQUEST=None, writeMethod=None):
    681          """
    682          Create a new backup file using zenbackup and the options specified
    683          in the request.
    684
    685          This method makes use of the fact that DataRoot is a Commandable
    686          in order to use Commandable.write
    687          """
    688          import popen2
    689          import fcntl
    690          import time
    691          import select
    692
    693          def write(s):
    694              if writeMethod:
    695                  writeMethod(s)
    696              elif REQUEST:
    697                  self.write(REQUEST.RESPONSE, s)
    698
    699          footer = None
    700          if REQUEST and not writeMethod:
    701              header, footer = self.commandOutputTemplate().split('OUTPUT_TOKEN')
    702              REQUEST.RESPONSE.write(str(header))
    703          write('')
    704          try:
    705              cmd = binPath('zenbackup') + ' -v10'
    706              if not includeEvents:
    707                  cmd += ' --no-eventsdb'
    708              if not includeMysqlLogin:
    709                  cmd += ' --no-save-mysql-access'
    710              try:
    711                  timeout = int(timeout)
    712              except ValueError:
    713                  timeout = 120
    714              timeout = max(timeout, 1)
    715              child = popen2.Popen4(cmd)
    716              flags = fcntl.fcntl(child.fromchild, fcntl.F_GETFL)
    717              fcntl.fcntl(child.fromchild, fcntl.F_SETFL, flags | os.O_NDELAY)
    718              endtime = time.time() + timeout
    719              write('%s' % cmd)
    720              write('')
    721              pollPeriod = 1
    722              firstPass = True
    723              while time.time() < endtime and (firstPass or child.poll() == -1):
    724                  firstPass = False
    725                  r, w, e = select.select([child.fromchild], [], [], pollPeriod)
    726                  if r:
    727                      t = child.fromchild.read()
    728                      # We are sometimes getting to this point without any data
    729                      # from child.fromchild.  I don't think that should happen
    730                      # but the conditional below seems to be necessary.
    731                      if t:
    732                          write(t)
    733
    734              if child.poll() == -1:
    735                  write('Backup timed out after %s seconds.' % timeout)
    736                  import signal
    737                  os.kill(child.pid, signal.SIGKILL)
    738
    739              write('DONE')
    740          except:
    741              write('Exception while performing backup.')
    742              write('type: %s  value: %s' % tuple(sys.exc_info()[:2]))
    743          write('')
    744          if REQUEST and footer:
    745              REQUEST.RESPONSE.write(footer)
    746
    747
    748      security.declareProtected(ZEN_MANAGE_DMD, 'manage_deleteBackups')
    749 -    def manage_deleteBackups(self, fileNames=(), REQUEST=None):
    750          """
    751          Delete the specified files from $ZENHOME/backups
    752          """
    753          backupsDir = zenPath('backups')
    754          count = 0
    755          if os.path.isdir(backupsDir):
    756              for dirPath, dirNames, dirFileNames in os.walk(backupsDir):
    757                  dirNames[:] = []
    758                  for fileName in fileNames:
    759                      if fileName in dirFileNames:
    760                          res = os.remove(os.path.join(dirPath, fileName))
    761                          if res == 0:
    762                              count += 1
    763              if REQUEST:
    764                  messaging.IMessageSender(self).sendToBrowser(
    765                      'Backups Deleted',
    766                      '%s backup files have been deleted.' % count
    767                  )
    768          else:
    769              if REQUEST:
    770                  messaging.IMessageSender(self).sendToBrowser(
    771                      'Backup Directory Missing',
    772                      'Unable to find $ZENHOME/backups.',
    773                      priority=messaging.WARNING
    774                  )
    775          if REQUEST:
    776              return self.callZenScreen(REQUEST)
    777
    778
    779 -    def getProductName(self):
    780          """
    781          Return a string that represents the Zenoss product that is installed.
    782          Currently this is something like 'core' or 'enterprise'.  This is
    783          used in the version check code to retrieve the available version
    784          for the correct product.
    785          """
    786          return getattr(self, 'productName', 'core')
    787
    788
    789 -    def error_handler(self, error=None):
    790          """
    791          Returns pretty messages when errors are raised in templates.
    792
    793          Access this method from a template like so:
    794              <div tal:content="..."
    795                   ...
    796                   tal:on-error="structure python:here.dmd.error_handler(error)">
    797
    798          @param error: A TALES.ErrorInfo instance with attributes type, value
    799                        and traceback.
    800          @return: HTML fragment with an error message
    801          """
    802          if error.type==MySQLConnectionError:
    803              msg = "Unable to connect to the MySQL server."
    804
    805          elif error.type in [ pythonThresholdException, rpnThresholdException ]:
    806              msg= error.value
    807
    808          else:
    809              raise
    810
    811          return '<b class="errormsg">%s</b>' % msg
    812
    813      @json
    814 -    def isDebugMode(self):
    815          """
    816          Whether we're in debug mode, so that javascript will behave accordingly
    817          """
    818          return DevelopmentMode
    819
    820 -    def versionId(self):
    821          """
    822          Get a string representative of the code version, to override JS
    823          caching.
    824          """
    825          return self.About.getZenossVersion().full().replace(
    826              'Zenoss','').replace(' ','').replace('.','')
    827
    828      security.declareProtected('Manage DMD', 'zmanage_editProperties')
    829 -    def zmanage_editProperties(self, REQUEST=None, redirect=False):
    830          """Handle our authentication mechanism
    831          """
    832          if REQUEST:
    833              app = self.unrestrictedTraverse('/')
    834              if REQUEST.get('userAuthType') == self.AUTH_TYPE_SESSION:
    835                  activateSessionBasedAuthentication(self.zport)
    836                  activateSessionBasedAuthentication(app) # for admin
    837              elif REQUEST.get('userAuthType') == self.AUTH_TYPE_COOKIE:
    838                  activateCookieBasedAuthentication(self.zport)
    839                  activateCookieBasedAuthentication(app) # for admin
    840          return super(DataRoot, self).zmanage_editProperties(REQUEST, redirect)
    841
    842
    843  InitializeClass(DataRoot)
    844