1
2
3
4
5
6
7
8
9
10
11
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 subprocess
25 import tarfile
26 import ConfigParser
27
28 import Globals
29 from ZCmdBase import ZCmdBase
30 from Products.ZenUtils.Utils import zenPath, binPath
31
32 from ZenBackupBase import *
33
34
36
38 ZenBackupBase.__init__(self)
39 self.log = logging.getLogger("zenrestore")
40 logging.basicConfig()
41 if self.options.verbose:
42 self.log.setLevel(10)
43 else:
44 self.log.setLevel(40)
45
47 """basic options setup sub classes can add more options here"""
48 ZenBackupBase.buildOptions(self)
49
50 self.parser.add_option('--file',
51 dest="file",
52 default=None,
53 help='File from which to restore.')
54 self.parser.add_option('--dir',
55 dest="dir",
56 default=None,
57 help='Path to an untarred backup file'
58 ' from which to restore.')
59 self.parser.add_option('--no-zodb',
60 dest="noZODB",
61 default=False,
62 action='store_true',
63 help='Do not restore the ZODB.')
64 self.parser.add_option('--no-eventsdb',
65 dest="noEventsDb",
66 default=False,
67 action='store_true',
68 help='Do not restore the events database.')
69 self.parser.add_option('--no-perfdata',
70 dest="noPerfdata",
71 default=False,
72 action='store_true',
73 help='Do not restore performance data.')
74 self.parser.add_option('--deletePreviousPerfData',
75 dest="deletePreviousPerfData",
76 default=False,
77 action='store_true',
78 help='Delete ALL existing performance data before restoring?')
79 self.parser.add_option('--zenpacks',
80 dest='zenpacks',
81 default=False,
82 action='store_true',
83 help=('Experimental: Restore any ZenPacks in '
84 'the backup. Some ZenPacks may not work '
85 'properly. Reinstall ZenPacks if possible'))
86
101
103 """
104 Find path to sql file in backup, trying gzipped versions
105 Returns path to real file, or none if nothing found
106 """
107 pathFile = os.path.join(self.tempDir, filename)
108 for path in (pathFile, pathFile + '.gz'):
109 if os.path.isfile(path):
110 return path
111 return None
112
114 """
115 Create MySQL database if it doesn't exist.
116 """
117 mysql_cmd = ['mysql', '-u%s' % user]
118 mysql_cmd.extend(passwd)
119 if host and host != 'localhost':
120 mysql_cmd.extend(['--host', host])
121 if self.options.compressTransport:
122 mysql_cmd.append('--compress')
123 if port and str(port) != '3306':
124 mysql_cmd.extend(['--port', str(port)])
125
126 mysql_cmd = subprocess.list2cmdline(mysql_cmd)
127
128 cmd = 'echo "create database if not exists %s" | %s' % (db, mysql_cmd)
129 os.system(cmd)
130
131 if sqlFile.endswith('.gz'):
132 cmd = 'gzip -dc %s | %s %s' % (
133 os.path.join(self.tempDir, sqlFile), mysql_cmd, db
134 )
135 else:
136 cmd = '%s %s < %s' % (
137 mysql_cmd, db, os.path.join(self.tempDir, sqlFile)
138 )
139
140 os.system(cmd)
141
142
144 '''
145 Restore ZEP DB and indexes
146 '''
147 zepSql = self.getSqlFile('zep.sql')
148 if not zepSql:
149 self.msg('This backup does not contain a ZEP database backup.')
150 return
151
152 self.msg('Restoring ZEP database.')
153 self.restoreMySqlDb(self.options.zepdbhost, self.options.zepdbport,
154 self.options.zepdbname, self.options.zepdbuser,
155 self.getPassArg('zepdbpass'), zepSql)
156 self.msg('ZEP database restored.')
157
158
159 index_dir = zenPath('var', 'zeneventserver', 'index')
160 if os.path.isdir(index_dir):
161 import shutil
162 self.msg('Removing existing ZEP indexes.')
163 shutil.rmtree(index_dir)
164
165 index_tar = os.path.join(self.tempDir, 'zep.tar')
166 if os.path.isfile(index_tar):
167 self.msg('Restoring ZEP indexes.')
168 zepTar = tarfile.open(os.path.join(self.tempDir, 'zep.tar'))
169 zepTar.extractall(zenPath('var'))
170 self.msg('ZEP indexes restored.')
171 else:
172 self.msg('ZEP indexes not found in backup file - will be recreated from database.')
173
174
175
177 repozoDir = os.path.join(self.tempDir, 'repozo')
178 return os.path.isdir(repozoDir)
179
182
185
197
199 zodbSql = self.getSqlFile('zodb.sql')
200 if not zodbSql:
201 self.msg('This archive does not contain a ZODB backup.')
202 return
203 self.msg('Restoring ZODB database.')
204 self.restoreMySqlDb(self.options.host, self.options.port,
205 self.options.mysqldb, self.options.mysqluser,
206 self.getPassArg('mysqlpasswd'), zodbSql)
207
209 repozoDir = os.path.join(self.tempDir, 'repozo')
210 tempFilePath = os.path.join(self.tempDir, 'Data.fs')
211 tempZodbConvert = os.path.join(self.tempDir, 'convert.conf')
212
213 self.msg('Restoring ZEO backup into MySQL.')
214
215
216 cmd = []
217 cmd.append(binPath('repozo'))
218 cmd.append('--recover')
219 cmd.append('--repository')
220 cmd.append(repozoDir)
221 cmd.append('--output')
222 cmd.append(tempFilePath)
223
224 rc = subprocess.call(cmd, stdout=PIPE, stderr=PIPE)
225 if rc:
226 return -1
227
228
229 zodbconvert_conf = open(tempZodbConvert, 'w')
230 zodbconvert_conf.write('<filestorage source>\n')
231 zodbconvert_conf.write(' path %s\n' % tempFilePath)
232 zodbconvert_conf.write('</filestorage>\n\n')
233
234 zodbconvert_conf.write('<relstorage destination>\n')
235 zodbconvert_conf.write(' <mysql>\n')
236 zodbconvert_conf.write(' host %s\n' % self.options.host)
237 zodbconvert_conf.write(' port %s\n' % self.options.port)
238 zodbconvert_conf.write(' db %s\n' % self.options.mysqldb)
239 zodbconvert_conf.write(' user %s\n' % self.options.mysqluser)
240 zodbconvert_conf.write(' passwd %s\n' % self.options.mysqlpasswd or '')
241 zodbconvert_conf.write(' </mysql>\n')
242 zodbconvert_conf.write('</relstorage>\n')
243 zodbconvert_conf.close()
244
245 rc = subprocess.call(['zodbconvert', '--clear', tempZodbConvert],
246 stdout=PIPE, stderr=PIPE)
247 if rc:
248 return -1
249
250
252 self.msg('Restoring config files.')
253 cmd = 'cp -p %s %s' % (os.path.join(zenPath('etc'), 'global.conf'), self.tempDir)
254 if os.system(cmd): return -1
255 cmd = 'rm -rf %s' % zenPath('etc')
256 if os.system(cmd): return -1
257 cmd = 'tar Cxf %s %s' % (
258 zenPath(),
259 os.path.join(self.tempDir, 'etc.tar')
260 )
261 if os.system(cmd): return -1
262 if not os.path.exists(os.path.join(zenPath('etc'), 'global.conf')):
263 self.msg('Restoring default global.conf')
264 cmd = 'mv %s %s' % (os.path.join(self.tempDir, 'global.conf'), zenPath('etc'))
265 if os.system(cmd): return -1
266
268 self.msg('Restoring ZenPacks.')
269 cmd = 'rm -rf %s' % zenPath('ZenPacks')
270 if os.system(cmd): return -1
271 cmd = 'tar Cxf %s %s' % (
272 zenPath(),
273 os.path.join(self.tempDir, 'ZenPacks.tar'))
274 if os.system(cmd): return -1
275
276
277 tempBin = os.path.join(self.tempDir, 'bin.tar')
278 if os.path.isfile(tempBin):
279 self.msg('Restoring bin dir.')
280
281 cmd = ['tar', 'Cxfk', zenPath(),
282 os.path.join(self.tempDir, 'bin.tar')]
283 self.runCommand(cmd)
284
286 dmd = ZCmdBase(noopts=True).dmd
287 self.log.info("Restoring ZenPack contents.")
288 for pack in dmd.ZenPackManager.packs():
289 pack.restore(self.tempDir, self.log)
290 self.log.info("ZenPack contents restored.")
291
293 cmd = 'rm -rf %s' % os.path.join(zenPath(), 'perf')
294 if os.system(cmd): return -1
295 self.msg('Restoring performance data.')
296 cmd = 'tar Cxf %s %s' % (
297 zenPath(),
298 os.path.join(self.tempDir, 'perf.tar'))
299 if os.system(cmd): return -1
300
302 """
303 Restore from a previous backup
304 """
305 if self.options.file and self.options.dir:
306 sys.stderr.write('You cannot specify both --file and --dir.\n')
307 sys.exit(-1)
308 elif not self.options.file and not self.options.dir:
309 sys.stderr.write('You must specify either --file or --dir.\n')
310 sys.exit(-1)
311
312
313
314 rootTempDir = ''
315 if self.options.file:
316 if not os.path.isfile(self.options.file):
317 sys.stderr.write('The specified backup file does not exist: %s\n' %
318 self.options.file)
319 sys.exit(-1)
320
321 self.msg('Unpacking backup file')
322 rootTempDir = self.getTempDir()
323 cmd = 'tar xzfC %s %s' % (self.options.file, rootTempDir)
324 if os.system(cmd): return -1
325 self.tempDir = os.path.join(rootTempDir, BACKUP_DIR)
326 else:
327 self.msg('Using %s as source of restore' % self.options.dir)
328 if not os.path.isdir(self.options.dir):
329 sys.stderr.write('The specified backup directory does not exist:'
330 ' %s\n' % self.options.dir)
331 sys.exit(-1)
332 self.tempDir = self.options.dir
333
334
335 self.getSettings()
336
337 if self.options.zenpacks and not self.hasZODBBackup():
338 sys.stderr.write('Archive does not contain ZODB backup; cannot'
339 'restore ZenPacks')
340 sys.exit(-1)
341
342
343 if self.hasZODBBackup():
344 self.restoreZODB()
345 else:
346 self.msg('Archive does not contain a ZODB backup')
347
348
349 self.restoreEtcFiles()
350
351
352 if self.options.zenpacks:
353 tempPacks = os.path.join(self.tempDir, 'ZenPacks.tar')
354 if os.path.isfile(tempPacks):
355 self.restoreZenPacks()
356 self.restoreZenPackContents()
357 else:
358 self.msg('Backup contains no ZenPacks.')
359
360
361 tempPerf = os.path.join(self.tempDir, 'perf.tar')
362 if os.path.isfile(tempPerf):
363 self.restorePerfData()
364 else:
365 self.msg('Backup contains no perf data.')
366
367
368 if self.options.noEventsDb:
369 self.msg('Skipping the events database.')
370 else:
371 self.restoreZEP()
372
373
374 if self.options.file:
375 self.msg('Cleaning up temporary files.')
376 cmd = 'rm -r %s' % rootTempDir
377 if os.system(cmd): return -1
378
379 self.msg('Restore complete.')
380 return 0
381
383 self.msg('Flushing memcached cache.')
384 import memcache
385 mc = memcache.Client(cacheservers, debug=0)
386 mc.flush_all()
387 mc.disconnect_all()
388 self.msg('Completed flushing memcached cache.')
389
390 if __name__ == '__main__':
391 zb = ZenRestore()
392 if zb.doRestore():
393 sys.exit(-1)
394