1
+ import boto3
2
+ import botocore
3
+ import click
4
+
5
+ session = boto3 .Session (profile_name = 'shotty' )
6
+ ec2 = session .resource ('ec2' )
7
+
8
+ def filter_instances (project ):
9
+ instances = []
10
+
11
+ if project :
12
+ filters = [{'Name' :'tag:Project' , 'Values' :[project ]}]
13
+ instances = ec2 .instances .filter (Filters = filters )
14
+ else :
15
+ instances = ec2 .instances .all ()
16
+
17
+ return instances
18
+
19
+ def has_pending_snapshot (volume ):
20
+ snapshots = list (volume .snapshots .all ())
21
+ return snapshots and snapshots [0 ].state == 'pending'
22
+
23
+ @click .group ()
24
+ def cli ():
25
+ """Shotty manages snapshots"""
26
+
27
+ @cli .group ('snapshots' )
28
+ def snapshots ():
29
+ """Commands for snapshots"""
30
+
31
+ @snapshots .command ('list' )
32
+ @click .option ('--project' , default = None ,
33
+ help = "Only snapshots for project (tag Project:<name>)" )
34
+ @click .option ('--all' , 'list_all' , default = False , is_flag = True ,
35
+ help = "List all snapshots for each volume, not just the most recent" )
36
+ def list_snapshots (project , list_all ):
37
+ "List EC2 snapshots"
38
+
39
+ instances = filter_instances (project )
40
+
41
+ for i in instances :
42
+ for v in i .volumes .all ():
43
+ for s in v .snapshots .all ():
44
+ print (", " .join ((
45
+ s .id ,
46
+ v .id ,
47
+ i .id ,
48
+ s .state ,
49
+ s .progress ,
50
+ s .start_time .strftime ("%c" )
51
+ )))
52
+
53
+ if s .state == 'completed' and not list_all : break
54
+
55
+ return
56
+
57
+ @cli .group ('volumes' )
58
+ def volumes ():
59
+ """Commands for volumes"""
60
+
61
+ @volumes .command ('list' )
62
+ @click .option ('--project' , default = None ,
63
+ help = "Only volumes for project (tag Project:<name>)" )
64
+ def list_volumes (project ):
65
+ "List EC2 volumes"
66
+
67
+ instances = filter_instances (project )
68
+
69
+ for i in instances :
70
+ for v in i .volumes .all ():
71
+ print (", " .join ((
72
+ v .id ,
73
+ i .id ,
74
+ v .state ,
75
+ str (v .size ) + "GiB" ,
76
+ v .encrypted and "Encrypted" or "Not Encrypted"
77
+ )))
78
+
79
+ return
80
+
81
+ @cli .group ('instances' )
82
+ def instances ():
83
+ """Commands for instances"""
84
+
85
+ @instances .command ('snapshot' ,
86
+ help = "Create snapshots of all volumes" )
87
+ @click .option ('--project' , default = None ,
88
+ help = "Only instances for project (tag Project:<name>)" )
89
+ def create_snapshots (project ):
90
+ "Create snapshots for EC2 instances"
91
+
92
+ instances = filter_instances (project )
93
+
94
+ for i in instances :
95
+ print ("Stopping {0}..." .format (i .id ))
96
+
97
+ i .stop ()
98
+ i .wait_until_stopped ()
99
+
100
+ for v in i .volumes .all ():
101
+ if has_pending_snapshot (v ):
102
+ print (" Skipping {0}, snapshot already in progress" .format (v .id ))
103
+ continue
104
+
105
+ print (" Creating snapshot of {0}" .format (v .id ))
106
+ v .create_snapshot (Description = "Created by SnapshotAlyzer 30000" )
107
+
108
+ print ("Starting {0}..." .format (i .id ))
109
+
110
+ i .start ()
111
+ i .wait_until_running ()
112
+
113
+ print ("Job's done!" )
114
+
115
+ return
116
+
117
+ @instances .command ('list' )
118
+ @click .option ('--project' , default = None ,
119
+ help = "Only instances for project (tag Project:<name>)" )
120
+ def list_instances (project ):
121
+ "List EC2 instances"
122
+
123
+ instances = filter_instances (project )
124
+
125
+ for i in instances :
126
+ tags = { t ['Key' ]: t ['Value' ] for t in i .tags or [] }
127
+ print (', ' .join ((
128
+ i .id ,
129
+ i .instance_type ,
130
+ i .placement ['AvailabilityZone' ],
131
+ i .state ['Name' ],
132
+ i .public_dns_name ,
133
+ tags .get ('Project' , '<no project>' )
134
+ )))
135
+
136
+ return
137
+
138
+ @instances .command ('stop' )
139
+ @click .option ('--project' , default = None ,
140
+ help = 'Only instances for project' )
141
+ def stop_instances (project ):
142
+ "Stop EC2 instances"
143
+
144
+ instances = filter_instances (project )
145
+
146
+ for i in instances :
147
+ print ("Stopping {0}..." .format (i .id ))
148
+ try :
149
+ i .stop ()
150
+ except botocore .exceptions .ClientError as e :
151
+ print (" Could not stop {0}. " .format (i .id ) + str (e ))
152
+ continue
153
+
154
+ return
155
+
156
+ @instances .command ('start' )
157
+ @click .option ('--project' , default = None ,
158
+ help = 'Only instances for project' )
159
+ def start_instances (project ):
160
+ "Start EC2 instances"
161
+
162
+ instances = filter_instances (project )
163
+
164
+ for i in instances :
165
+ print ("Starting {0}..." .format (i .id ))
166
+ try :
167
+ i .start ()
168
+ except botocore .exceptions .ClientError as e :
169
+ print (" Could not start {0}. " .format (i .id ) + str (e ))
170
+ continue
171
+
172
+ return
173
+
174
+ if __name__ == '__main__' :
175
+ cli ()
0 commit comments