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