Skip to content

Commit 3c1b923

Browse files
Merge pull request #98 from timvaillancourt/MCB_1.0-bugfix-v9
Mcb 1.0 bugfix v9
2 parents 126c79d + 06b8a9f commit 3c1b923

File tree

11 files changed

+147
-40
lines changed

11 files changed

+147
-40
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
bin
22
build
3-
rpmbuild
43
tmp
54
*.pyc
65
.idea

Makefile

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,24 @@ install: bin/mongodb-consistent-backup
1818
rm -rf bin build 2>/dev/null
1919
mkdir -p $(BINDIR) $(SHAREDIR)/$(NAME) || true
2020
install -m 0755 bin/mongodb-consistent-backup $(BINDIR)/mongodb-consistent-backup
21-
install -m 0644 conf/example.yml $(SHAREDIR)/$(NAME)/example.yml
21+
install -m 0644 conf/mongodb-consistent-backup.example.yml $(SHAREDIR)/$(NAME)/example.yml
2222
install -m 0644 LICENSE $(SHAREDIR)/$(NAME)/LICENSE
2323
install -m 0644 README.rst $(SHAREDIR)/$(NAME)/README.rst
2424

2525
uninstall:
2626
rm -f $(BINDIR)/mongodb-consistent-backup
2727
rm -rf $(SHAREDIR)/$(NAME)
2828

29-
rpm: clean
30-
mkdir -p rpmbuild/{SPECS,SOURCES/$(NAME)}
31-
cp -dpR $(NAME) conf Makefile setup.py scripts requirements.txt LICENSE README.rst VERSION rpmbuild/SOURCES/$(NAME)
32-
install scripts/$(NAME).spec rpmbuild/SPECS/$(NAME).spec
33-
tar --remove-files -C rpmbuild/SOURCES -czf rpmbuild/SOURCES/$(NAME).tar.gz $(NAME)
34-
rpmbuild -D "_topdir $(PWD)/rpmbuild" -D "version $(VERSION)" -bb rpmbuild/SPECS/$(NAME).spec
29+
rpm: bin/mongodb-consistent-backup
30+
rm -rf build/rpm 2>/dev/null || true
31+
mkdir -p build/rpm/SOURCES
32+
cp -f $(PWD)/{LICENSE,README.rst} build/rpm/SOURCES
33+
cp -f $(PWD)/bin/mongodb-consistent-backup build/rpm/SOURCES/mongodb-consistent-backup
34+
cp -f $(PWD)/conf/mongodb-consistent-backup.example.yml build/rpm/SOURCES/mongodb-consistent-backup.yml
35+
rpmbuild -D "_topdir $(PWD)/build/rpm" -D "version $(VERSION)" -bb scripts/$(NAME).spec
3536

3637
docker:
3738
docker build --no-cache -t mongodb_consistent_backup .
3839

3940
clean:
40-
rm -rf bin build rpmbuild $(NAME).egg-info tmp 2>/dev/null
41+
rm -rf bin build $(NAME).egg-info tmp 2>/dev/null

conf/mongodb-consistent-backup.example.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ production:
44
#username:
55
#password:
66
#authdb: admin
7+
log_dir: /tmp
78
backup:
89
method: mongodump
9-
name: test
10+
name: default
1011
location: /opt/mongodb/backup
1112
mongodump:
1213
binary: /usr/bin/mongodump

mongodb_consistent_backup/Backup/Mongodump/Mongodump.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def __init__(self, manager, config, timer, base_dir, backup_dir, replsets, shard
2020
super(Mongodump, self).__init__(self.__class__.__name__, manager, config, timer, base_dir, backup_dir)
2121
self.compression_method = self.config.backup.mongodump.compression
2222
self.binary = self.config.backup.mongodump.binary
23-
self.user = self.config.user
23+
self.user = self.config.username
2424
self.password = self.config.password
2525
self.authdb = self.config.authdb
2626
self.replsets = replsets
@@ -152,7 +152,7 @@ def run(self):
152152
self.verbose
153153
)]
154154
self.dump_threads[0].start()
155-
self.wait()
155+
self.dump_threads[0].join()
156156

157157
self.completed = True
158158
return self._summary

mongodb_consistent_backup/Common/Config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ def makeParser(self):
4343
parser.add_argument("-n", "--backup.name", dest="backup.name", help="Name of the backup set (required)", type=str)
4444
parser.add_argument("-l", "--backup.location", dest="backup.location", help="Base path to store the backup data (required)", type=str)
4545
parser.add_argument("-m", "--backup.method", dest="backup.method", help="Method to be used for backup (default: mongodump)", default='mongodump', choices=['mongodump'])
46-
parser.add_argument("--lockfile", dest="lockfile", help="Location of lock file (default: /tmp/mongodb_consistent_backup.lock)", default='/tmp/mongodb_consistent_backup.lock', type=str)
46+
parser.add_argument("-L", "--log-dir", dest="log_dir", help="Path to write log files to (default: disabled)", default='', type=str)
47+
parser.add_argument("--lock-file", dest="lock_file", help="Location of lock file (default: /tmp/mongodb_consistent_backup.lock)", default='/tmp/mongodb_consistent_backup.lock', type=str)
4748
parser.add_argument("--sharding.balancer.wait_secs", dest="sharding.balancer.wait_secs", help="Maximum time to wait for balancer to stop, in seconds (default: 300)", default=300, type=int)
4849
parser.add_argument("--sharding.balancer.ping_secs", dest="sharding.balancer.ping_secs", help="Interval to check balancer state, in seconds (default: 3)", default=3, type=int)
4950
return parser

mongodb_consistent_backup/Common/DB.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
class DB:
1111
def __init__(self, uri, config, do_replset=False, read_pref='primaryPreferred', do_connect=True, conn_timeout=5000, retries=5):
1212
self.uri = uri
13-
self.username = config.user
13+
self.username = config.username
1414
self.password = config.password
1515
self.authdb = config.authdb
1616
self.do_replset = do_replset
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import logging
2+
import os
3+
4+
from gzip import GzipFile
5+
6+
7+
class Logger:
8+
def __init__(self, config, backup_time):
9+
self.config = config
10+
self.backup_time = backup_time
11+
12+
self.log_level = logging.INFO
13+
if self.config.verbose:
14+
self.log_level = logging.DEBUG
15+
16+
self.do_file_log = False
17+
if self.config.log_dir is not '':
18+
if os.path.isdir(self.config.log_dir):
19+
self.do_file_log = True
20+
else:
21+
print("ERROR: Log directory: %s does not exist! Skipping file-based logging" % self.config.log_dir)
22+
23+
self.log_format = '[%(asctime)s] [%(levelname)s] [%(processName)s] [%(module)s:%(funcName)s:%(lineno)d] %(message)s'
24+
self.file_log = None
25+
self.last_log = None
26+
27+
def start(self):
28+
try:
29+
logging.basicConfig(level=self.log_level, format=self.log_format)
30+
if self.do_file_log:
31+
self.current_log_file = os.path.join(self.config.log_dir, "backup.log")
32+
self.backup_log_file = os.path.join(self.config.log_dir, "backup.%s.log" % self.backup_time)
33+
self.file_log = logging.FileHandler(self.backup_log_file)
34+
self.file_log.setLevel(self.log_level)
35+
self.file_log.setFormatter(logging.Formatter(self.log_format))
36+
logging.getLogger('').addHandler(self.file_log)
37+
except OSError, e:
38+
logging.warning("Could not start file log handler, writing to stdout only")
39+
pass
40+
41+
def close(self):
42+
if self.file_log:
43+
self.file_log.close()
44+
45+
def compress_last(self):
46+
gz_log = None
47+
try:
48+
if not os.path.isfile(self.last_log):
49+
return
50+
gz_file = "%s.gz" % self.last_log
51+
gz_log = GzipFile(gz_file, "w+")
52+
with open(self.last_log) as f:
53+
for line in f:
54+
gz_log.write(line)
55+
os.remove(self.last_log)
56+
finally:
57+
if gz_log:
58+
gz_log.close()
59+
60+
def rotate(self):
61+
if self.do_file_log:
62+
if os.path.islink(self.current_log_file):
63+
logging.info("Rotating previous log file")
64+
self.last_log = os.readlink(self.current_log_file)
65+
if self.last_log == self.backup_log_file:
66+
return
67+
os.remove(self.current_log_file)
68+
self.compress_last()
69+
os.symlink(self.backup_log_file, self.current_log_file)

mongodb_consistent_backup/Main.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from Backup import Backup
1111
from Common import Config, DB, Lock, MongoUri, Timer
1212
from Errors import Error, NotifyError, OperationError
13+
from Logger import Logger
1314
from Notify import Notify
1415
from Oplog import Tailer, Resolver
1516
from Replication import Replset, ReplsetSharded
@@ -44,6 +45,10 @@ def __init__(self, prog_name="mongodb-consistent-backup"):
4445
self.manager = Manager()
4546
self.timer = Timer(self.manager)
4647
self.timer_name = "mongodb_consistent_backup.%s" % self.__class__.__name__
48+
self.backup_time = datetime.now().strftime("%Y%m%d_%H%M")
49+
self.logger = None
50+
self.current_log_file = None
51+
self.backup_log_file = None
4752

4853
try:
4954
self.setup_config()
@@ -65,11 +70,11 @@ def setup_config(self):
6570
sys.exit(1)
6671

6772
def setup_logger(self):
68-
self.log_level = logging.INFO
69-
if self.config.verbose:
70-
self.log_level = logging.DEBUG
71-
logging.basicConfig(level=self.log_level,
72-
format='[%(asctime)s] [%(levelname)s] [%(processName)s] [%(module)s:%(funcName)s:%(lineno)d] %(message)s')
73+
try:
74+
self.logger = Logger(self.config, self.backup_time)
75+
self.logger.start()
76+
except Exception, e:
77+
self.exception("Could not start logger: %s" % e, e)
7378

7479
def setup_signal_handlers(self):
7580
try:
@@ -80,7 +85,6 @@ def setup_signal_handlers(self):
8085
sys.exit(1)
8186

8287
def set_backup_dirs(self):
83-
self.backup_time = datetime.now().strftime("%Y%m%d_%H%M")
8488
self.backup_root_directory = os.path.join(self.config.backup.location, self.config.backup.name)
8589
self.backup_latest_symlink = os.path.join(self.backup_root_directory, "latest")
8690
self.backup_previous_symlink = os.path.join(self.backup_root_directory, "previous")
@@ -107,11 +111,11 @@ def get_db_conn(self):
107111
def get_lock(self):
108112
# noinspection PyBroadException
109113
try:
110-
if not self.config.lockfile:
111-
self.config.lockfile = '/tmp/%s.lock' % self.program_name
112-
self.lock = Lock(self.config.lockfile)
114+
if not self.config.lock_file:
115+
self.config.lock_file = '/tmp/%s.lock' % self.program_name
116+
self.lock = Lock(self.config.lock_file)
113117
except Exception:
114-
logging.fatal("Could not acquire lock: '%s'! Is another %s process running? Exiting" % (self.config.lockfile, self.program_name))
118+
logging.fatal("Could not acquire lock: '%s'! Is another %s process running? Exiting" % (self.config.lock_file, self.program_name))
115119
self.cleanup_and_exit(None, None)
116120

117121
def release_lock(self):
@@ -180,6 +184,11 @@ def cleanup_and_exit(self, code, frame):
180184
self.db.close()
181185

182186
logging.info("Cleanup complete, exiting")
187+
188+
if self.logger:
189+
self.logger.rotate()
190+
self.logger.close()
191+
183192
self.release_lock()
184193
sys.exit(1)
185194

@@ -448,4 +457,6 @@ def run(self):
448457
self.update_symlinks()
449458
logging.info("Completed %s in %.2f sec" % (self.program_name, self.timer.duration(self.timer_name)))
450459

460+
self.logger.rotate()
461+
self.logger.close()
451462
self.release_lock()

mongodb_consistent_backup/Oplog/Tailer/Tailer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Tailer(Task):
1717
def __init__(self, manager, config, timer, base_dir, backup_dir, replsets, status_secs=15):
1818
super(Tailer, self).__init__(self.__class__.__name__, manager, config, timer, base_dir, backup_dir)
1919
self.backup_name = self.config.name
20-
self.user = self.config.user
20+
self.user = self.config.username
2121
self.password = self.config.password
2222
self.authdb = self.config.authdb
2323
self.replsets = replsets
@@ -30,9 +30,9 @@ def summary(self):
3030
return self._summary
3131

3232
def prepare_oplog_files(self, shard_name):
33-
oplog_dir = os.path.join(self.base_dir, shard_name)
33+
oplog_dir = os.path.join(self.backup_dir, shard_name)
3434
if not os.path.isdir(oplog_dir):
35-
os.makedirs(oplog_dir)
35+
os.mkdir(oplog_dir)
3636
oplog_file = os.path.join(oplog_dir, "oplog-tailed.bson")
3737
return oplog_file
3838

mongodb_consistent_backup/Replication/Replset.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ class Replset:
1111
def __init__(self, config, db):
1212
self.config = config
1313
self.db = db
14-
self.user = self.config.user
15-
self.password = self.config.password
16-
self.authdb = self.config.authdb
1714
self.max_lag_secs = self.config.replication.max_lag_secs
1815
self.min_priority = self.config.replication.min_priority
1916
self.max_priority = self.config.replication.max_priority

0 commit comments

Comments
 (0)