Wednesday, June 16, 2010

collective.migrator


collective.migrator is a buildout based tool to help migrate content between Plone/Zope instances. It can be installed as follows:

$ easy_install collective.migrator


Once installed you can run the tool to set up the migration environment

$ migrator


This creates a folder called migrator and installs a buildout environment there. All further actions are run from this folder.

The first thing that you want to do at this point is customize the instance.cfg file.

By default it looks like this:


[remote]
host = xxx.webfactional.com
user = ssh_user
port = 8080
extensions = /usr/Plone-2.5.5/zeocluster/client1/Extensions
zmi_user = admin
zmi_pwd = admin
root = Plone
export = /usr/Plone-2.5.5/zeocluster/client1/var

[local]
host = localhost
port = 8080
extensions = /home/suresh/plone4/parts/instance/Extensions
zmi_user = admin
zmi_pwd = admin
root = Plone
import = /home/suresh/plone4/var/instance/import


This defines the settings for all the Plone instances involved in the migration.

The buildout.cfg defines the steps that will be executed as part of the migration.

Here is the default content:


[buildout]
extends = instance.cfg
migrate_frontpage.cfg
migrate_users.cfg
migrate_props.cfg
parts =
tbd =
${migrate_frontpage:parts}
${migrate_users:parts}
${migrate_props:parts}


As you can see, parts has been intentionally left blank. Also instance.cfg described previously is being used here. The other migrate_*.cfg files contain some sample steps to move various objects between the instances.

As a simple test, you can change parts in buildout.cfg to look like this:


parts = export_frontpage


This step is defined in migrate_frontpage.cfg.

Now after you run buildout as follows:

bin/buildout


you should notice that the front-page object has been exported in the remote Plone instance. Once you gain more confidence in the tool, you can even try to run the other steps found in the migrate_*.cfg files.

PS: This may not be the "coolest" way to manipulate your Plone and some of these actions may be better done with GenericSetup profiles, but this worked for me!

Monday, June 7, 2010

Login problems using zope.testbrowser.browser


Recently, while writing some automated scripts for the ZMI with zope.testbrowser, I ran into some problems.

Here was my first try straight out of the pypi page of zope.testbrowser:


from zope.testbrowser.browser import Browser
br = Browser()
br.addHeader('Authorization', 'Basic %s:%s' % (zmi_user, zmi_passwd))
url = 'http://%s:%s/manage_main' % (zmi_host, zmi_port)
br.open(url)

Contrary to expectations, this resulted in

mechanize._response.HTTPError: HTTP Error 500: Internal Server Error

Looking at the server error log was more puzzling.

Traceback (innermost last):
Module ZPublisher.Publish, line 106, in publish
Module ZPublisher.BaseRequest, line 452, in traverse
Module Products.PluggableAuthService.PluggableAuthService, line 234, in validate
Module Products.PluggableAuthService.PluggableAuthService, line 627, in _extractUserIds
Module Products.PluggableAuthService.PluggableAuthService, line 123, in extractCredentials
Module ZPublisher.HTTPRequest, line 1343, in _authUserPW
ValueError: need more than 1 value to unpack

Suspecting that this may be due to a Zope/zope.testbrowser version mismatch as this server was running Zope 2.9.8, I then tried the same with a brand new Plone 4 instance. It still failed but the traceback gave me a clue

Traceback (innermost last):
Module ZPublisher.Publish, line 116, in publish
Module ZPublisher.BaseRequest, line 593, in traverse
Module Products.PluggableAuthService.PluggableAuthService, line 232, in validate
Module Products.PluggableAuthService.PluggableAuthService, line 558, in _extractUserIds
Module Products.PluggableAuthService.plugins.HTTPBasicAuthHelper, line 76, in extractCredentials
Module ZPublisher.HTTPRequest, line 1519, in _authUserPW
Module base64, line 321, in decodestring
Error: Incorrect padding

I tried the obvious

import base64
br.addHeader('Authorization', base64.encodestring('Basic %s:%s' % (zmi_user, zmi_passwd)))

but was now left with

mechanize._response.HTTPError: HTTP Error 401: Unauthorized

After poking some more at ZPublisher.HTTPRequest.py I came up with the winning

br.addHeader('Authorization', 'Basic %s' % base64.encodestring('%s:%s' % (zmi_user, zmi_passwd)))