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

# Source Code for Module Products.ZenModel.DataRoot

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