1
1
#! /usr/bin/env python3
2
- import boto3
3
- import botocore
2
+
3
+ """
4
+ Small AWS Lambda function to copy RDS snapshots to another region.
5
+ """
6
+
4
7
import datetime
5
8
import re
6
9
10
+ import boto3
11
+ import botocore
12
+
7
13
# Region where the automated snapshots are located
8
14
SOURCE_REGION = "eu-west-1"
9
15
# List of RDS instances which snapshots should be copied
16
22
KEEP = 10
17
23
18
24
19
- def bySnapshotId (snap ):
25
+ class BackupException (Exception ):
26
+ """
27
+ Backup exception raised when somthing went wrong doing the backups.
28
+ """
29
+
30
+
31
+ def by_snapshot_id (snap ):
32
+ """
33
+ Returns the snapshot identifier for an RDS instance
34
+ """
20
35
return snap ["DBSnapshotIdentifier" ]
21
36
22
37
23
- def byClusterSnapshotId (snap ):
38
+ def by_cluster_snapshot_id (snap ):
39
+ """
40
+ Returns the snapshot identifier for an RDS cluster instance
41
+ """
24
42
return snap ["DBClusterSnapshotIdentifier" ]
25
43
26
44
27
- def byTimestamp (snap ):
45
+ def by_timestamp (snap ):
46
+ """
47
+ Returns the timestamp of a snapshot
48
+ """
28
49
if "SnapshotCreateTime" in snap :
29
50
return datetime .datetime .isoformat (snap ["SnapshotCreateTime" ])
30
- else :
31
- return datetime .datetime .isoformat (datetime .datetime .now ())
51
+ return datetime .datetime .isoformat (datetime .datetime .now ())
32
52
33
53
34
54
def copy_rds_snapshots (source_client , target_client , account , name ):
35
- print ("Copying snapshots for {}" .format (name ))
55
+ """
56
+ Copies snapshots from an RDS instance
57
+ """
58
+ print (f"Copying snapshots for { name } " )
36
59
source_snaps = source_client .describe_db_snapshots (SnapshotType = "automated" , DBInstanceIdentifier = name )[
37
60
"DBSnapshots"
38
61
]
39
62
if len (source_snaps ) == 0 :
40
63
print ("Found no automated snapshots. Nothing to do" )
41
64
return
42
65
43
- source_snap = sorted (source_snaps , key = bySnapshotId , reverse = True )[0 ]["DBSnapshotIdentifier" ]
44
- source_snap_arn = "arn:aws:rds:{}:{}:snapshot:{}" . format ( SOURCE_REGION , account , source_snap )
45
- target_snap_id = "copy-of-{}" . format ( re .sub (" rds:" , "" , source_snap ))
46
- print ("Will copy {} to {}" . format ( source_snap_arn , target_snap_id ) )
66
+ source_snap = sorted (source_snaps , key = by_snapshot_id , reverse = True )[0 ]["DBSnapshotIdentifier" ]
67
+ source_snap_arn = f "arn:aws:rds:{ SOURCE_REGION } :{ account } :snapshot:{ source_snap } "
68
+ target_snap_id = f "copy-of-{ re .sub (' rds:' , '' , source_snap )} "
69
+ print (f "Will copy { source_snap_arn } to { target_snap_id } " )
47
70
48
71
try :
49
72
target_client .copy_db_snapshot (
@@ -53,31 +76,34 @@ def copy_rds_snapshots(source_client, target_client, account, name):
53
76
CopyTags = True ,
54
77
SourceRegion = SOURCE_REGION ,
55
78
)
56
- except botocore .exceptions .ClientError as e :
57
- raise Exception ( "Could not issue copy command: %s" % e )
79
+ except botocore .exceptions .ClientError as ex :
80
+ raise BackupException ( f "Could not issue copy command: { ex } " ) from ex
58
81
59
82
copied_snaps = target_client .describe_db_snapshots (SnapshotType = "manual" , DBInstanceIdentifier = name )["DBSnapshots" ]
60
83
if len (copied_snaps ) > KEEP :
61
- for snap in sorted (copied_snaps , key = byTimestamp , reverse = True )[KEEP :]:
62
- print ("Will remove {}" . format ( snap [" DBSnapshotIdentifier" ]) )
84
+ for snap in sorted (copied_snaps , key = by_timestamp , reverse = True )[KEEP :]:
85
+ print (f "Will remove { snap [' DBSnapshotIdentifier' ] } " )
63
86
try :
64
87
target_client .delete_db_snapshot (DBSnapshotIdentifier = snap ["DBSnapshotIdentifier" ])
65
- except botocore .exceptions .ClientError as e :
66
- raise Exception ( "Could not delete snapshot {}: {}" . format ( snap [ "DBSnapshotIdentifier" ], e ))
88
+ except botocore .exceptions .ClientError as ex :
89
+ raise BackupException ( f "Could not delete snapshot { snap [ 'DBSnapshotIdentifier' ] } : { ex } " ) from ex
67
90
68
91
69
92
def copy_cluster_snapshots (source_client , target_client , account , cluster_name ):
70
- print ("Copying snapshots for {}" .format (cluster_name ))
93
+ """
94
+ Coppies snapshots from a RDS cluster
95
+ """
96
+ print (f"Copying snapshots for { cluster_name } " )
71
97
source_snaps = source_client .describe_db_cluster_snapshots (
72
98
SnapshotType = "automated" , DBClusterIdentifier = cluster_name
73
99
)["DBClusterSnapshots" ]
74
100
if len (source_snaps ) == 0 :
75
101
print ("Found no automated snapshots. Nothing to do" )
76
102
return
77
- source_snap = sorted (source_snaps , key = byClusterSnapshotId , reverse = True )[0 ]["DBClusterSnapshotIdentifier" ]
78
- source_snap_arn = "arn:aws:rds:{}:{}:cluster-snapshot:{}" . format ( SOURCE_REGION , account , source_snap )
79
- target_snap_id = "copy-of-{}" . format ( re .sub (" rds:" , "" , source_snap ))
80
- print ("Will copy {} to {}" . format ( source_snap_arn , target_snap_id ) )
103
+ source_snap = sorted (source_snaps , key = by_cluster_snapshot_id , reverse = True )[0 ]["DBClusterSnapshotIdentifier" ]
104
+ source_snap_arn = f "arn:aws:rds:{ SOURCE_REGION } :{ account } :cluster-snapshot:{ source_snap } "
105
+ target_snap_id = f "copy-of-{ re .sub (' rds:' , '' , source_snap )} "
106
+ print (f "Will copy { source_snap_arn } to { target_snap_id } " )
81
107
82
108
target_client .copy_db_cluster_snapshot (
83
109
SourceDBClusterSnapshotIdentifier = source_snap_arn ,
@@ -90,16 +116,22 @@ def copy_cluster_snapshots(source_client, target_client, account, cluster_name):
90
116
"DBClusterSnapshots"
91
117
]
92
118
if len (copied_snaps ) > KEEP :
93
- for snap in sorted (copied_snaps , key = byTimestamp , reverse = True )[KEEP :]:
119
+ for snap in sorted (copied_snaps , key = by_timestamp , reverse = True )[KEEP :]:
94
120
snap_id = snap ["DBClusterSnapshotIdentifier" ]
95
- print ("Will remove {}" . format ( snap_id ) )
121
+ print (f "Will remove { snap_id } " )
96
122
try :
97
123
target_client .delete_db_snapshot (DBClusterSnapshotIdentifier = snap_id )
98
- except botocore .exceptions .ClientError as e :
99
- raise Exception ( "Could not delete snapshot {}: {}" . format ( snap_id , e ))
124
+ except botocore .exceptions .ClientError as ex :
125
+ raise BackupException ( f "Could not delete snapshot { snap_id } : { ex } " ) from ex
100
126
101
127
102
128
def lambda_handler (event , context ):
129
+ """
130
+ Entrypoint for AWS Lambda
131
+ """
132
+ if context :
133
+ print (f"Starting '{ context .function_name } ' version { context .function_version } " )
134
+
103
135
target_region = event ["region" ]
104
136
account = event ["account" ]
105
137
source_client = boto3 .client ("rds" , region_name = SOURCE_REGION )
0 commit comments