-
Notifications
You must be signed in to change notification settings - Fork 1
/
02_elasticip_assigner.config
341 lines (272 loc) · 11.9 KB
/
02_elasticip_assigner.config
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
files:
"/tmp/eip-exclusion":
mode: "00555"
owner: root
group: root
encoding: plain
content: |
# DevOps Production IPs in US-WEST-2
1.1.1.1
2.2.2.2
# DevOps Production IPs in US-EAST-1
3.3.3.3
4.4.4.4
"/usr/local/PRODUCT-tools/assign-eip":
mode: "00555"
owner: root
group: root
encoding: plain
content: |
#!/usr/bin/env python
"""
Assign an Elastic IP to THIS instance, allocate new if required
-------------------------------------------------------------------
Creator: Dave North <[email protected]>
Requires: boto
"""
import os
import sys
import time
import syslog
import boto.ec2
import boto.sqs
import boto.utils
lockQueueNameFile = "/usr/local/PRODUCT-tools/EIPQUEUENAME"
def myLog(message):
procName = __file__
syslogMsg = procName + ": " + message
syslog.syslog(syslogMsg)
print '%s' % message
def getLock(queueFile,region):
# Use the SQS queue as as lock so we don't have 2 instances trying to obtain
# EIPs at the same time
queueName = None
# get the lock queue name. It's written to this file by the beanstalk resources
myLog("getLock: Reading queue name from file %s" % queueFile)
if ( os.path.exists(queueFile) ):
with open(queueFile) as f:
queueName = f.readline()
queueName = queueName.rstrip();
else:
myLog("getLock: unable to read queue name from file %s" %queueFile)
myLog("getLock: queueName %s" % queueName)
lockAcquired = None
readAttempts = 10
readDelay = 10
sqsConn = boto.sqs.connect_to_region(region)
lockQueue = sqsConn.get_queue(queueName)
if (lockQueue):
myLog("getLock: Successfully found SQS queue")
currentReadAttempt = 0
while currentReadAttempt <= readAttempts:
myLog("getLock: Reading from queue - attempt %s" % currentReadAttempt)
messages = lockQueue.get_messages(1)
if ( len(messages) == 0 ):
# We did not get the lock - sleep and try again
myLog("getLock: No messages read from queue - retrying in %i seconds" % readDelay)
time.sleep(readDelay)
else:
# We did get the lock - break out and return
myLog("getLock: message read from queue - lock acquired")
lockAcquired = True
break
currentReadAttempt +=1
else:
myLog("getLock: Unable to find SQS queue %s in region %s" % (queueName,region))
return lockAcquired
####################### MAINLINE ######################################
myLog("Elastic IP Assigner starts....")
# get our current region and instance ID for "this" instance
region = boto.utils.get_instance_metadata()['placement']['availability-zone'][:-1]
current_instance_id = boto.utils.get_instance_metadata()['instance-id']
conn = boto.ec2.connect_to_region(region)
exclusionsFile = ""
# Get the file containing any EIPs that should not excluded
# needed because we have some reserved IPs in production
if ( len(sys.argv) == 2 ):
exclusionsFile = sys.argv[1]
myLog("Reading elastic IP exclusions from file: %s" % exclusionsFile)
elasticExclusions = {}
if (exclusionsFile):
with open(exclusionsFile) as f:
for line in f:
if (line[0] == "#"):
continue
key = line.rstrip()
elasticExclusions[key] = 1
# Obtain a lock
if ( getLock(lockQueueNameFile,region) ):
myLog("Successfully obtained lock from SQS queue")
# Get a list of all elastic IPs
all_region_eips = conn.get_all_addresses()
proceedWithAssignment = True
elasticIP = ""
for eip in all_region_eips:
if ( eip.domain == "standard" ):
if (eip.instance_id == current_instance_id):
# There is already an EIP assigned to this instance
myLog("This instance is already using elastic IP: %s" % eip.public_ip)
elasticIP = eip.public_ip
proceedWithAssignment = False
if ( (not eip.instance_id) and (not elasticIP) ):
# this is an unallocated address
if (eip.public_ip in elasticExclusions):
myLog("Elastic IP %s found unallocated but is on the exclusion list - skipping" % eip.public_ip)
else:
myLog("Found an unallocated candidate elastic IP to use: %s" % eip.public_ip)
elasticIP = eip.public_ip
# See if we need to provision a new elastic IP
if ( not elasticIP ) :
myLog("No unallocated elastic IP addresses - provisioning new")
try:
newElasticIP = conn.allocate_address()
elasticIP = newElasticIP.public_ip
myLog("Provisioned new elastic IP: %s" % elasticIP)
except Exception as e:
myLog("Failed to provision new elastic IP: %s" % e)
proceedWithAssignment = False
# Now associate the address with this instance
if (proceedWithAssignment):
myLog("Assigning elastic IP: %s" % elasticIP)
result = conn.associate_address(current_instance_id, elasticIP)
if (result):
myLog("Successfully associated elastic IP %s with instanceID %s" % (elasticIP,current_instance_id))
else:
myLog("Not proceeding with elastic IP assignment - elastic IP already assigned")
else:
myLog("Unable to obtain SQS queue lock - no EIP assignment performed")
"/tmp/eip-queue-keepalive":
mode: "00555"
owner: root
group: root
encoding: plain
content: |
#!/usr/bin/env python
"""
Read a message from a queue and then re-post
Needed as part of the elasticIP manager as messages are expired from
queues after 4 days (default)
-------------------------------------------------------------------
Creator: Dave North <[email protected]>
Requires: boto
"""
import os
import sys
import time
import datetime
import syslog
import boto.sqs
import boto.sqs.message
from boto.exception import SQSError
import boto.utils
lockQueueNameFile = "/usr/local/PRODUCT-tools/EIPQUEUENAME"
def myLog(message):
procName = __file__
syslogMsg = procName + ": " + message
syslog.syslog(syslogMsg)
print '%s' % message
def getQueueName(queueFile):
queueName = None
# get the lock queue name. It's written to this file by the beanstalk resources
myLog("getQueueName: Reading queue name from file %s" % queueFile)
if ( os.path.exists(queueFile) ):
with open(queueFile) as f:
queueName = f.readline()
queueName = queueName.rstrip()
else:
myLog("getQueueName: unable to read queue name from file %s" %queueFile)
myLog("getQueueName: Found SQS queue name: %s" % queueName)
return queueName
def initialSeed(queueFile,region,instID):
# post the initial seed message
queueName = getQueueName(queueFile)
retVal = None
sqsConn = boto.sqs.connect_to_region(region)
lockQueue = sqsConn.get_queue(queueName)
if (lockQueue):
datetimeStr = datetime.datetime.now().strftime("%I:%M%p %B %d, %Y")
body = "initial seed message generated by " + instID + " on " + datetimeStr
myMessage = boto.sqs.message.RawMessage()
myMessage.set_body(body)
try:
myLog("initialSeed: Writing new message to SQS queue")
lockQueue.write(myMessage)
retVal = True
except SQSError, e:
myLog("initialSeed: Unable to write message to SQS queue")
myLog("initialSeed: Error %s - %s" % (e.code, str(e)))
else:
myLog("initialSeed: Unable to find SQS queue %s in region %s" % (queueName,region))
return retVal
def replaceMessage(queueFile,region,instID):
# get a messsage from the queue, delete it and add a new one
queueName = getQueueName(queueFile)
myLog("replaceMessage: queueName %s" % queueName)
retVal = None
readAttempts = 10
readDelay = 10
sqsConn = boto.sqs.connect_to_region(region)
lockQueue = sqsConn.get_queue(queueName)
if (lockQueue):
myLog("replaceMessage: Successfully found SQS queue")
currentReadAttempt = 0
while currentReadAttempt <= readAttempts:
myLog("replaceMessage: Reading from queue - attempt %s" % currentReadAttempt)
messages = lockQueue.get_messages(1)
if ( len(messages) == 0 ):
# We did not get the message - sleep and try again
myLog("replaceMessage: No messages read from queue - retrying in %i seconds" % readDelay)
time.sleep(readDelay)
else:
# We did get the message - delete it and post a new one
myLog("replaceMessage: message read from queue")
if ( lockQueue.delete_message(messages[0]) ):
myLog("replaceMessage: Successfully removed lock message from queue")
datetimeStr = datetime.datetime.now().strftime("%I:%M%p %B %d, %Y")
body = "Keepalive message generated by " + instID + " on " + datetimeStr
myMessage = boto.sqs.message.RawMessage()
myMessage.set_body(body)
try:
myLog("replaceMessage: Writing new message to SQS queue")
lockQueue.write(myMessage)
retVal = True
break
except SQSError, e:
myLog("Unable to write message to SQS queue")
myLog("Error %s - %s" % (e.code, str(e)))
else:
myLog("replaceMessage: Error removing lock message from queue")
currentReadAttempt +=1
else:
myLog("replaceMessage: Unable to find SQS queue %s in region %s" % (queueName,region))
return retVal
####################### MAINLINE ######################################
myLog("Queue Message Poster process starts....")
# get our current region and instance ID for "this" instance
region = boto.utils.get_instance_metadata()['placement']['availability-zone'][:-1]
current_instance_id = boto.utils.get_instance_metadata()['instance-id']
# do we need to seed the queue with the inital message?
action = None
if ( len(sys.argv) == 2 ):
action = sys.argv[1]
if (action == "SEED"):
if (initialSeed(lockQueueNameFile,region,current_instance_id) ):
myLog("Successfully posted seed message to SQS queue from instance %s" % current_instance_id)
sys.exit(0)
else:
myLog("Unable to post seed message to SQS queue from instance %s" % current_instance_id)
sys.exit(1)
else:
if ( replaceMessage(lockQueueNameFile,region,current_instance_id) ):
myLog("Successfully posted new message to SQS queue from instance %s" % current_instance_id)
sys.exit(0)
else:
myLog("Unable to post keepalive message to SQS queue from instance %s" % current_instance_id)
sys.exit(1)
container_commands:
01_assign_elastic_ip:
command: "/usr/local/PRODUCT-tools/assign-eip /tmp/eip-exclusion"
02_sleep_elastic_ip:
command: "/bin/sleep 10"
03_add_cron_task:
command: "mv -f /tmp/eip-queue-keepalive /etc/cron.daily/eip-queue-keepalive"