| |
Editing Data.fs from the command line (or, how to fix POSKeyErrors)
Submitted by: heureso
Last Edited: 2004-10-14
Category:
|
| Average rating is:
5.0 out of 5
|
(5 ratings) |
|
Description:
NOTE: This recipe applies primarily to Zope 2.6 and lower. For Zope 2.7+, 'zopectl debug' is recommended. There is an updated version of this recipe relevant to Zope >= 2.7 at http://www.zopelabs.com/cookbook/1095965033
This recipe explains how to edit a Zope Data.fs file from the Python interactive shell (command line) in order to delete, rename, or add objects into Zope's database. This a *very* useful thing to be able to do when your Data.fs has been damaged.
It was specifically useful for me after a disk error and subsequent fsrecover resulted in a Zope folder becoming inaccessible with a POSKeyError. The error, in this case, resulted from a single object, the acl_users folder, becoming inaccessible to referring objects. I was able to use this technique to delete the remnants of the errant acl_users folder and create a new one, thus resolving the problem.
Many, many thanks to Dieter Maurer <dieter@handshake.de> for providing most of this code.
|
Source (Text):
1. First, stop your Zope instance (otherwise, you will not be able to open the Data.fs as it will be locked).
2. Then go to a command line (Start->Run->cmd in Windows, or a command shell under Linux).
3. Change to your Zope directory (e.g., C:\Program Files\Zope in Windows, or /usr/local/zope under Linux).
4. Make sure that your PYTHONPATH environment variable is set appropriately:
Under Windows:
C:\Program Files\Zope>set PYTHONPATH=C:\Program Files\Zope\lib\python
Under Linux (assuming bash shell):
[user@machine zope]$ PYTHONPATH=/usr/local/zope/lib/python;export PYTHONPATH
5. Run the python interpreter:
Win:
C:\Program Files\Zope>bin\python
Linux:
[user@machine zope]$ bin/python
6. At the Python prompt, you will need to import a few of the relevant modules:
>> from Zope import app
>> from ZODB import POSException
>> from OFS import DTMLMethod
>> from AccessControl import User
7. You now need to load your root object:
>> root= app()
>> obj= root.unrestrictedTraverse(url_to_obj)
8. Assume, "obj" now contains the folder with the dangling reference; we must find the id belonging to the reference:
>> for id,val in obj.objectItems():
... try: val.getId()
... except POSKeyError: break
9. You can now delete the "bad" object:
>> obj.manage_delObjects(id)
>> get_transaction().commit()
10. In my case, I needed to re-create the bad user folder object:
>> obj.manage_addUserFolder('acl_users')
>> get_transaction().commit()
If you needed to re-create a bad DTMLMethod, it would be something like:
>> obj.manage_addDTMLMethod('method_id')
>> get_transaction().commit()
The script is not complete. You must import "POSKeyError" and
"DTMLMethod" and provide "url_to_obj".
11. You can now exit out of the Python command shell (Ctrl-D on Linux, Ctrl-Z then Enter on Windows). Restart your Zope instance, and try accessing the formerly inaccessible object. Hopefully, things will now work for you.
|
Explanation:
The target audience for this recipe is anyone who needs to manually add, modify or delete objects in the Zope Data.fs database from the command line (because said objects are not accessible from the ZMI web interface, etc. This was useful for me in resolving a pesky POSKeyError that began after running fsrecover.py on a corrupted database.
|
Comments:
| THANK YOU!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! by randolpho - 2004-10-14 |
Thankyouthankyouthankyouthankyou! This recipe worked flawlessly, and all I lost was a single object, out of thousands that could have been lost, all thanks to you! A million thanks!
And thanks to google who helped me get here!
Let me go rate your recipe a fiver!
Oh, just one nitpick: obj.manage_delObject(id) should be obj.manage_delObjects(id). Folks relatively familiar with Zope might catch that typo, but some n00bs might not. |
|
| Pretty cool! by slinkp - 2004-10-14 |
-- UPDATED Jan. 7 2004 --
If you don't have a clue where to start looking for the broken object(s), you need
a solution that searches for them. Below I discuss two approaches, fsrefs.py and a
recursive script based on the above recipe.
1) fsrefs.py should find ALL POSKeyErrors in a FileStorage, as Dieter Maurer
reminded me on zodb-dev. But it only tells you the OID so you have to do a
bit more work to find the object in the zope heirarchy.
If fsrefs.py doesn't come with your version of zope, you can find it
by downloading ZODB3 (for zope 2.6, get ZODB3-3.1.2 or later), and look
in the Tools directory. To run it you have to set PYTHONPATH to include
$ZOPE/lib/python.
fsrefs.py will tell you the OID of any object that can't be loaded. In
some cases, these seem not to be referred to by anything and it seems that
these can be fixed by a pack. In other cases, one object refers to
another object that is missing. This causes the first object to be broken.
In this case, packing may not help and you may need to manually delete the
referring object.
Now that we have an OID, let's resolve that to the actual object like so:
>>> app._p_jar[oid]
Note that fsfrefs.py prints OIDs like '0x1e9af', but
app._p_jar[oid] needs the oid like '\x00\x00\x00\x00\x00\x01\xe9\xaf'.
If you don't feel like working out the conversion by hand,
try importing p64 from ZODB.utils.
It will convert an integer into a correctly packed string.
Once you have the object, you can start getting clues about it. It's not
acquisition-wrapped so you can't easily find out its containment path,
but you can find out its ID, mod time, type, and owner, like so:
>>> obj = app._p_jar[oid]
>>> print obj.getId()
>>> print obj.bobobase_modification_time()
>>> print obj.meta_type
>>> print obj.__ac_local_roles__
Armed with that information, you can use the Find tab in zope and
should be able to easily track down the object. Once you know the path,
you can delete it, and a subsequent pack should remove all traces of the
offending object. Yay!
2) Recursive script approach:
Below is an alternative to fsrefs.py which I cooked up when I forgot
about fsrefs.py. It has the advantage of printing the physical path to
broken objects that it finds, so you don't have to do any further detective
work; but unlike fsrefs.py, this script only recurses ObjectManagers and
might miss other kinds of references (notably image & file Data references),
so fsrefs.py might find problems that this function misses.
Define the following function, import and run it in a zope debugging session.
This function is likely to take a looong time on a big database.
It will search the whole tree, unless you set stop_on_first true in which case
it exits on the first exception encountered.
(fixes from Russ Ferriday added 10/22/03)
from ZODB.POSException import POSKeyError
def error_finder(folder, exception=POSKeyError, stop_on_first=None):
""" start at the given folderish object.
If stop_on_first is true, quit after one exception;
otherwise, keep going through the whole tree."""
for id, next_item in folder.objectItems():
try:
next_item.getId()
except exception:
print `exception`, "in folder",
print '/'.join(folder.getPhysicalpath()),
print "at id:", id
if stop_on_first:
raise "done" # hack to break out of recursion
else:
# no error, recurse if it's objectManagerish
if hasattr(next_item.aq_base, 'objectItems'):
error_finder(next_item, exception, stop_on_first)
|
|
| 2.7+ slightly different by richard - 2004-10-14 |
2.7+ will require a valid configuration setup::
>>> from Zope.Startup.run import configure;
configure('etc/zope.conf')
>>> from Zope import app; root = app()
>>> obj=
root.unrestrictedTraverse('CGPublisher/publishers/13/proposals')
and then the rest as given in the entry above. |
|
|
|