1717Use 'python replicate.py --help' to get more detailed usage instructions.
1818"""
1919
20- import couchdb . client
20+ from couchdb import http , client
2121import optparse
2222import sys
2323import time
24+ import urlparse
25+ import re
26+
27+ def findpath (parser , s ):
28+ '''returns (server url, path component)'''
29+
30+ if s == '.' :
31+ return client .DEFAULT_BASE_URL , ''
32+ if not s .startswith ('http' ):
33+ return client .DEFAULT_BASE_URL , s
34+
35+ bits = urlparse .urlparse (s )
36+ res = http .Resource ('%s://%s/' % (bits .scheme , bits .netloc ), None )
37+ parts = bits .path .split ('/' )[1 :]
38+ if not parts [- 1 ]:
39+ parts = parts [:- 1 ]
40+
41+ cut = None
42+ for i in range (0 , len (parts ) + 1 ):
43+ try :
44+ data = res .get_json (parts [:i ])[2 ]
45+ except Exception :
46+ data = None
47+ if data and 'couchdb' in data :
48+ cut = i
49+
50+ if cut is None :
51+ raise parser .error ("'%s' does not appear to be a CouchDB" % s )
52+
53+ base = res .url + ('/' .join (parts [:cut ]) if parts [:cut ] else '' )
54+ return base , '/' .join (parts [cut :])
2455
2556def main ():
2657
2758 usage = '%prog [options]'
2859 parser = optparse .OptionParser (usage = usage )
29- parser .add_option ('--database' ,
30- action = 'append' ,
31- dest = 'dbnames' ,
32- help = 'Database to replicate. Can be given more than once. [all databases]' )
3360 parser .add_option ('--continuous' ,
3461 action = 'store_true' ,
3562 dest = 'continuous' ,
@@ -43,32 +70,51 @@ def main():
4370 if len (args ) != 2 :
4471 raise parser .error ('need source and target arguments' )
4572
73+ # set up server objects
74+
4675 src , tgt = args
47- if not src .endswith ('/' ):
48- src += '/'
49- if not tgt .endswith ('/' ):
50- tgt += '/'
76+ sbase , spath = findpath (parser , src )
77+ source = client .Server (sbase )
78+ tbase , tpath = findpath (parser , tgt )
79+ target = client .Server (tbase )
80+
81+ # check database name specs
82+
83+ if '*' in tpath :
84+ raise parser .error ('invalid target path: must be single db or empty' )
85+ elif '*' in spath and tpath :
86+ raise parser .error ('target path must be empty with multiple sources' )
5187
52- source_server = couchdb .client .Server (src )
53- target_server = couchdb .client .Server (tgt )
88+ all = sorted (i for i in source )
89+ if not spath :
90+ raise parser .error ('source database must be specified' )
91+ elif spath in all :
92+ databases = [(spath , tpath if tpath else spath )]
93+ elif '*' in spath :
94+ check = re .compile (spath .replace ('*' , '.*?' ))
95+ databases = [(i , i ) for i in all if check .match (i )]
5496
55- if not options .dbnames :
56- dbnames = sorted (i for i in source_server )
57- else :
58- dbnames = options .dbnames
97+ if not databases :
98+ raise parser .error ("no source databases match glob '%s'" % spath )
5999
60- for dbname in sorted (dbnames , reverse = True ):
100+ # do the actual replication
101+
102+ for sdb , tdb in databases :
61103
62104 start = time .time ()
63- print dbname ,
105+ print sdb , '->' , tdb ,
64106 sys .stdout .flush ()
65- if dbname not in target_server :
66- target_server .create (dbname )
107+
108+ if tdb not in target :
109+ target .create (tdb )
67110 print "created" ,
68111 sys .stdout .flush ()
69112
70- sdb = '%s%s' % (src , dbname )
71- target_server .replicate (sdb , dbname , continuous = options .continuous )
113+ sdb = '%s%s' % (sbase , sdb )
114+ if options .continuous :
115+ target .replicate (sdb , tdb , continuous = options .continuous )
116+ else :
117+ target .replicate (sdb , tdb )
72118 print '%.1fs' % (time .time () - start )
73119
74120 if not options .compact :
0 commit comments