也许我可以帮忙。
您始终可以在 12 月 24 日的适当时间截断 ZODB:这当然意味着您丢失了对工作站点的任何后续更改(显然,不能对其他站点进行任何更改)。
您可能能够确定自 12/24 以来工作站点上的更改,将其导出,并在截断后重新导入。
然后你需要完全分离两个站点,分离 ZODB,最好分离 ZOPE 服务器,以确保这种情况不会再次发生。我会导出所有中文内容并导入到新的 Plone 站点。
这是我使用的脚本:它依赖于使用collective.recipe.backup 执行定期备份的构建配置,但是您应该能够插入任何ZODB 文件作为输入(这可能是最简单的,尽管添加collective.recipe .backup到 buildout,重新运行 buildout,在现有 ZODB 上运行备份,然后运行此脚本。
无论如何,请先保存当前 ZODB 的副本!
#! /usr/bin/python2.6 zopepy
# Standalone usage:
# cd to the directory containing this script, then
# HTTP_REFERER=anything ./restoreDB.py yyyy-mm-dd [hh:mm:ss]
#
# The HTTP_REFERER environment variable needs to be set to any non-null value
# to make the script think it's being called in a CGI setting
# The arguments are a date/time stamp in Local time.
import cgi
import cgitb
import datetime
import time
from dateutil import tz
from dateutil.parser import parse as date_parse
import logging
import os
import re
import subprocess
from subprocess import PIPE
import sys
import ConfigParser
from ZODB.FileStorage import FileIterator
from ZODB.TimeStamp import TimeStamp as ZODBTimeStamp
cgitb.enable()
zeo = ''
loglevel = logging.INFO
logging.basicConfig(filename='/tmp/recover.log',level=loglevel,
format='%(asctime)s %(levelname)s: %(message)s')
def restartZeo():
subprocess.check_call([zeo, 'start chone-zeo'])
def execute(cmd, stdin='', shell=False):
proc = subprocess.Popen(cmd, stdin = PIPE, stdout = PIPE, stderr= subprocess.STDOUT, shell=shell)
output, error = proc.communicate(input=stdin)
logging.info(cmd)
logging.info(output)
if proc.returncode > 0:
restartZeo()
print "\n"
raise Exception("Aborted")
return output
def start():
print "Content-type: text/html"
global zeo
form = cgi.FieldStorage()
logging.info(form)
restore_date = date_parse(form.getfirst('restore-to-time', ''))
restore_date = restore_date.replace(second=59,tzinfo=tz.tzlocal()).astimezone(tz.tzoffset(None, 0))
logging.info('restore_date: %s' % restore_date)
referer = os.environ.get('HTTP_REFERER','')
if not referer:
print "\n"
raise Exception('HTTP_REFERER (%s) does not appear to be part of the Plone site' % referer)
# read the buildout's .installed.cfg file
config = ConfigParser.ConfigParser()
config.readfp(open('../.installed.cfg'))
# get the path to the backup command
try:
paths = re.split(r'\s+',config.get('backup','__buildout_installed__'))
backup= [x for x in paths if x.endswith('/backup')][0]
except:
print "\n"
raise Exception("Can't find the path to the backup command")
bin = os.path.dirname(backup)
site= bin.split(os.sep)[-2].split('-')[-1]
stopped = False
# shutdown the ZEO before we start the recovery
# we try to do it from the parent directory's "supervisorctl", otherwise directly
# from the local zeoserver command
for cmd in ['/../../bin/supervisorctl','/zeoserver']:
zeo = bin+cmd
try:
subprocess.check_call([zeo, 'stop %s-zeo' % site])
stopped = True
break
except:
pass
if not stopped:
print "\n"
raise Exception("Could not stop the ZEO server.")
# get the path to the Data.fs
db = config.get('backup','datafs')
# determine the first backup FOLLOWING the time given
repozo_date = restore_date.strftime('%Y-%m-%d-%H-%M-%S')
logging.info('repozo_date: '+repozo_date)
backup_path = config.get('backup','location')
logging.info('backup_path: '+backup_path)
# get the list of backups (most recent first)
files = [x for x in sorted(os.listdir(backup_path),reverse=True)
if x.endswith('sz') and x > repozo_date]
# take a backup before we start, but only if:
# - there is no backup containing the repozo_date or,
# - the latest backup is older than the current db
# this prevents dropping our last full backup off the cycle when we
# immediately decide to restore the original file after a previous restore
current_ts=time.gmtime(os.path.getmtime(db)) # the modification timestamp of the current database
if len(files) == 0 or files[0] < time.strftime('%Y-%m-%d-%H-%M-%S',current_ts):
execute([backup])
# if we found an older backup we need restored:
if len(files) > 0:
# do a restore to the end of the first backup after the time we really want
repozo_date = files[-1].split('.')[0]
logging.info('repozo_date: '+repozo_date)
output = execute([bin+'/restore', repozo_date], stdin='yes\n\n\n')
for line in output.splitlines():
if 'restoring database file' in line:
db = line.split(' to ')[1]
break
# find the next transaction after our timestamp
ts = list(restore_date.timetuple())[:6]
tid = repr(ZODBTimeStamp(*ts))
it = FileIterator(db, tid)
f = open(db, 'r+b')
# truncate the database at that transaction
logging.info('truncating db at %d' % it._pos)
f.seek(it._pos)
f.truncate()
f.close()
# restart the zeo
execute([zeo, 'restart %(site)s-zeo %(site)s-plone' % locals()])
# redirect to the Restore page
print "Location: %s\n" % referer
if __name__ == '__main__':
from optparse import OptionParser
parser = OptionParser()
(options, args) = parser.parse_args()
os.environ['QUERY_STRING'] = "restore-to-time=%s" % ' '.join(args)
start()