1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#!/usr/bin/python
# This script updates Soledad's design documents in the session database and
# all user databases with contents from the installed leap.soledad.common
# package.
import json
import logging
import argparse
import re
import threading
import binascii
from getpass import getpass
from ConfigParser import ConfigParser
from couchdb.client import Server
from couchdb.http import Resource, Session
from datetime import datetime
from urlparse import urlparse
from leap.soledad.common import ddocs
# parse command line for the log file name
logger_fname = "/tmp/update-design-docs_%s.log" % \
str(datetime.now()).replace(' ', '_')
parser = argparse.ArgumentParser()
parser.add_argument('--log', action='store', default=logger_fname, type=str,
required=False, help='the name of the log file', nargs=1)
args = parser.parse_args()
# configure the logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
print "Logging to %s." % args.log
logging.basicConfig(
filename=args.log,
format="%(asctime)-15s %(message)s")
# configure threads
max_threads = 20
semaphore_pool = threading.BoundedSemaphore(value=max_threads)
threads = []
# get couch url
cp = ConfigParser()
cp.read('/etc/leap/soledad-server.conf')
url = urlparse(cp.get('soledad-server', 'couch_url'))
# get admin password
netloc = re.sub('^.*@', '', url.netloc)
url = url._replace(netloc=netloc)
password = getpass("Admin password for %s: " % url.geturl())
url = url._replace(netloc='admin:%s@%s' % (password, netloc))
resource = Resource(url.geturl(), Session(retry_delays=[1,2,4,8], timeout=10))
server = Server(url=resource)
hidden_url = re.sub(
'http://(.*):.*@',
'http://\\1:xxxxx@',
url.geturl())
print """
==========
ATTENTION!
==========
This script will modify Soledad's shared and user databases in:
%s
This script does not make a backup of the couch db data, so make sure you
have a copy or you may loose data.
""" % hidden_url
confirm = raw_input("Proceed (type uppercase YES)? ")
if confirm != "YES":
exit(1)
# convert design doc content
design_docs = {
'_design/docs': json.loads(binascii.a2b_base64(ddocs.docs)),
'_design/syncs': json.loads(binascii.a2b_base64(ddocs.syncs)),
'_design/transactions': json.loads(binascii.a2b_base64(ddocs.transactions)),
}
#
# Thread
#
class DBWorkerThread(threading.Thread):
def __init__(self, server, dbname, db_idx, db_len, release_fun):
threading.Thread.__init__(self)
self._dbname = dbname
self._cdb = server[self._dbname]
self._db_idx = db_idx
self._db_len = db_len
self._release_fun = release_fun
def run(self):
logger.info("(%d/%d) Updating db %s." % (self._db_idx, self._db_len,
self._dbname))
for doc_id in design_docs:
doc = self._cdb[doc_id]
for key in ['lists', 'views', 'updates']:
if key in design_docs[doc_id]:
doc[key] = design_docs[doc_id][key]
self._cdb.save(doc)
# release the semaphore
self._release_fun()
db_idx = 0
db_len = len(server)
for dbname in server:
db_idx += 1
if not (dbname.startswith('user-') or dbname == 'shared') \
or dbname == 'user-test-db':
logger.info("(%d/%d) Skipping db %s." % (db_idx, db_len, dbname))
continue
# get access to couch db
cdb = Server(url.geturl())[dbname]
#---------------------------------------------------------------------
# Start DB worker thread
#---------------------------------------------------------------------
semaphore_pool.acquire()
thread = DBWorkerThread(server, dbname, db_idx, db_len, semaphore_pool.release)
thread.daemon = True
thread.start()
threads.append(thread)
map(lambda thread: thread.join(), threads)
|