3030import time
3131
3232import boto
33- from fabric .api import *
3433import paramiko
3534
3635EC2_INSTANCE_TYPE = 'm1.small'
3938
4039# Load state from file
4140
41+ instance_ids = []
4242if os .path .isfile (STATE_FILENAME ):
4343with open (STATE_FILENAME , 'r' ) as f :
4444text = f .read ()
45- env .instance_ids = text .split ('\n ' )
46-
47- print 'Read %i bees from the roster.' % len (env .instance_ids )
48- else :
49- env .instance_ids = []
45+ instance_ids = text .split ('\n ' )
46+
47+ print 'Read %i bees from the roster.' % len (instance_ids )
5048
5149# Utilities
5250
5351def _write_server_list (instances ):
5452with open (STATE_FILENAME , 'w' ) as f :
5553f .write ('\n ' .join ([instance .id for instance in instances ]))
56-
54+
5755# Methods
5856
5957def up (count = 5 , group = 'staging' , zone = 'us-east-1d' ):
6058"""
6159 Startup the load testing server.
6260 """
63- if env . instance_ids :
61+ if instance_ids :
6462print 'Bees are already assembled and awaiting orders.'
6563return
66-
64+
6765count = int (count )
68-
66+
6967print 'Connecting to the hive.'
70-
68+
7169ec2_connection = boto .connect_ec2 ()
72-
70+
7371print 'Attempting to call up %i bees.' % count
74-
72+
7573reservation = ec2_connection .run_instances (
7674image_id = 'ami-ff17fb96' ,
7775min_count = count ,
@@ -80,113 +78,113 @@ def up(count=5, group='staging', zone='us-east-1d'):
8078security_groups = [group ],
8179instance_type = EC2_INSTANCE_TYPE ,
8280placement = zone )
83-
81+
8482print 'Waiting for bees to load their machine guns...'
85-
83+
8684for instance in reservation .instances :
8785while instance .state != 'running' :
8886print '.'
8987time .sleep (5 )
9088instance .update ()
91-
89+
9290print 'Bee %s is ready for the attack.' % instance .id
93-
91+
9492_write_server_list (reservation .instances )
95-
93+
9694print 'The swarm has assembled %i bees.' % len (reservation .instances )
97-
95+
9896def report ():
9997"""
10098 Report the status of the load testing servers.
101- """
102- if not env . instance_ids :
99+ """
100+ if not instance_ids :
103101print 'No bees have been mobilized.'
104102return
105103
106104ec2_connection = boto .connect_ec2 ()
107-
108- reservations = ec2_connection .get_all_instances (instance_ids = env . instance_ids )
105+
106+ reservations = ec2_connection .get_all_instances (instance_ids = instance_ids )
109107
110108instances = []
111109
112110for reservation in reservations :
113111instances .extend (reservation .instances )
114-
112+
115113for instance in instances :
116114print 'Bee %s: %s' % (instance .id , instance .state )
117-
115+
118116def down ():
119117"""
120118 Shutdown the load testing server.
121119 """
122- if not env . instance_ids :
120+ if not instance_ids :
123121print 'No bees have been mobilized.'
124122return
125-
123+
126124print 'Connecting to the hive.'
127125
128126ec2_connection = boto .connect_ec2 ()
129-
127+
130128print 'Calling off the swarm.'
131-
129+
132130terminated_instance_ids = ec2_connection .terminate_instances (
133- instance_ids = env . instance_ids )
134-
131+ instance_ids = instance_ids )
132+
135133print 'Stood down %i bees.' % len (terminated_instance_ids )
136-
134+
137135os .remove (STATE_FILENAME )
138-
136+
139137def _attack (params ):
140138"""
141139 Test the target URL with requests.
142-
140+
143141 Intended for use with multiprocessing.
144142 """
145143print 'Bee %i is joining the swarm.' % params ['i' ]
146-
144+
147145client = paramiko .SSHClient ()
148146client .set_missing_host_key_policy (paramiko .AutoAddPolicy ())
149147client .connect (
150148params ['instance_name' ],
151149username = 'newsapps' ,
152150key_filename = '/Users/sk/.ssh/frakkingtoasters.pem' )
153-
151+
154152print 'Bee %i is firing his machine gun. Bang bang!' % params ['i' ]
155-
153+
156154stdin , stdout , stderr = client .exec_command ('ab -r -n %(num_requests)s -c %(concurrent_requests)s -C "sessionid=NotARealSessionID" %(url)s' % params )
157-
155+
158156response = {}
159-
157+
160158ab_results = stdout .read ()
161159ms_per_request_search = re .search ('Time\ per\ request:\s+([0-9.]+)\ \[ms\]\ \(mean\)' , ab_results )
162-
160+
163161if not ms_per_request_search :
164162print 'Bee %i lost sight of the target (connection timed out).' % params ['i' ]
165163return None
166-
167- requests_per_second_search = re .search ('Requests\ per\ second:\s+([0-9.]+)\ \[#\/sec\]\ \(mean\)' , ab_results )
164+
165+ requests_per_second_search = re .search ('Requests\ per\ second:\s+([0-9.]+)\ \[#\/sec\]\ \(mean\)' , ab_results )
168166fifty_percent_search = re .search ('\s+50\%\s+([0-9]+)' , ab_results )
169167ninety_percent_search = re .search ('\s+90\%\s+([0-9]+)' , ab_results )
170168complete_requests_search = re .search ('Complete\ requests:\s+([0-9]+)' , ab_results )
171-
169+
172170response ['ms_per_request' ] = float (ms_per_request_search .group (1 ))
173171response ['requests_per_second' ] = float (requests_per_second_search .group (1 ))
174172response ['fifty_percent' ] = float (fifty_percent_search .group (1 ))
175173response ['ninety_percent' ] = float (ninety_percent_search .group (1 ))
176174response ['complete_requests' ] = float (complete_requests_search .group (1 ))
177-
175+
178176print 'Bee %i is out of ammo.' % params ['i' ]
179-
177+
180178client .close ()
181-
179+
182180return response
183-
181+
184182def _print_results (results ):
185183"""
186184 Print summarized load-testing results.
187185 """
188186incomplete_results = [r for r in results if r is None ]
189-
187+
190188if incomplete_results :
191189print ' Target failed to fully respond to %i bees.' % len (incomplete_results )
192190
@@ -197,19 +195,19 @@ def _print_results(results):
197195complete_results = [r ['requests_per_second' ] for r in results if r is not None ]
198196mean_requests = sum (complete_results )
199197print ' Requests per second:\t %f [#/sec] (mean)' % mean_requests
200-
198+
201199complete_results = [r ['ms_per_request' ] for r in results if r is not None ]
202200mean_response = sum (complete_results ) / len (complete_results )
203201print ' Time per request:\t \t %f [ms] (mean)' % mean_response
204-
202+
205203complete_results = [r ['fifty_percent' ] for r in results if r is not None ]
206204mean_fifty = sum (complete_results ) / len (complete_results )
207205print ' 50%% response time:\t \t %f [ms] (mean)' % mean_fifty
208-
206+
209207complete_results = [r ['ninety_percent' ] for r in results if r is not None ]
210208mean_ninety = sum (complete_results ) / len (complete_results )
211209print ' 90%% response time:\t \t %f [ms] (mean)' % mean_ninety
212-
210+
213211if mean_response < 500 :
214212print 'Mission Assessment: Target crushed bee offensive.'
215213elif mean_response < 1000 :
@@ -225,31 +223,31 @@ def attack(url, n=10000, c=100):
225223"""
226224 Test the root url of this site.
227225 """
228- if not env . instance_ids :
226+ if not instance_ids :
229227print 'No bees are ready to attack.'
230228return
231-
229+
232230print 'Connecting to the hive.'
233231
234232ec2_connection = boto .connect_ec2 ()
235233
236234print 'Assembling bees.'
237235
238- reservations = ec2_connection .get_all_instances (instance_ids = env . instance_ids )
239-
236+ reservations = ec2_connection .get_all_instances (instance_ids = instance_ids )
237+
240238instances = []
241-
239+
242240for reservation in reservations :
243241instances .extend (reservation .instances )
244-
242+
245243instance_count = len (instances )
246244requests_per_instance = int (n ) / instance_count
247245connections_per_instance = int (c ) / instance_count
248-
246+
249247print 'Each of %i bees will fire %s rounds, %s at a time.' % (instance_count , requests_per_instance , connections_per_instance )
250-
248+
251249params = []
252-
250+
253251for i , instance in enumerate (instances ):
254252params .append ({
255253'i' : i ,
@@ -259,20 +257,20 @@ def attack(url, n=10000, c=100):
259257'concurrent_requests' : connections_per_instance ,
260258'num_requests' : requests_per_instance ,
261259 })
262-
260+
263261print 'Stinging URL so it will be cached for the attack.'
264-
262+
265263# Ping url so it will be cached for testing
266264local ('curl %s >> /dev/null' % url )
267-
265+
268266print 'Organizing the swarm.'
269-
267+
270268# Spin up processes for connecting to EC2 instances
271269pool = Pool (len (params ))
272270results = pool .map (_attack , params )
273-
271+
274272print 'Offensive complete.'
275-
273+
276274_print_results (results )
277275
278276print 'The swarm is awaiting new orders.'
0 commit comments