forked from markcaudill/haproxy-autoscale
-
Notifications
You must be signed in to change notification settings - Fork 0
/
haproxy_autoscale.py
232 lines (191 loc) · 8.41 KB
/
haproxy_autoscale.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
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
from boto.ec2 import EC2Connection, get_region
import boto.exception
import logging
import subprocess
import urllib2
from mako.template import Template
__version__ = '0.4.1'
def get_self_instance_id():
'''
Get this instance's id.
'''
logging.debug('get_self_instance_id()')
response = urllib2.urlopen('http://169.254.169.254/1.0/meta-data/instance-id')
instance_id = response.read()
return instance_id
def steal_elastic_ip(access_key=None, secret_key=None, ip=None):
'''
Assign an elastic IP to this instance.
'''
logging.debug('steal_elastic_ip()')
instance_id = get_self_instance_id()
conn = EC2Connection(aws_access_key_id=access_key,
aws_secret_access_key=secret_key)
conn.associate_address(instance_id=instance_id, public_ip=ip)
def get_running_instances(access_key=None, secret_key=None, security_group=None, region=None):
'''
Get all running instances. Only within a security group if specified.
'''
logging.debug('get_running_instances()')
instances_all_regions_list = []
if region is None:
conn = EC2Connection(aws_access_key_id=access_key,
aws_secret_access_key=secret_key)
ec2_region_list = conn.get_all_regions()
else:
ec2_region_list = [get_region(region)]
for region in ec2_region_list:
conn = EC2Connection(aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
region=region)
running_instances = []
try:
for s in conn.get_all_security_groups():
if s.name == security_group:
running_instances.extend([i for i in s.instances() if i.state == 'running'])
except boto.exception.EC2ResponseError:
logging.error('Region [' + region.name + '] inaccessible')
if running_instances:
for instance in running_instances:
instances_all_regions_list.append(instance)
return instances_all_regions_list
def file_contents(filename=None, content=None):
'''
Just return the contents of a file as a string or write if content
is specified. Returns the contents of the filename either way.
'''
logging.debug('file_contents()')
if content:
f = open(filename, 'w')
f.write(content)
f.close()
try:
f = open(filename, 'r')
text = f.read()
f.close()
except:
text = None
return text
def generate_haproxy_config(template=None, instances=None):
'''
Generate an haproxy configuration based on the template and instances list.
'''
return Template(filename=template).render(instances=instances)
def restart_haproxy(args):
'''
Restart haproxy, either by an Ubuntu service or standalone binary
'''
logging.info('Restarting haproxy.')
if args.haproxy:
# Get PID if haproxy is already running.
logging.debug('Fetching PID from %s.' % args.pid)
pid = file_contents(filename=args.pid)
command = '''%s -p %s -f %s -sf %s''' % (args.haproxy, args.pid, args.output, pid or '')
else:
command = "service %s restart" % args.servicename
logging.debug('Executing: %s' % command)
subprocess.call(command, shell=True)
"""
this class is used for the tests functionality
"""
class Backends():
# instances without these tags will be excluded from backends
required_keys = ['AppName',
'AppPort']
backend_templates = {'default': {'mode':'http',
'option':'httpchk',
'balance':'roundrobin'},
'ssl-backend': {'mode':'https',
'option':'httpchk',
'balance':'roundrobin'}}
comment = ("# Autogenerated with haproxy_autoscale version %s"
%__version__)
def get_acls(self, instances_dict, tabindent, domain, prefixes=None):
"""Generate neatly printed cfg-worthy backends for haproxy.
Args:
instances_dict: haproxy_autoscale.get_running_instances return.
tabindent: Int, number of spaces to prepend hanging config lines.
domain: Str, TLD to serve all backends from.
prefixes: List, strings to prepend to acls and backends.
Returns:
return_comment: Str, version comment information.
"""
# flatten all security group lookups into single instance list
self.all_instances = []
for instance_list in instances_dict.values():
for instance in instance_list:
self.all_instances.append(instance)
self.all_backends = []
self.included_instances = []
self.excluded_instances = []
if type(prefixes) is list:
for prefix in prefixes:
self.required_keys.append(prefix)
else:
prefixes = []
for instance in self.all_instances:
instance.missing_tags = []
for key in self.required_keys:
if instance.tags.has_key(key) is False:
instance.missing_tags.append(key)
if len(instance.missing_tags) is 0:
self.included_instances.append(instance)
app_name = instance.tags['AppName']
prefix_str = ''
if len(prefixes) > 0:
for prefix in prefixes:
prefix_str = prefix_str + "%s-" %instance.tags[prefix]
backend_name = "%s%s" %(prefix_str, app_name)
instance.tags['backend'] = backend_name
if backend_name not in self.all_backends:
self.all_backends.append(backend_name)
else:
self.excluded_instances.append(instance)
# generate acls and redirects
tabindent_str = (' ' * tabindent)
return_str = "\n%s%s" %(tabindent_str, self.comment)
for backend in self.all_backends:
return_str = return_str + ("\n%sacl %s hdr(host) -i %s.%s"
%(tabindent_str,
backend,
backend,
domain))
return_str = return_str + ("\n%suse_backend %s if %s"
%(tabindent_str,
backend,
backend))
return return_str
def generate(self, template_name, tabindent, cookie=True):
"""Iterate over all backend objects and generate default backend.
Args:
template_name: Str, a haproxy_autoscale.Backends.backend_templates.
tabindent: Int, number of spaces to prepend hanging config lines.
cookie: Bool, False to disabled sticky sessions.
Returns:
return_str: Str, formatted haproxy backend text block.
"""
template = self.backend_templates.get(template_name)
tabindent_str = (' ' * tabindent)
return_str = ''
# generate backend cfg from template
for backend in self.all_backends:
return_str = return_str + ("\n\n%s\nbackend %s"
%(self.comment, backend))
for key,value in template.iteritems():
return_str = return_str + "\n%s%s %s" %(tabindent_str,
key, value)
if cookie is True:
return_str = return_str + ("\n%scookie SERVERID insert"
" indirect nocache" %tabindent_str)
return_str = return_str + "\n"
# populate backend with instances
for instance in self.included_instances:
if instance.tags['backend'] == backend:
return_str = return_str + ("\n%sserver %s %s:%s"
%(tabindent_str,
instance.id,
instance.private_dns_name,
instance.tags['AppPort']))
if cookie is True:
return_str = return_str + " cookie %s" %(instance.id)
return return_str