def do_dbrecover(self, arg, opts=None): '''Load a db backup to file or stdout.''' if not opts.id and not opts.job: sys.stderr.write("Please specify either -j or -i options.\n") else: if opts.id: r = Report(None) dmp = r.get_catalogs(catalog=opts.id)[0] else: r = Report(opts.job) dmp = r.get_catalogs(entries=1, types=('MysqlDump', 'gzMysqlDump'))[0] cs = getattr(self.cf, dmp.job.name) if dmp.enc and not cs.encryption: raise RecoverException( 'Archive %s is encrypted, you must ' 'provide proper credentials in order to recover, or ' 'recover manually.' % dmp.id) dmp_file = os.path.join( cs.archive_store, dmp.job.name.encode(), "%s.1.%s" % ( dmp.id.encode(), find_ext(dmp.type.name) ) ) #unencrypted types if not cs.encryption: if dmp.type.name == "gzMysqlDump": comm = 'zcat %s' % dmp_file elif dmp.type.name == "MysqlDump": comm = 'cat %s' % dmp_file #encrypted types elif cs.encryption: pfile = mk_ssl_auth_file(cs.encryption.split(":")[1]) if dmp.type.name == "gzMysqlDump": comm = 'openssl enc -in %s -d -aes-256-cbc -pass '\ 'file:%s | gunzip' % (dmp_file, pfile) elif dmp.type.name == "MysqlDump": comm = 'openssl enc -in %s -d -aes-256-cbc -pass '\ 'file:%s' % (dmp_file, pfile) if opts.filename == "-": retcode = subprocess.call(comm, shell=True) else: if os.path.exists(opts.filename): raise BackupDBException('Cowardly refusing to overwrite' ' %s.' % opts.filename) fd = os.open(opts.filename, os.O_WRONLY | os.O_CREAT, 0600) with os.fdopen(fd, 'w') as out: retcode = subprocess.call(comm, stdout=out, shell=True) if cs.encryption: os.unlink(pfile) if retcode != 0: sys.stderr.write("Recovery failed with status %s.\n" % retcode)
def db_backup(self): if not self.cf.mysql: raise ConfigException('MySQL dumps are not enabled ' 'for this job.\n') lock = self.lock("db_backup") mpath = os.path.join(self.cf.archive_store, self.section) mkdir(mpath) if self.cf.mysql_compr: btype_name = "gzMysqlDump" ext = ".dmp.gz" else: btype_name = "MysqlDump" ext = ".dmp" btype = self.get_or_create(BackupType, name=btype_name) cat = Catalog(type=btype, job=self.Job) self.sess.add(cat) lock.cat = cat self.sess.add(lock) self.sess.commit() # we use a slice naming convention as in dar, in a future, it would #be nice to be able to split dump files. file_name = os.path.join(mpath, cat.id + ".1" + ext) if self.cf.redundancy and self.cf.par_local: dest_file_name = file_name file_name = os.path.join(self.cf.local_store, self.section, cat.id + ".1" + ext) args = "mysqldump --defaults-extra-file=%s --single-transaction " args += "--all-databases -e --opt " #mk_mysql_auth_file will create the config for the job auth_file = mk_mysql_auth_file( id=cat.id.encode(), mysql_host=self.cf.mysql_host, mysql_user=self.cf.mysql_user, mysql_pass=self.cf.mysql_pass) self.logger.debug("Mysql authfile created: %s" % auth_file) args = shlex.split(args % auth_file) testcmd = 'echo status | mysql --defaults-extra-file=%s' % auth_file try: t = subprocess.check_call(testcmd, shell=True, **commands['popen_defaults']) except subprocess.CalledProcessError: self.logger.debug('Deleting auth_file %s' % auth_file) os.unlink(auth_file) raise BackupDBException('Could not contact the mysql server, check' ' your configuration/connectivity.\n') _dir_name = os.path.dirname(file_name) if not os.path.exists(_dir_name): os.makedirs(_dir_name, 0722) fd = os.open(file_name, os.O_WRONLY | os.O_CREAT, 0600) with os.fdopen(fd, 'w') as dmpfile: if self.cf.mysql_compr: cmdline = " ".join(args) + " | gzip - " if self.cf.encryption: pfile = mk_ssl_auth_file(self.cf.encryption.split(":")[1]) cmdline += '| openssl aes-256-cbc -salt -pass file:%s' \ % pfile cat.enc = True elif self.cf.encryption: pfile = mk_ssl_auth_file(self.cf.encryption.split(":")[1]) cmdline = " ".join(args) + \ ' | openssl aes-256-cbc -salt -pass file:%s ' % pfile cat.enc = True else: cmdline = " ".join(args) self.logger.debug(cmdline) self.logger.debug(cmdline) p = subprocess.Popen( cmdline, stdin=None, stderr=subprocess.PIPE, stdout=dmpfile, shell=True) start_time = time.time() comm = p.communicate() end_time = time.time() cat.ttook = int(end_time - start_time) cat.log = "stdout:\n%s\nstderr:\n%s\n" % comm self.sess.commit() if p.returncode == 0: if self.cf.redundancy: if self.cf.par_local: store = self.cf.local_store else: store = self.cf.archive_store fpath = str( "%s/%s" % (store, cat.job.name)) dar_par(mode="Creating", cmd=[fpath, cat.id.encode(), '1', ext[1:], '', str(self.cf.redundancy)]) if self.cf.par_local: dar_move( cmd=["-j", self.section, "-i", cat.id.encode(), "-s", "1"], sect=self.cf) self.save_stats(cat) cat.clean = True cat.status = p.returncode dmpfile.flush() save_xattr(cat, self.cf) self.sess.commit() self.logger.debug("Deleting authfile %s.." % auth_file) os.unlink(auth_file) if self.cf.encryption: if os.path.exists(pfile): os.unlink(pfile) self.sess.delete(lock) self.sess.commit() return cat