-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbackup.py
95 lines (84 loc) · 3.05 KB
/
backup.py
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
"""Finds an EC2 instance running a MongoDB secondary and snapshots it"""
from boto import ec2
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure
import settings, reporting
def get_connection():
"""Connects to AWS using credentials from settings.py"""
return ec2.connection.EC2Connection(
aws_access_key_id=settings.AWS_ACCESS_KEY,
aws_secret_access_key=settings.AWS_SECRET_KEY
)
def get_mongo_url(host):
"""Gets Mongo URL from EC2 instance and credentials in settings.py"""
return "mongodb://" + \
settings.MONGO_USERNAME + ":" + \
settings.MONGO_PASSWORD + "@" + \
host + settings.MONGO_PORT + "/" + \
settings.MONGO_DATABASE
def is_secondary(instance):
"""Checks if EC2 instance is a seconday member of a replica set"""
public_dns = instance.public_dns_name
try:
client = MongoClient(
host=get_mongo_url(public_dns)
)
except ConnectionFailure:
return False
if client.is_primary:
client.disconnect()
return False
client.disconnect()
return True
def get_volume_from_instance(instance, connection):
"""Gets the EBS volume attached to the EC2 instance"""
if len(instance.block_device_mapping) != 1:
return False
mappings = connection.get_instance_attribute(
instance_id=instance.id,
attribute='blockDeviceMapping'
)['blockDeviceMapping']
_, volume = mappings.popitem()
return volume.volume_id
def get_volume(connection):
"""Finds an EC2 instance that is seconday member and returns its EBS volume"""
reservations = connection.get_all_reservations(filters=settings.INSTANCE_FILTERS)
for reservation in reservations:
for instance in reservation.instances:
if is_secondary(instance):
volume = get_volume_from_instance(instance, connection)
if volume:
return volume
return None
def make_snapshot(connection):
"""Finds appropriate volume and snapshots it"""
volume = get_volume(connection)
if volume is None:
reporting.error("Could not find a volume to backup")
exit(1)
try:
connection.create_snapshot(
volume,
description="Automatically created by backup daemon"
)
except:
reporting.error("Error saving snapshot")
return
reporting.success("Snapshot saved of volume " + volume)
def trim_snapshots(connection):
"""Removes old snapshots based on retention settings from settings.py"""
try:
return connection.trim_snapshots(
hourly_backups=settings.RETAIN_HOURLY,
daily_backups=settings.RETAIN_DAILY,
monthly_backups=settings.RETAIN_MONTHLY
)
except:
reporting.error("Error trimming snapshots")
def backup():
"""Finds and snapshots a running secondary and removes stale snapshots"""
connection = get_connection()
make_snapshot(connection)
trim_snapshots(connection)
if __name__ == "__main__":
backup()