Merge lp:~therp-nl/openupgrade-server/openupgrade-server_migrate-script into lp:openupgrade-server/6.1

Proposed by Holger Brunn (Therp)
Status: Merged
Merge reported by: Holger Brunn (Therp)
Merged at revision: not available
Proposed branch: lp:~therp-nl/openupgrade-server/openupgrade-server_migrate-script
Merge into: lp:openupgrade-server/6.1
Diff against target: 222 lines (+217/-0)
1 file modified
scripts/migrate.py (+217/-0)
To merge this branch: bzr merge lp:~therp-nl/openupgrade-server/openupgrade-server_migrate-script
Reviewer Review Type Date Requested Status
Stefan Rijnhart (Opener) Approve
Review via email: mp+134734@code.launchpad.net

This proposal supersedes a proposal from 2012-11-16.

Description of the change

Usage: migrate.py [options]

Migrate script for the impatient or lazy. Makes a copy of your database,
downloads the files necessary to migrate it as requested and runs the
migration on the copy (so your original database will not be touched). While
the migration is running only errors are shown, for a detailed log see
${branch-dir}/migration.log

Options:
  -h, --help show this help message and exit
  -C CONFIG, --config=CONFIG
                        current openerp config (required)
  -D DATABASE, --database=DATABASE
                        current openerp database (required if not given in
                        config)
  -B BRANCH_DIR, --branch-dir=BRANCH_DIR
                        the directory to download openupgrade-server code to
                        [/var/tmp/openupgrade]
  -R MIGRATIONS, --run-migrations=MIGRATIONS
                        comma separated list of migrations to run 6.0,6.1
                        (required)
  -A ADD, --add=ADD load a python module that declares on dict
                        'migrations' which is merged with the one of this
                        script (see the source for details)
  -I, --inplace don't copy database before attempting upgrade
                        (dangerous)

To post a comment you must log in.
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

Hi Holger,

Thank you, I can see this being useful. However, it seems a bit tailor made for Therp as it has the Banking Addons hardcoded in the script. Worse is that it does not allow for an arbitrary collection of addons branches.

Could you make it a little more generic so that an arbitrary number of branches may be downloaded and incorporated in the addons path? Such a construction may very well feature the Banking Addons as an example.

One more thing is the location of the script (the root path of the repository) which together with its name make it a little too prominent. It seems to imply that the script is tantamount to migrating any database. Please move it to the openupgrade directory, and change it to something like 'batch-migrate.py'.

Cheers,
Stefan.

review: Needs Fixing
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

Very nice!

review: Approve
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'scripts'
2=== added file 'scripts/migrate.py'
3--- scripts/migrate.py 1970-01-01 00:00:00 +0000
4+++ scripts/migrate.py 2012-11-16 19:31:47 +0000
5@@ -0,0 +1,217 @@
6+#!/usr/bin/python
7+
8+import os
9+import sys
10+import StringIO
11+import psycopg2
12+import psycopg2.extensions
13+from optparse import OptionParser
14+from ConfigParser import SafeConfigParser
15+from bzrlib.branch import Branch
16+from bzrlib.repository import Repository
17+from bzrlib.workingtree import WorkingTree
18+import bzrlib.plugin
19+import bzrlib.builtins
20+import bzrlib.info
21+
22+migrations={
23+ '6.1': {
24+ 'addons': {
25+ 'addons': 'lp:openupgrade-addons',
26+ 'banking': 'lp:banking-addons',
27+ 'web': {'url': 'lp:openerp-web/6.1', 'addons_dir': 'addons'},
28+ },
29+ 'server': {
30+ 'url': 'lp:openupgrade-server',
31+ 'addons_dir': os.path.join('openerp','addons'),
32+ 'root_dir': os.path.join(''),
33+ 'cmd': 'openerp-server --update=all --database=%(db)s '+
34+ '--config=%(config)s --stop-after-init --no-xmlrpc --no-netrpc',
35+ },
36+ },
37+ '6.0': {
38+ 'addons': {
39+ 'addons': 'lp:openupgrade-addons/6.0',
40+ 'banking': 'lp:banking-addons/6.0',
41+ },
42+ 'server': {
43+ 'url': 'lp:openupgrade-server/6.0',
44+ 'addons_dir': os.path.join('bin','addons'),
45+ 'root_dir': os.path.join('bin'),
46+ 'cmd': 'bin/openerp-server.py --update=all --database=%(db)s '+
47+ '--config=%(config)s --stop-after-init --no-xmlrpc --no-netrpc',
48+ },
49+ },
50+}
51+config = SafeConfigParser()
52+parser = OptionParser(description="""Migrate script for the impatient or lazy.
53+Makes a copy of your database, downloads the files necessary to migrate
54+it as requested and runs the migration on the copy (so your original
55+database will not be touched). While the migration is running only errors are
56+shown, for a detailed log see ${branch-dir}/migration.log
57+""")
58+parser.add_option("-C", "--config", action="store", type="string",
59+ dest="config",
60+ help="current openerp config (required)")
61+parser.add_option("-D", "--database", action="store", type="string",
62+ dest="database",
63+ help="current openerp database (required if not given in config)")
64+parser.add_option("-B", "--branch-dir", action="store", type="string",
65+ dest="branch_dir",
66+ help="the directory to download openupgrade-server code to [%default]",
67+ default='/var/tmp/openupgrade')
68+parser.add_option("-R", "--run-migrations", action="store", type="string",
69+ dest="migrations",
70+ help="comma separated list of migrations to run\n\n"+
71+ ','.join(sorted([a for a in migrations]))+
72+ "\n(required)")
73+parser.add_option("-A", "--add", action="store", type="string", dest="add",
74+ help="load a python module that declares on dict 'migrations' which is"+
75+ " merged with the one of this script (see the source for details)")
76+parser.add_option("-I", "--inplace", action="store_true", dest="inplace",
77+ help="don't copy database before attempting upgrade (dangerous)")
78+(options, args) = parser.parse_args()
79+
80+if (not options.config or not options.migrations
81+ or not reduce(lambda a,b: a and (b in migrations),
82+ options.migrations.split(','), True)):
83+ parser.print_help()
84+ sys.exit()
85+
86+config.read(options.config)
87+
88+db_user=config.get('options', 'db_user')
89+db_name=options.database or config.get('options', 'db_name')
90+
91+if not db_name or db_name=='' or db_name.isspace() or db_name.lower()=='false':
92+ parser.print_help()
93+ sys.exit()
94+
95+if options.inplace:
96+ db=db_name
97+else:
98+ db=db_name+'_migrated'
99+
100+if options.add:
101+ merge_migrations={}
102+ if os.path.isfile(options.add):
103+ import imp
104+ merge_migrations_mod=imp.load_source('merge_migrations_mod',
105+ options.add)
106+ merge_migrations=merge_migrations_mod.migrations
107+ else:
108+ merge_migrations=eval(options.add)
109+
110+ def deep_update(dict1, dict2):
111+ result={}
112+ for (name,value) in dict1.iteritems():
113+ if dict2.has_key(name):
114+ if isinstance(dict1[name], dict) and isinstance(dict2[name],
115+ dict):
116+ result[name]=deep_update(dict1[name], dict2[name])
117+ else:
118+ result[name]=dict2[name]
119+ else:
120+ result[name]=dict1[name]
121+ for (name,value) in dict2.iteritems():
122+ if name not in dict1:
123+ result[name]=value
124+ return result
125+
126+ migrations=deep_update(migrations, merge_migrations)
127+
128+for version in options.migrations.split(','):
129+ if version not in migrations:
130+ print '%s is not a valid version! (valid verions are %s)' % (version,
131+ ','.join(sorted([a for a in migrations])))
132+
133+bzrlib.plugin.load_plugins()
134+bzrlib.trace.enable_default_logging()
135+logfile=os.path.join(options.branch_dir,'migration.log')
136+
137+if not os.path.exists(options.branch_dir):
138+ os.mkdir(options.branch_dir)
139+
140+for version in options.migrations.split(','):
141+ if not os.path.exists(os.path.join(options.branch_dir,version)):
142+ os.mkdir(os.path.join(options.branch_dir,version))
143+ for (name,url) in dict(migrations[version]['addons'],
144+ server=migrations[version]['server']['url']).iteritems():
145+ link=url.get('link', False) if isinstance(url, dict) else False
146+ url=url['url'] if isinstance(url, dict) else url
147+ if os.path.exists(os.path.join(options.branch_dir,version,name)):
148+ if link:
149+ continue
150+ cmd_revno=bzrlib.builtins.cmd_revno()
151+ cmd_revno.outf=StringIO.StringIO()
152+ cmd_revno.run(location=os.path.join(options.branch_dir,version,
153+ name))
154+ print 'updating %s rev%s' %(os.path.join(version,name),
155+ cmd_revno.outf.getvalue().strip())
156+ cmd_pull=bzrlib.builtins.cmd_pull()
157+ cmd_pull.outf=StringIO.StringIO()
158+ cmd_pull.outf.encoding='utf8'
159+ cmd_pull.run(directory=os.path.join(options.branch_dir,version,
160+ name), overwrite=True)
161+ if hasattr(cmd_pull, '_operation'):
162+ cmd_pull.cleanup_now()
163+ print 'now at rev'+cmd_revno.outf.getvalue().strip()
164+ else:
165+ if link:
166+ print 'linking %s to %s'%(url,
167+ os.path.join(options.branch_dir,version,name))
168+ os.symlink(url, os.path.join(options.branch_dir,version,name))
169+ else:
170+ print 'getting '+url
171+ cmd_branch=bzrlib.builtins.cmd_branch()
172+ cmd_branch.outf=StringIO.StringIO()
173+ cmd_branch.run(url, os.path.join(options.branch_dir,version,
174+ name))
175+
176+if not options.inplace:
177+ print('copying database %(db_name)s to %(db)s...' % {'db_name': db_name,
178+ 'db': db})
179+ conn=psycopg2.connect(database=db_name, user=db_user)
180+ conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
181+ cur=conn.cursor()
182+ cur.execute('drop database if exists %(db)s' % {'db': db})
183+ cur.execute('create database %(db)s' % {'db': db})
184+ cur.close()
185+ os.system(('pg_dump --format=custom --user=%(user)s %(db_name)s | pg_restore '+
186+ '--dbname=%(db)s') % {
187+ 'user': db_user,
188+ 'db_name': db_name,
189+ 'db': db,
190+ })
191+
192+for version in options.migrations.split(','):
193+ print 'running migration for '+version
194+ config.set('options', 'without_demo', 'True')
195+ config.set('options', 'logfile', logfile)
196+ config.set('options', 'port', 'False')
197+ config.set('options', 'netport', 'False')
198+ config.set('options', 'xmlrpc_port', 'False')
199+ config.set('options', 'netrpc_port', 'False')
200+ config.set('options', 'addons_path',
201+ ','.join([os.path.join(options.branch_dir,
202+ version,'server',migrations[version]['server']['addons_dir'])] +
203+ [
204+ os.path.join(options.branch_dir,version,name,
205+ url.get('addons_dir', '') if isinstance(url, dict) else '')
206+ for (name,url) in migrations[version]['addons'].iteritems()
207+ ]
208+ )
209+ )
210+ config.set('options', 'root_path', os.path.join(options.branch_dir,version,
211+ 'server', migrations[version]['server']['root_dir']))
212+ config.write(open(
213+ os.path.join(options.branch_dir,version,'server.cfg'), 'w+'))
214+ os.system(
215+ os.path.join(options.branch_dir,version,'server',
216+ migrations[version]['server']['cmd'] % {
217+ 'db': db,
218+ 'config': os.path.join(options.branch_dir,version,
219+ 'server.cfg')
220+ }
221+ )
222+ )

Subscribers

People subscribed via source and target branches

to status/vote changes: