Package Products :: Package ZenUtils :: Module ZenRestore |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenUtils.ZenRestore

      1  #! /usr/bin/env python
      2  ###########################################################################
      3  #
      4  # This program is part of Zenoss Core, an open source monitoring platform.
      5  # Copyright (C) 2007, 2009 Zenoss Inc.
      6  #
      7  # This program is free software; you can redistribute it and/or modify it
      8  # under the terms of the GNU General Public License version 2 as published by
      9  # the Free Software Foundation.
     10  #
     11  # For complete information please visit: http://www.zenoss.com/oss/
     12  #
     13  ###########################################################################
     14
     15  __doc__='''zenrestore
     16
     17  Restores a zenoss backup created by zenbackup.
     18  '''
     19
     20  import logging
     21  import sys
     22  import os
     23  import os.path
     24  import ConfigParser
     25
     26  import Globals
     27  from Products.ZenUtils.Utils import zenPath, binPath
     28
     29  from ZenBackupBase import *
     30
     31
     32 -class ZenRestore(ZenBackupBase):
     33
     34 -    def __init__(self):
     35          ZenBackupBase.__init__(self)
     36          self.log = logging.getLogger("zenrestore")
     37          logging.basicConfig()
     38          if self.options.verbose:
     39              self.log.setLevel(10)
     40          else:
     41              self.log.setLevel(40)
     42
     43 -    def buildOptions(self):
     44          """basic options setup sub classes can add more options here"""
     45          ZenBackupBase.buildOptions(self)
     46
     47          self.parser.add_option('--dbname',
     48                                 dest='dbname',
     49                                 default=None,
     50                                 help='MySQL events database name.  Defaults'
     51                                      ' to value saved with backup or "events".')
     52          self.parser.add_option('--dbuser',
     53                                 dest='dbuser',
     54                                 default=None,
     55                                 help='MySQL username.  Defaults'
     56                                      ' to value saved with backup or "zenoss".')
     57          self.parser.add_option('--dbpass',
     58                                 dest='dbpass',
     59                                 default=None,
     60                                 help='MySQL password. Defaults'
     61                                      ' to value saved with backup.')
     62          self.parser.add_option('--dbhost',
     63                                 dest='dbhost',
     64                                 default='localhost',
     65                                 help='MySQL server host.'
     66                                  ' Defaults to value saved with backup.'),
     67          self.parser.add_option('--dbport',
     68                                 dest='dbport',
     69                                 default='3306',
     70                                 help='MySQL server port number.'
     71                                  ' Defaults to value saved with backup.'),
     72          self.parser.add_option('--file',
     73                                 dest="file",
     74                                 default=None,
     75                                 help='File from which to restore.')
     76          self.parser.add_option('--dir',
     77                                 dest="dir",
     78                                 default=None,
     79                                 help='Path to an untarred backup file'
     80                                          ' from which to restore.')
     81          self.parser.add_option('--no-zodb',
     82                                 dest="noZODB",
     83                                 default=False,
     84                                 action='store_true',
     85                                 help='Do not restore the ZODB.')
     86          self.parser.add_option('--no-eventsdb',
     87                                 dest="noEventsDb",
     88                                 default=False,
     89                                 action='store_true',
     90                                 help='Do not restore the events database.')
     91          self.parser.add_option('--no-perfdata',
     92                                 dest="noPerfdata",
     93                                 default=False,
     94                                 action='store_true',
     95                                 help='Do not restore performance data.')
     96          self.parser.add_option('--deletePreviousPerfData',
     97                                 dest="deletePreviousPerfData",
     98                                 default=False,
     99                                 action='store_true',
    100                                 help='Delete ALL existing performance data before restoring?')
    101          self.parser.add_option('--zenpacks',
    102                                 dest='zenpacks',
    103                                 default=False,
    104                                 action='store_true',
    105                                 help=('Experimental: Restore any ZenPacks in '
    106                                       'the backup. Some ZenPacks may not work '
    107                                       'properly. Reinstall ZenPacks if possible'))
    108
    109 -    def getSettings(self, tempDir):
    110          ''' Retrieve some options from settings file
    111          '''
    112          try:
    113              f = open(os.path.join(tempDir, CONFIG_FILE), 'r')
    114          except:
    115              return
    116          try:
    117              config = ConfigParser.SafeConfigParser()
    118              config.readfp(f)
    119          finally:
    120              f.close()
    121          for key, default, zemAttr in CONFIG_FIELDS:
    122              if getattr(self.options, key, None) == None:
    123                  if config.has_option(CONFIG_SECTION, key):
    124                      setattr(self.options, key, config.get(CONFIG_SECTION, key))
    125                  else:
    126                      setattr(self.options, key, default)
    127
    128
    129 -    def createMySqlDb(self):
    130          '''
    131          Create the events schema in MySQL if it does not exist.
    132          Return true if the command was able to complete, otherwise
    133          (eg permissions or login error), return false.
    134          '''
    135          # The original dbname is stored in the backup within dbname.txt
    136          # For now we ignore it and use the database specified on the command
    137          # line.
    138          sql = 'create database if not exists %s' % self.options.dbname
    139          if self.runMysqlCmd(sql):
    140              self.msg('MySQL events database creation was successful.')
    141              return True
    142          else:
    143              self.msg('MySQL events database creation faild and returned %d' % result[2])
    144              return False
    145
    146 -    def restoreEventsDatabase(self, tempDir):
    147          """
    148          Restore the MySQL events database
    149          """
    150          eventsSql = os.path.join(tempDir, 'events.sql')
    151          if not os.path.isfile(eventsSql):
    152              self.msg('This backup does not contain an events database backup.')
    153              return
    154
    155          # Create the MySQL db if it doesn't exist already
    156          self.msg('Creating the events database (if necessary).')
    157          if not self.createMySqlDb():
    158              return
    159
    160          # Restore the mysql tables
    161          self.msg('Restoring events database.')
    162          sql = 'source %s' % eventsSql
    163          if self.runMysqlCmd(sql, switchDB=True):
    164              self.msg('Successfully loaded events into MySQL database.')
    165          else:
    166              self.msg('FAILED to load events into MySQL events database.')
    167
    168 -    def doRestore(self):
    169          '''
    170          Restore from a previous backup
    171          '''
    172          def hasZeoBackup(tempDir):
    173              repozoDir = os.path.join(tempDir, 'repozo')
    174              return os.path.isdir(repozoDir)
    175
    176          if self.options.file and self.options.dir:
    177              sys.stderr.write('You cannot specify both --file and --dir.\n')
    178              sys.exit(-1)
    179          elif not self.options.file and not self.options.dir:
    180              sys.stderr.write('You must specify either --file or --dir.\n')
    181              sys.exit(-1)
    182
    183
    184          # Maybe check to see if zeo is up and tell user to quit zenoss first
    185
    186          rootTempDir = ''
    187          if self.options.file:
    188              if not os.path.isfile(self.options.file):
    189                  sys.stderr.write('The specified backup file does not exist: %s\n' %
    190                        self.options.file)
    191                  sys.exit(-1)
    192              # Create temp dir and untar backup into it
    193              self.msg('Unpacking backup file')
    194              rootTempDir = self.getTempDir()
    195              cmd = 'tar xzfC %s %s' % (self.options.file, rootTempDir)
    196              if os.system(cmd): return -1
    197              tempDir = os.path.join(rootTempDir, BACKUP_DIR)
    198          else:
    199              self.msg('Using %s as source of restore' % self.options.dir)
    200              if not os.path.isdir(self.options.dir):
    201                  sys.stderr.write('The specified backup directory does not exist:'
    202                                  ' %s\n' % self.options.dir)
    203                  sys.exit(-1)
    204              tempDir = self.options.dir
    205
    206          if self.options.zenpacks and not hasZeoBackup(tempDir):
    207              sys.stderr.write('archive does not contain ZEO database backup, '
    208                               'cannot restore ZenPacks.\n')
    209              sys.exit(-1)
    210
    211          # Maybe use values from backup file as defaults for self.options.
    212          self.getSettings(tempDir)
    213          if not self.options.dbname:
    214              self.options.dbname = 'events'
    215          if not self.options.dbuser:
    216              self.options.dbuser = 'zenoss'
    217
    218          # If there is not a Data.fs then create an empty one
    219          # Maybe should read file location/name from zeo.conf
    220          # but we're going to assume the standard location for now.
    221          if not os.path.isfile(zenPath('var', 'Data.fs')):
    222              self.msg('There does not appear to be a zeo database.'
    223                          ' Starting zeo to create one.')
    224              os.system(binPath('zeoctl') + 'start > /dev/null')
    225              os.system(binPath('zeoctl') + 'stop > /dev/null')
    226
    227          # Restore zopedb
    228          if self.options.noZODB:
    229              self.msg('Skipping the ZEO database..')
    230          elif hasZeoBackup(tempDir):
    231              repozoDir = os.path.join(tempDir, 'repozo')
    232              self.msg('Restoring the ZEO database.')
    233              cmd ='%s %s --recover --repository %s --output %s' % (
    234                          binPath('python'),
    235                          binPath('repozo.py'),
    236                          repozoDir,
    237                          zenPath('var', 'Data.fs'))
    238              if os.system(cmd): return -1
    239          else:
    240              self.msg('Archive does not contain ZEO database backup')
    241
    242          # Copy etc files
    243          self.msg('Restoring config files.')
    244          cmd = 'rm -rf %s' % zenPath('etc')
    245          if os.system(cmd): return -1
    246          cmd = 'tar Cxf %s %s' % (
    247                          zenPath(),
    248                          os.path.join(tempDir, 'etc.tar'))
    249          if os.system(cmd): return -1
    250
    251          # Copy ZenPack files if requested
    252          # check for existence of ZEO backup
    253          if not self.options.noZODB and \
    254             self.options.zenpacks and \
    255             hasZeoBackup(tempDir):
    256              tempPacks = os.path.join(tempDir, 'ZenPacks.tar')
    257              if os.path.isfile(tempPacks):
    258                  self.msg('Restoring ZenPacks.')
    259                  cmd = 'rm -rf %s' % zenPath('ZenPacks')
    260                  if os.system(cmd): return -1
    261                  cmd = 'tar Cxf %s %s' % (
    262                                  zenPath(),
    263                                  os.path.join(tempDir, 'ZenPacks.tar'))
    264                  if os.system(cmd): return -1
    265                  # restore bin dir when restoring zenpacks
    266                  #make sure bin dir is in tar
    267                  tempBin = os.path.join(tempDir, 'bin.tar')
    268                  if os.path.isfile(tempBin):
    269                      self.msg('Restoring bin dir.')
    270                      #k option prevents overwriting existing bin files
    271                      cmd = ['tar', 'Cxfk', zenPath(),
    272                             os.path.join(tempDir, 'bin.tar')]
    273                      self.runCommand(cmd)
    274              else:
    275                  self.msg('Backup contains no ZenPacks.')
    276
    277          # Restore perf files
    278          if self.options.noPerfdata:
    279              self.msg('Skipping performance data.')
    280          else:
    281              tempPerf = os.path.join(tempDir, 'perf.tar')
    282              if os.path.isfile(tempPerf):
    283                  if self.options.deletePreviousPerfData:
    284                      self.msg('Removing previous performance data...')
    285                      cmd = 'rm -rf %s' % os.path.join(zenPath(), 'perf')
    286                      if os.system(cmd):
    287                          return -1
    288
    289                  self.msg('Restoring performance data.')
    290                  cmd = 'tar Cxf %s %s' % (
    291                                  zenPath(),
    292                                  os.path.join(tempDir, 'perf.tar'))
    293                  if os.system(cmd): return -1
    294              else:
    295                  self.msg('Backup contains no perf data.')
    296
    297          if self.options.noEventsDb:
    298              self.msg('Skipping the events database.')
    299          else:
    300              self.restoreEventsDatabase(tempDir)
    301
    302          # clean up
    303          if self.options.file:
    304              self.msg('Cleaning up temporary files.')
    305              cmd = 'rm -r %s' % rootTempDir
    306              if os.system(cmd): return -1
    307
    308          self.msg('Restore complete.')
    309          return 0
    310
    311
    312  if __name__ == '__main__':
    313      zb = ZenRestore()
    314      if zb.doRestore():
    315          sys.exit(-1)
    316