Skip to content

Commit 8e1bed2

Browse files
committed
Merge remote branch 'thejefflarson/master' into refactor
2 parents 4d9effc + 94f439c commit 8e1bed2

File tree

2 files changed

+115
-67
lines changed

2 files changed

+115
-67
lines changed

‎bees.py‎

Lines changed: 65 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
importtime
3131

3232
importboto
33-
fromfabric.apiimport*
3433
importparamiko
3534

3635
EC2_INSTANCE_TYPE='m1.small'
@@ -39,39 +38,38 @@
3938

4039
# Load state from file
4140

41+
instance_ids= []
4242
ifos.path.isfile(STATE_FILENAME):
4343
withopen(STATE_FILENAME, 'r') asf:
4444
text=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

5351
def_write_server_list(instances):
5452
withopen(STATE_FILENAME, 'w') asf:
5553
f.write('\n'.join([instance.idforinstanceininstances]))
56-
54+
5755
# Methods
5856

5957
defup(count=5, group='staging', zone='us-east-1d'):
6058
"""
6159
Startup the load testing server.
6260
"""
63-
ifenv.instance_ids:
61+
ifinstance_ids:
6462
print'Bees are already assembled and awaiting orders.'
6563
return
66-
64+
6765
count=int(count)
68-
66+
6967
print'Connecting to the hive.'
70-
68+
7169
ec2_connection=boto.connect_ec2()
72-
70+
7371
print'Attempting to call up %i bees.'%count
74-
72+
7573
reservation=ec2_connection.run_instances(
7674
image_id='ami-ff17fb96',
7775
min_count=count,
@@ -80,113 +78,113 @@ def up(count=5, group='staging', zone='us-east-1d'):
8078
security_groups=[group],
8179
instance_type=EC2_INSTANCE_TYPE,
8280
placement=zone)
83-
81+
8482
print'Waiting for bees to load their machine guns...'
85-
83+
8684
forinstanceinreservation.instances:
8785
whileinstance.state!='running':
8886
print'.'
8987
time.sleep(5)
9088
instance.update()
91-
89+
9290
print'Bee %s is ready for the attack.'%instance.id
93-
91+
9492
_write_server_list(reservation.instances)
95-
93+
9694
print'The swarm has assembled %i bees.'%len(reservation.instances)
97-
95+
9896
defreport():
9997
"""
10098
Report the status of the load testing servers.
101-
"""
102-
ifnotenv.instance_ids:
99+
"""
100+
ifnotinstance_ids:
103101
print'No bees have been mobilized.'
104102
return
105103

106104
ec2_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

110108
instances= []
111109

112110
forreservationinreservations:
113111
instances.extend(reservation.instances)
114-
112+
115113
forinstanceininstances:
116114
print'Bee %s: %s'% (instance.id, instance.state)
117-
115+
118116
defdown():
119117
"""
120118
Shutdown the load testing server.
121119
"""
122-
ifnotenv.instance_ids:
120+
ifnotinstance_ids:
123121
print'No bees have been mobilized.'
124122
return
125-
123+
126124
print'Connecting to the hive.'
127125

128126
ec2_connection=boto.connect_ec2()
129-
127+
130128
print'Calling off the swarm.'
131-
129+
132130
terminated_instance_ids=ec2_connection.terminate_instances(
133-
instance_ids=env.instance_ids)
134-
131+
instance_ids=instance_ids)
132+
135133
print'Stood down %i bees.'%len(terminated_instance_ids)
136-
134+
137135
os.remove(STATE_FILENAME)
138-
136+
139137
def_attack(params):
140138
"""
141139
Test the target URL with requests.
142-
140+
143141
Intended for use with multiprocessing.
144142
"""
145143
print'Bee %i is joining the swarm.'%params['i']
146-
144+
147145
client=paramiko.SSHClient()
148146
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
149147
client.connect(
150148
params['instance_name'],
151149
username='newsapps',
152150
key_filename='/Users/sk/.ssh/frakkingtoasters.pem')
153-
151+
154152
print'Bee %i is firing his machine gun. Bang bang!'%params['i']
155-
153+
156154
stdin, stdout, stderr=client.exec_command('ab -r -n %(num_requests)s -c %(concurrent_requests)s -C "sessionid=NotARealSessionID" %(url)s'%params)
157-
155+
158156
response={}
159-
157+
160158
ab_results=stdout.read()
161159
ms_per_request_search=re.search('Time\ per\ request:\s+([0-9.]+)\ \[ms\]\ \(mean\)', ab_results)
162-
160+
163161
ifnotms_per_request_search:
164162
print'Bee %i lost sight of the target (connection timed out).'%params['i']
165163
returnNone
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)
168166
fifty_percent_search=re.search('\s+50\%\s+([0-9]+)', ab_results)
169167
ninety_percent_search=re.search('\s+90\%\s+([0-9]+)', ab_results)
170168
complete_requests_search=re.search('Complete\ requests:\s+([0-9]+)', ab_results)
171-
169+
172170
response['ms_per_request'] =float(ms_per_request_search.group(1))
173171
response['requests_per_second'] =float(requests_per_second_search.group(1))
174172
response['fifty_percent'] =float(fifty_percent_search.group(1))
175173
response['ninety_percent'] =float(ninety_percent_search.group(1))
176174
response['complete_requests'] =float(complete_requests_search.group(1))
177-
175+
178176
print'Bee %i is out of ammo.'%params['i']
179-
177+
180178
client.close()
181-
179+
182180
returnresponse
183-
181+
184182
def_print_results(results):
185183
"""
186184
Print summarized load-testing results.
187185
"""
188186
incomplete_results= [rforrinresultsifrisNone]
189-
187+
190188
ifincomplete_results:
191189
print' Target failed to fully respond to %i bees.'%len(incomplete_results)
192190

@@ -197,19 +195,19 @@ def _print_results(results):
197195
complete_results= [r['requests_per_second'] forrinresultsifrisnotNone]
198196
mean_requests=sum(complete_results)
199197
print' Requests per second:\t%f [#/sec] (mean)'%mean_requests
200-
198+
201199
complete_results= [r['ms_per_request'] forrinresultsifrisnotNone]
202200
mean_response=sum(complete_results) /len(complete_results)
203201
print' Time per request:\t\t%f [ms] (mean)'%mean_response
204-
202+
205203
complete_results= [r['fifty_percent'] forrinresultsifrisnotNone]
206204
mean_fifty=sum(complete_results) /len(complete_results)
207205
print' 50%% response time:\t\t%f [ms] (mean)'%mean_fifty
208-
206+
209207
complete_results= [r['ninety_percent'] forrinresultsifrisnotNone]
210208
mean_ninety=sum(complete_results) /len(complete_results)
211209
print' 90%% response time:\t\t%f [ms] (mean)'%mean_ninety
212-
210+
213211
ifmean_response<500:
214212
print'Mission Assessment: Target crushed bee offensive.'
215213
elifmean_response<1000:
@@ -225,31 +223,31 @@ def attack(url, n=10000, c=100):
225223
"""
226224
Test the root url of this site.
227225
"""
228-
ifnotenv.instance_ids:
226+
ifnotinstance_ids:
229227
print'No bees are ready to attack.'
230228
return
231-
229+
232230
print'Connecting to the hive.'
233231

234232
ec2_connection=boto.connect_ec2()
235233

236234
print'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+
240238
instances= []
241-
239+
242240
forreservationinreservations:
243241
instances.extend(reservation.instances)
244-
242+
245243
instance_count=len(instances)
246244
requests_per_instance=int(n) /instance_count
247245
connections_per_instance=int(c) /instance_count
248-
246+
249247
print'Each of %i bees will fire %s rounds, %s at a time.'% (instance_count, requests_per_instance, connections_per_instance)
250-
248+
251249
params= []
252-
250+
253251
fori, instanceinenumerate(instances):
254252
params.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+
263261
print'Stinging URL so it will be cached for the attack.'
264-
262+
265263
# Ping url so it will be cached for testing
266264
local('curl %s >> /dev/null'%url)
267-
265+
268266
print'Organizing the swarm.'
269-
267+
270268
# Spin up processes for connecting to EC2 instances
271269
pool=Pool(len(params))
272270
results=pool.map(_attack, params)
273-
271+
274272
print'Offensive complete.'
275-
273+
276274
_print_results(results)
277275

278276
print'The swarm is awaiting new orders.'

‎main.py‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
importbees
2+
importsys
3+
fromoptparseimportOptionParser
4+
5+
defparse_options():
6+
"""
7+
Handle the command line arguments for spinning up bees
8+
"""
9+
command=sys,
10+
parser=OptionParser(usage="""
11+
bees COMMAND URL [options]
12+
13+
Bees With Machine Guns
14+
15+
A utility for arming (creating) many bees (small EC2 instances) to attack
16+
(load test) targets (web applications).
17+
18+
commands:
19+
up start a batch of load testing servers
20+
attack begin the attack on a specific url
21+
down shutdown and deactivate the load testing servers
22+
""")
23+
24+
parser.add_option('-c', '--count', metavar="COUNT", nargs=1,
25+
help="number of instance to start")
26+
parser.add_option('-g', '--group', metavar="GROUP", nargs=1,
27+
help="the security group to run the instances under")
28+
parser.add_option('-z', '--zone', metavar="ZONE", nargs=1,
29+
help="the availability zone to start the instances in")
30+
31+
(options, args) =parser.parse_args()
32+
33+
ifnotargs>0:
34+
parser.error("please enter a command")
35+
command=args[0]
36+
ifcommand[0] is"attack"andlen(args) ==1:
37+
parser.error("to run an attack you need to present a url")
38+
url=args[-1].split(",")
39+
40+
ifcommand=="up":
41+
bees.up(count=options.count, group=options.group, zone=options.zone)
42+
elifcommand=="attack":
43+
bees.attack(url[0], url[1], url[2])
44+
elifcommand=="down":
45+
bees.down()
46+
47+
48+
defmain():
49+
parse_options()
50+

0 commit comments

Comments
(0)