forked from osiegmar/aws-lambda-backup
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathebs-backup.py
126 lines (102 loc) · 4.77 KB
/
ebs-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
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
# Copyright 2017 Oliver Siegmar
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from datetime import date
import boto3
from dateutil.relativedelta import relativedelta
VERSION = '1.0.1'
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
ec2 = boto3.resource('ec2')
client = boto3.client('ec2')
regions = client.describe_regions().get('Regions',[] )
for region in regions:
reg=region['RegionName']
logger.info('Checking Region {}'.format(reg))
ec2 = boto3.resource('ec2', region_name=reg)
client = boto3.client('ec2', region_name=reg)
logger.info('Start ebs-backup v{}'.format(VERSION))
backup(ec2, client)
expire(ec2)
def backup(ec2, client):
instances = ec2.instances.filter(Filters=[{'Name': 'tag-key',
'Values': ['LambdaBackupConfiguration']}])
for instance in instances:
try:
backup_instance(instance, client)
except:
logging.exception('Error creating snapshot for {}'.format(instance.id))
def backup_instance(instance, client):
instance_tags = dict(map(lambda x: (x['Key'], x['Value']), instance.tags or []))
instance_name = instance_tags.get('Name', '[unnamed]')
backup_cfg_str = instance_tags['LambdaBackupConfiguration']
backup_cfg = parse_config(instance, instance_name, backup_cfg_str)
backup_label, retention = calc_retention(backup_cfg)
if not backup_label:
logger.info('Skip backup of instance {} ({}); LambdaBackupConfiguration is {}'
.format(instance.id, instance_name, backup_cfg_str))
return
delete_date_fmt = (date.today() + retention).strftime('%Y-%m-%d')
logger.info('Work on instance {} ({}); Create {} backups to be deleted on {}'
.format(instance.id, instance_name, backup_label, delete_date_fmt))
snapshot_ids = []
for device_mapping in instance.block_device_mappings:
volume_id = device_mapping['Ebs']['VolumeId']
device_name = device_mapping['DeviceName']
logger.info('Create snapshot of volume {} (mounted at {})'.format(volume_id, device_name))
snapshot = client.create_snapshot(VolumeId=volume_id, Description='Automatic Backup of {} {}'
.format(instance_name, device_name))
snapshot_ids.append(snapshot['SnapshotId'])
if snapshot_ids:
logger.info('Create tags for snapshots {}'.format(snapshot_ids))
tags = {
'Name': 'lambda-backup-' + instance_name,
'BackupLabel': backup_label,
'InstanceId': instance.instance_id,
'InstanceName': instance_name,
'DeleteOn': delete_date_fmt
}
tag_list = list(map(lambda kv: {'Key': kv[0], 'Value': kv[1]}, list(tags.items())))
client.create_tags(Resources=snapshot_ids, Tags=tag_list)
def parse_config(instance, instance_name, config):
try:
backup_configuration = list(map(int, config.split(',')))
if any(i < 0 for i in backup_configuration):
raise ValueError('Values must be >= 0')
return backup_configuration
except:
raise ValueError('Syntax error in LambdaBackupConfiguration of {} ({}): {}'
.format(instance.id, instance_name, config))
def calc_retention(backup_configuration):
today = date.today()
r_daily, r_weekly, r_monthly, r_yearly = backup_configuration
if today.day == 1:
if today.month == 1 and r_yearly > 0:
return 'yearly', relativedelta(years=r_yearly)
if r_monthly > 0:
return 'monthly', relativedelta(months=r_monthly)
if today.weekday() == 6 and r_weekly > 0:
return 'weekly', relativedelta(weeks=r_weekly)
if r_daily > 0:
return 'daily', relativedelta(days=r_daily)
return None, None
def expire(ec2):
delete_fmt = date.today().strftime('%Y-%m-%d')
snapshots = ec2.snapshots.filter(OwnerIds=['self'],
Filters=[{'Name': 'tag:DeleteOn', 'Values': [delete_fmt]}])
for snapshot in snapshots:
logger.info('Remove snapshot {} (of volume {}) created at {}'
.format(snapshot.id, snapshot.volume_id, snapshot.start_time))
snapshot.delete()