Skip to content

Commit ffdadbd

Browse files
Merge branch 'master' into 1.2.0-read_pref_tags
2 parents be3ce3f + ea5626a commit ffdadbd

25 files changed

+378
-144
lines changed

README.rst

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ Features
3131
archiving method (*optional*)
3232
- `AWS S3 <https://aws.amazon.com/s3/>`__ Secure Multipart backup uploads (*optional*)
3333
- `Google Cloud Storage <https://cloud.google.com/storage/>`__ Secure backup uploads (*optional*)
34+
- Rsync (over SSH) secure backup uploads (*optional*)
3435
- `Nagios NSCA <https://sourceforge.net/p/nagios/nsca>`__ push
3536
notification support (*optional*)
3637
- Modular backup, archiving, upload and notification components
38+
- Support for MongoDB Authentication and SSL database connections
3739
- Multi-threaded, single executable
3840
- Auto-scales to number of available CPUs by default
3941

@@ -77,6 +79,7 @@ To build an CentOS/RedHat RPM of the tool *(recommended)*:
7779
::
7880

7981
$ cd /path/to/mongodb_consistent_backup
82+
$ sudo yum install -y rpm-build
8083
$ make rpm
8184

8285
To build and install from source *(to default '/usr/local/bin/mongodb-consistent-backup')*:
@@ -148,25 +151,40 @@ The backups are `mongorestore <https://docs.mongodb.com/manual/reference/program
148151
...
149152
$ mongorestore --host mongod12.example.com --port 27017 -u admin -p 123456 --oplogReplay --dir /var/lib/mongodb-consistent-backup/default/20170424_0000/rs0/dump
150153

151-
Run as Docker Container (Experimental)
154+
Run as Docker Container
152155
~~~~~~~~~~~~~~~~~~~~~~~
153156

154-
Note: you need to use persistent volumes to store backups and/or config files long-term when using Docker. Data in Docker containers is destroyed when the container is deleted. See `scripts/docker-persistent.sh <scripts/docker-persistent.sh>`__ and `scripts/docker-persistent.example.conf <scripts/docker-persistent.example.conf>`__ as an example/demo of how to implement persistence.
157+
To persist logs, configs and backup data 3 directories should be mapped to be inside the Docker containter.
158+
159+
The 'docker run' command -v/--volume flags in the examples below map container paths to paths on your Docker host. The example below assumes there is a path on the Docker host named *'/data/mongobackup'* with *'data'*, *'conf'* and *'logs'* subdirectories mapped to inside the container. Replace any instance of *'/data/mongobackup'* below to a different path if necessary.
160+
161+
*Note: store a copy of your mongodb-consistent-backup.conf in the 'conf' directory and pass it's container path as the --config= flag if you wish to use config files.*
155162

156163
**Via Docker Hub**
157164

158165
::
159166

160-
$ docker run -i timvaillancourt/mongodb_consistent_backup <mongodb_consistent_backup-flags>
167+
$ mkdir -p /data/mongobackup/{conf,data,logs}
168+
$ cp -f /path/to/mongodb-consistent-backup.conf /data/mongobackup/conf
169+
$ docker run -it \
170+
-v "/data/mongobackup/conf:/conf:Z" \
171+
-v "/data/mongobackup/data:/var/lib/mongodb-consistent-backup:Z" \
172+
-v "/data/mongobackup/logs:/var/log/mongodb-consistent-backup:Z" \
173+
timvaillancourt/mongodb_consistent_backup:latest --config=/conf/mongodb-consistent-backup.conf
161174

162175
**Build and Run Docker Image**
163176

164177
::
165178

166179
$ cd /path/to/mongodb_consistent_backup
167180
$ make docker
168-
$ docker run -t mongodb_consistent_backup <mongodb_consistent_backup-flags>
169-
181+
$ mkdir -p /data/mongobackup/{conf,data,logs}
182+
$ cp -f /path/to/mongodb-consistent-backup.conf /data/mongobackup/conf
183+
$ docker run -it \
184+
-v "/data/mongobackup/conf:/conf:Z" \
185+
-v "/data/mongobackup/data:/var/lib/mongodb-consistent-backup:Z" \
186+
-v "/data/mongobackup/logs:/var/log/mongodb-consistent-backup:Z" \
187+
mongodb_consistent_backup --config=/conf/mongodb-consistent-backup.conf
170188

171189
ZBackup Archiving (Optional)
172190
~~~~~~~

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.1.0
1+
1.2.0

conf/mongodb-consistent-backup.example.conf

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ production:
1717
location: /var/lib/mongodb-consistent-backup
1818
# mongodump:
1919
# binary: [path] (default: /usr/bin/mongodump)
20-
# compression: [auto|none|gzip] (default: auto - enable gzip if supported)
21-
# threads: [1-16] (default: auto-generated - shards/cpu)
20+
# compression: [auto|none|gzip] (default: auto = enable gzip if supported)
21+
# threads: [1-16] (default: auto-generated, shards/cpu)
2222
#replication:
2323
# max_lag_secs: [1+] (default: 10)
2424
# min_priority: [0-999] (default: 0)
@@ -30,18 +30,19 @@ production:
3030
# wait_secs: [1+] (default: 300)
3131
# ping_secs: [1+] (default: 3)
3232
#oplog:
33-
# compression: [none|gzip] (default: gzip - if gzip is used by backup stage)
33+
# compression: [none|gzip] (default: gzip, if used by backup stage)
3434
# flush:
35-
# max_docs: 100
36-
# max_secs: 1
37-
# resolver_threads: [1+] (default: 2 per CPU)
35+
# max_docs: [1+] (default: 100)
36+
# max_secs: [1+] (default: 1)
37+
# resolver:
38+
# threads: [1+] (default: 2 per CPU)
3839
# tailer:
3940
# enabled: true
4041
# status_interval: 30
4142
archive:
4243
method: tar
4344
# tar:
44-
# compression: [none|gzip] (default: gzip - none if backup is already compressed)
45+
# compression: [none|gzip] (default: gzip, none if backup already compressed)
4546
# threads: [1+] (default: 1 per CPU)
4647
# zbackup:
4748
# binary: [path] (default: /usr/bin/zbackup)
@@ -59,21 +60,26 @@ production:
5960
upload:
6061
method: none
6162
# remove_uploaded: [true|false] (default: false)
63+
# retries: [1+] (default: 5)
64+
# threads: [1+] (default: 4)
6265
# gs:
6366
# project_id: [Google Cloud Project ID]
64-
# access_key: [Google Cloud Storage Access Key]
65-
# secret_key: [Google Cloud Storage Secret Key]
67+
# access_key: [Google Cloud Storage Interoperability API Access Key]
68+
# secret_key: [Google Cloud Storage Interoperability API Secret Key]
6669
# bucket_name: [Google Cloud Storage Bucket Name]
6770
# bucket_prefix: [prefix] (default: /)
68-
# threads: [1+] (default: 1 per CPU)
71+
# rsync:
72+
# path: [Rsync Destination Path]
73+
# user: [SSH Username]
74+
# host: [SSH Hostname/IP]
75+
# port: [SSH Port Number] (default: 22)
76+
# delete: [true|false] (default: false)
6977
# s3:
7078
# region: [AWS S3 Region] (default: us-east-1)
7179
# access_key: [AWS S3 Access Key]
7280
# secret_key: [AWS S3 Secret Key]
7381
# bucket_name: [AWS S3 Bucket Name]
7482
# bucket_prefix: [prefix] (default: /)
75-
# threads: [1+] (default: 1 per CPU)
7683
# chunk_size_mb: [1+] (default: 50)
7784
# secure: [true|false] (default: true)
78-
# retries: [1+] (default: 5)
7985
# acl: [acl_str] (default: none)

mongodb_consistent_backup/Backup/Mongodump/Mongodump.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import os
22
import logging
3-
import sys
43

54
from math import floor
65
from subprocess import check_output
@@ -57,22 +56,21 @@ def parse_mongodump_version(self):
5756
raise OperationError("Could not parse mongodump --version output!")
5857

5958
def choose_compression(self):
60-
if self.can_gzip():
59+
if self.can_compress():
6160
if self.compression() == 'auto':
6261
logging.info("Mongodump binary supports gzip compression, auto-enabling gzip compression")
6362
self.compression('gzip')
6463
elif self.compression() == 'gzip':
6564
raise OperationError("mongodump gzip compression requested on binary that does not support gzip!")
6665

67-
def can_gzip(self):
66+
def can_compress(self):
6867
if os.path.isfile(self.binary) and os.access(self.binary, os.X_OK):
69-
logging.debug("Mongodump binary supports gzip")
68+
logging.debug("Mongodump binary supports gzip compression")
7069
if tuple("3.2.0".split(".")) <= tuple(self.version.split(".")):
7170
return True
7271
return False
7372
else:
74-
logging.fatal("Cannot find or execute the mongodump binary file %s!" % self.binary)
75-
sys.exit(1)
73+
raise OperationError("Cannot find or execute the mongodump binary file %s!" % self.binary)
7674

7775
def summary(self):
7876
return self._summary

mongodb_consistent_backup/Common/Config.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,13 @@ def check_required(self):
105105
try:
106106
self._get(key)
107107
except:
108-
raise mongodb_consistent_backup.Errors.OperationError('Field "%s" must be set via command-line or config file!' % key)
108+
raise mongodb_consistent_backup.Errors.OperationError(
109+
'Field "%s" (config file field: "%s.%s") must be set via command-line or config file!' % (
110+
key,
111+
self._config.environment,
112+
key
113+
)
114+
)
109115

110116
def parse(self):
111117
self._config.parse(self.cmdline)

mongodb_consistent_backup/Common/DB.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ def __init__(self, uri, config, do_replset=False, read_pref='primaryPreferred',
4040
self.ssl_client_cert_file = self.config.ssl.client_cert_file
4141
self.read_pref_tags = self.config.replication.read_pref_tags
4242

43+
self.username = self.config.username
44+
self.password = self.config.password
45+
self.authdb = self.config.authdb
46+
self.ssl_ca_file = self.config.ssl.ca_file
47+
self.ssl_crl_file = self.config.ssl.crl_file
48+
self.ssl_client_cert_file = self.config.ssl.client_cert_file
49+
4350
self.replset = None
4451
self._conn = None
4552
self._is_master = None

mongodb_consistent_backup/Common/Util.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import socket
22

33
from dateutil import parser
4+
from select import select
45

56
from mongodb_consistent_backup.Errors import OperationError
67

@@ -31,3 +32,23 @@ def validate_hostname(hostname):
3132
socket.getaddrinfo(hostname, None)
3233
except socket.error, e:
3334
raise OperationError("Could not resolve host '%s', error: %s" % (hostname, e))
35+
36+
37+
def wait_popen(process, stderr_callback, stdout_callback):
38+
try:
39+
while not process.returncode:
40+
poll = select([process.stderr.fileno(), process.stdout.fileno()], [], [])
41+
if len(poll) >= 1:
42+
for fd in poll[0]:
43+
if process.stderr and fd == process.stderr.fileno():
44+
stderr_callback(process.stderr.readline().rstrip())
45+
if process.stdout and fd == process.stdout.fileno():
46+
stdout_callback(process.stdout.readline().rstrip())
47+
if process.poll() is not None:
48+
break
49+
stderr, stdout = process.communicate()
50+
stderr_callback(stderr.rstrip())
51+
stdout_callback(stdout.rstrip())
52+
except Exception, e:
53+
raise e
54+
return True

mongodb_consistent_backup/Common/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
from Lock import Lock # NOQA
55
from MongoUri import MongoUri # NOQA
66
from Timer import Timer # NOQA
7-
from Util import config_to_string, is_datetime, parse_method, validate_hostname # NOQA
7+
from Util import config_to_string, is_datetime, parse_method, validate_hostname, wait_popen # NOQA

mongodb_consistent_backup/Pipeline/Stage.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def close(self):
6464
def is_compressed(self):
6565
if self.has_task() and hasattr(self._task, "is_compressed"):
6666
return self._task.is_compressed()
67+
return False
6768

6869
def compression(self, task=None):
6970
if self.has_task() and hasattr(self._task, "compression"):

mongodb_consistent_backup/Pipeline/Task.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ def compression(self, method=None):
3939
return parse_method(self.compression_method)
4040

4141
def is_compressed(self):
42-
if self.compression() != 'none':
42+
if self.compression() == 'auto' and hasattr(self, "can_compress"):
43+
return self.can_compress()
44+
elif self.compression() != 'none':
4345
return True
4446
return False
4547

0 commit comments

Comments
 (0)