Skip to content

Commit 75d92f2

Browse files
committed
Pass database and server in one URL, allow globs.
1 parent dcffc5a commit 75d92f2

File tree

1 file changed

+67
-21
lines changed

1 file changed

+67
-21
lines changed

couchdb/tools/replicate.py

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,46 @@
1717
Use 'python replicate.py --help' to get more detailed usage instructions.
1818
"""
1919

20-
import couchdb.client
20+
from couchdb import http, client
2121
import optparse
2222
import sys
2323
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:])
2455

2556
def 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

Comments
 (0)