ec2cli 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +71 -0
- data/bin/ec2cli +67 -0
- data/lib/ec2cli/cli/evaluate.rb +53 -0
- data/lib/ec2cli/cli/functions.rb +104 -0
- data/lib/ec2cli/cli/help.rb +40 -0
- data/lib/ec2cli/cli/options.rb +35 -0
- data/lib/ec2cli/ec2-client.rb +108 -0
- data/lib/ec2cli/ec2-driver.rb +322 -0
- data/lib/ec2cli/ec2-endpoint.rb +32 -0
- data/lib/ec2cli/ec2-error.rb +10 -0
- data/lib/ec2cli/ec2-parser.tab.rb +519 -0
- data/lib/ec2cli/ec2-parser.y +233 -0
- data/lib/ec2cli.rb +11 -0
- metadata +74 -0
@@ -0,0 +1,322 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'rexml/parsers/pullparser'
|
3
|
+
|
4
|
+
require 'ec2cli/ec2-client'
|
5
|
+
require 'ec2cli/ec2-parser.tab'
|
6
|
+
|
7
|
+
module EC2
|
8
|
+
class Driver
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
class Rownum
|
12
|
+
def initialize(rownum)
|
13
|
+
@rownum = rownum
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_i
|
17
|
+
@rownum
|
18
|
+
end
|
19
|
+
end # Rownum
|
20
|
+
|
21
|
+
def initialize(accessKeyId, secretAccessKey, endpoint_or_region)
|
22
|
+
@client = EC2::Client.new(accessKeyId, secretAccessKey, endpoint_or_region)
|
23
|
+
end
|
24
|
+
|
25
|
+
def_delegators(
|
26
|
+
:@client,
|
27
|
+
:endpoint,
|
28
|
+
:region,
|
29
|
+
:set_endpoint_and_region,
|
30
|
+
:debug, :'debug=')
|
31
|
+
|
32
|
+
def execute(query, opts = {})
|
33
|
+
parsed, script_type, script = EC2::Parser.parse(query)
|
34
|
+
command = parsed.class.name.split('::').last.to_sym
|
35
|
+
|
36
|
+
case command
|
37
|
+
when :DESCRIBE_INSTANCES
|
38
|
+
describe_instances(parsed.filter)
|
39
|
+
when :DESCRIBE_IMAGES
|
40
|
+
describe_images(parsed.filter, :all => parsed.all)
|
41
|
+
when :RUN_INSTANCES
|
42
|
+
run_instainces(parsed.image_id)
|
43
|
+
when :START_INSTANCES
|
44
|
+
start_instainces(parsed.instances)
|
45
|
+
when :STOP_INSTANCES
|
46
|
+
stop_instainces(parsed.instances)
|
47
|
+
when :CREATE_TAGS
|
48
|
+
create_tags(parsed.filter, parsed.tags)
|
49
|
+
when :SHOW_REGIONS
|
50
|
+
EC2::Endpoint.regions
|
51
|
+
when :USE
|
52
|
+
set_endpoint_and_region(parsed.endpoint_or_region)
|
53
|
+
nil
|
54
|
+
else
|
55
|
+
raise 'must not happen'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def create_filter_params(filter)
|
62
|
+
params = {}
|
63
|
+
|
64
|
+
(filter || {}).each_with_index do |name_values, i|
|
65
|
+
name, values, = name_values
|
66
|
+
params["Filter.#{i+1}.Name"] = name
|
67
|
+
|
68
|
+
values.each_with_index do |value, j|
|
69
|
+
params["Filter.#{i+1}.Value.#{j+1}"] = value
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
return params
|
74
|
+
end
|
75
|
+
|
76
|
+
def describe_instances(filter)
|
77
|
+
params = create_filter_params(filter)
|
78
|
+
source = @client.query('DescribeInstances', params)
|
79
|
+
parser = REXML::Parsers::PullParser.new(source)
|
80
|
+
|
81
|
+
rows = []
|
82
|
+
instance_id = nil
|
83
|
+
status = nil
|
84
|
+
ip_addr = nil
|
85
|
+
groups = nil
|
86
|
+
instance_type = nil
|
87
|
+
zone = nil
|
88
|
+
tags = {}
|
89
|
+
|
90
|
+
while parser.has_next?
|
91
|
+
event = parser.pull
|
92
|
+
next if event.event_type != :start_element
|
93
|
+
|
94
|
+
case event[0]
|
95
|
+
when 'instanceId'
|
96
|
+
ip_addr = nil
|
97
|
+
tags = {}
|
98
|
+
instance_id = parser.pull[0]
|
99
|
+
when 'instanceState'
|
100
|
+
until event.event_type == :start_element and event[0] == 'name'
|
101
|
+
event = parser.pull
|
102
|
+
end
|
103
|
+
|
104
|
+
status = parser.pull[0]
|
105
|
+
when 'instanceType'
|
106
|
+
instance_type = parser.pull[0]
|
107
|
+
when 'availabilityZone'
|
108
|
+
zone = parser.pull[0]
|
109
|
+
when 'privateIpAddress'
|
110
|
+
ip_addr = parser.pull[0] unless ip_addr
|
111
|
+
when 'groupSet'
|
112
|
+
groups = []
|
113
|
+
|
114
|
+
until event.event_type == :end_element and event[0] == 'groupSet'
|
115
|
+
event = parser.pull
|
116
|
+
|
117
|
+
if event.event_type == :start_element and event[0] == 'groupName'
|
118
|
+
groups << parser.pull[0]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
when 'tagSet'
|
122
|
+
tag_key = nil
|
123
|
+
|
124
|
+
until event.event_type == :end_element and event[0] == 'tagSet'
|
125
|
+
event = parser.pull
|
126
|
+
next unless event.event_type == :start_element
|
127
|
+
|
128
|
+
case event[0]
|
129
|
+
when 'key'; tag_key = parser.pull[0]
|
130
|
+
when 'value'; tags[tag_key] = parser.pull[0]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
when 'ebsOptimized'
|
134
|
+
row = [ tags['Name'],
|
135
|
+
instance_id,
|
136
|
+
status,
|
137
|
+
ip_addr,
|
138
|
+
instance_type,
|
139
|
+
groups,
|
140
|
+
tags,
|
141
|
+
zone,
|
142
|
+
]
|
143
|
+
|
144
|
+
yield row if block_given?
|
145
|
+
rows << row
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
return rows
|
150
|
+
end # describe_instances
|
151
|
+
|
152
|
+
def describe_images(filter, opts = {})
|
153
|
+
filter ||= []
|
154
|
+
params = {}
|
155
|
+
owner = filter.find {|k, v| k =~ /\Aowner\Z/i }
|
156
|
+
|
157
|
+
if owner
|
158
|
+
filter.delete_if {|k, v| k == owner[0] }
|
159
|
+
params['Owner.1'] = owner[1].first
|
160
|
+
elsif not opts[:qll]
|
161
|
+
params['Owner.1'] = 'self'
|
162
|
+
end
|
163
|
+
|
164
|
+
unless filter.any? {|k, v| k == 'image-id' }
|
165
|
+
filter << ['image-type', ['machine']]
|
166
|
+
end
|
167
|
+
|
168
|
+
params.update(create_filter_params(filter))
|
169
|
+
source = @client.query('DescribeImages', params)
|
170
|
+
parser = REXML::Parsers::PullParser.new(source)
|
171
|
+
|
172
|
+
rows = []
|
173
|
+
image_id = nil
|
174
|
+
image_state = nil
|
175
|
+
architecture = nil
|
176
|
+
image_type = nil
|
177
|
+
name = nil
|
178
|
+
description = nil
|
179
|
+
|
180
|
+
while parser.has_next?
|
181
|
+
event = parser.pull
|
182
|
+
next if event.event_type != :start_element
|
183
|
+
|
184
|
+
case event[0]
|
185
|
+
when 'imageId'
|
186
|
+
image_id = parser.pull[0]
|
187
|
+
when 'imageState'
|
188
|
+
image_state = parser.pull[0]
|
189
|
+
when 'architecture'
|
190
|
+
architecture = parser.pull[0]
|
191
|
+
when 'imageType'
|
192
|
+
image_type = parser.pull[0]
|
193
|
+
when 'name'
|
194
|
+
name = parser.pull[0]
|
195
|
+
when 'description'
|
196
|
+
description = parser.pull[0]
|
197
|
+
when 'hypervisor'
|
198
|
+
row = [
|
199
|
+
image_id,
|
200
|
+
image_state,
|
201
|
+
architecture,
|
202
|
+
image_type,
|
203
|
+
name,
|
204
|
+
description,
|
205
|
+
]
|
206
|
+
|
207
|
+
yield row if block_given?
|
208
|
+
rows << row
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
return rows
|
213
|
+
end # describe_images
|
214
|
+
|
215
|
+
def run_instainces(image_id)
|
216
|
+
# XXX:
|
217
|
+
params = {
|
218
|
+
'ImageId' => image_id,
|
219
|
+
'MinCount' => 1,
|
220
|
+
'MaxCount' => 1,
|
221
|
+
}
|
222
|
+
|
223
|
+
@client.query('RunInstances', params)
|
224
|
+
Rownum.new(1)
|
225
|
+
end
|
226
|
+
|
227
|
+
def start_instainces(instances)
|
228
|
+
instances = get_instaince_ids_by_names(instances)
|
229
|
+
params = {}
|
230
|
+
|
231
|
+
instances.each_with_index do |instance_id, i|
|
232
|
+
params["InstanceId.#{i+1}"] = instance_id
|
233
|
+
end
|
234
|
+
|
235
|
+
@client.query('StartInstances', params)
|
236
|
+
|
237
|
+
Rownum.new(instances.length)
|
238
|
+
end
|
239
|
+
|
240
|
+
def stop_instainces(instances)
|
241
|
+
instances = get_instaince_ids_by_names(instances)
|
242
|
+
params = {}
|
243
|
+
|
244
|
+
instances.each_with_index do |instance_id, i|
|
245
|
+
params["InstanceId.#{i+1}"] = instance_id
|
246
|
+
end
|
247
|
+
|
248
|
+
@client.query('StopInstances', params)
|
249
|
+
|
250
|
+
Rownum.new(instances.length)
|
251
|
+
end
|
252
|
+
|
253
|
+
def create_tags(filter, tags)
|
254
|
+
instance_ids = get_instance_ids(create_filter_params(filter))
|
255
|
+
|
256
|
+
params = {}
|
257
|
+
delete_params = {}
|
258
|
+
i = j = 0
|
259
|
+
|
260
|
+
instance_ids.each do |instance_id|
|
261
|
+
tags.each do |key, value|
|
262
|
+
if value
|
263
|
+
params["ResourceId.#{i+1}"] = instance_id
|
264
|
+
params["Tag.#{i+1}.Key"] = key
|
265
|
+
params["Tag.#{i+1}.Value"] = value
|
266
|
+
i += 1
|
267
|
+
else
|
268
|
+
delete_params["ResourceId.#{j+1}"] = instance_id
|
269
|
+
delete_params["Tag.#{j+1}.Key"] = key
|
270
|
+
j += 1
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
@client.query('CreateTags', params) unless params.empty?
|
276
|
+
@client.query('DeleteTags', delete_params) unless delete_params.empty?
|
277
|
+
|
278
|
+
Rownum.new(instance_ids.length)
|
279
|
+
end
|
280
|
+
|
281
|
+
def get_instaince_ids_by_names(id_or_names)
|
282
|
+
id_or_names.map do |id_or_name|
|
283
|
+
if id_or_name =~ /\Ai-[0-9a-z]+\Z/
|
284
|
+
id_or_name
|
285
|
+
else
|
286
|
+
get_instance_id_by_name(id_or_name)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def get_instance_id_by_name(name)
|
292
|
+
params = {
|
293
|
+
'Filter.1.Name' => 'tag:Name',
|
294
|
+
'Filter.1.Value' => name
|
295
|
+
}
|
296
|
+
|
297
|
+
instance_ids = get_instance_ids(params)
|
298
|
+
|
299
|
+
return instance_ids.first
|
300
|
+
end
|
301
|
+
|
302
|
+
|
303
|
+
def get_instance_ids(params)
|
304
|
+
instance_ids = []
|
305
|
+
|
306
|
+
source = @client.query('DescribeInstances', params)
|
307
|
+
parser = REXML::Parsers::PullParser.new(source)
|
308
|
+
|
309
|
+
while parser.has_next?
|
310
|
+
event = parser.pull
|
311
|
+
next if event.event_type != :start_element
|
312
|
+
|
313
|
+
if event[0] == 'instanceId'
|
314
|
+
instance_ids << parser.pull[0]
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
return instance_ids
|
319
|
+
end
|
320
|
+
|
321
|
+
end # Driver
|
322
|
+
end # EC2
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'ec2cli/ec2-error'
|
2
|
+
|
3
|
+
module EC2
|
4
|
+
class Endpoint
|
5
|
+
|
6
|
+
ENDPOINTS = {
|
7
|
+
'ec2.us-east-1.amazonaws.com' => 'us-east-1',
|
8
|
+
'ec2.us-west-2.amazonaws.com' => 'us-west-2',
|
9
|
+
'ec2.us-west-1.amazonaws.com' => 'us-west-1',
|
10
|
+
'ec2.eu-west-1.amazonaws.com' => 'eu-west-1',
|
11
|
+
'ec2.ap-southeast-1.amazonaws.com' => 'ap-southeast-1',
|
12
|
+
'ec2.ap-southeast-2.amazonaws.com' => 'ap-southeast-2',
|
13
|
+
'ec2.ap-northeast-1.amazonaws.com' => 'ap-northeast-1',
|
14
|
+
'ec2.sa-east-1.amazonaws.com' => 'sa-east-1',
|
15
|
+
}
|
16
|
+
|
17
|
+
def self.endpoint_and_region(endpoint_or_region)
|
18
|
+
if ENDPOINTS.key?(endpoint_or_region)
|
19
|
+
[endpoint_or_region, ENDPOINTS[endpoint_or_region]]
|
20
|
+
elsif ENDPOINTS.value?(endpoint_or_region)
|
21
|
+
[ENDPOINTS.key(endpoint_or_region), endpoint_or_region]
|
22
|
+
else
|
23
|
+
raise EC2::Error, "Unknown endpoint or region: #{endpoint_or_region}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.regions
|
28
|
+
ENDPOINTS.values.dup
|
29
|
+
end
|
30
|
+
|
31
|
+
end # Endpoint
|
32
|
+
end # EC2
|