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