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