17
17
Use 'python replicate.py --help' to get more detailed usage instructions.
18
18
"""
19
19
20
- import couchdb . client
20
+ from couchdb import http , client
21
21
import optparse
22
22
import sys
23
23
import 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 :])
24
55
25
56
def main ():
26
57
27
58
usage = '%prog [options]'
28
59
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]' )
33
60
parser .add_option ('--continuous' ,
34
61
action = 'store_true' ,
35
62
dest = 'continuous' ,
@@ -43,32 +70,51 @@ def main():
43
70
if len (args ) != 2 :
44
71
raise parser .error ('need source and target arguments' )
45
72
73
+ # set up server objects
74
+
46
75
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' )
51
87
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 )]
54
96
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 )
59
99
60
- for dbname in sorted (dbnames , reverse = True ):
100
+ # do the actual replication
101
+
102
+ for sdb , tdb in databases :
61
103
62
104
start = time .time ()
63
- print dbname ,
105
+ print sdb , '->' , tdb ,
64
106
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 )
67
110
print "created" ,
68
111
sys .stdout .flush ()
69
112
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 )
72
118
print '%.1fs' % (time .time () - start )
73
119
74
120
if not options .compact :
0 commit comments