tengine_resource_ec2 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +125 -0
- data/README.rdoc +20 -0
- data/lib/tengine/resource_ec2/dummy_connection.rb +167 -0
- data/lib/tengine/resource_ec2/dummy_connection.rb~ +148 -0
- data/lib/tengine/resource_ec2/launch_options.rb +182 -0
- data/lib/tengine/resource_ec2/launch_options.rb~ +181 -0
- data/lib/tengine/resource_ec2/provider.rb +309 -0
- data/lib/tengine/resource_ec2/provider.rb~ +189 -0
- data/lib/tengine/resource_ec2.rb +8 -0
- data/lib/tengine_resource_ec2.rb +5 -0
- data/spec/fixtures/goku_at_ec2_ap_northeast.rb +177 -0
- data/tmp/.gitkeep +0 -0
- metadata +231 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/support/core_ext/enumerable/map_to_hash'
|
3
|
+
|
4
|
+
class Tengine::Resource::Credential::Ec2::LaunchOptions
|
5
|
+
|
6
|
+
def initialize(credential)
|
7
|
+
@credential = credential
|
8
|
+
end
|
9
|
+
|
10
|
+
LAUNCH_OPTIONS_KEYS = %w(current_region regions availability_zones key_pairs
|
11
|
+
security_groups images instance_types kernel_ids ramdisk_ids)
|
12
|
+
|
13
|
+
def launch_options(connection, current_region)
|
14
|
+
@connection, @current_region = connection, current_region
|
15
|
+
LAUNCH_OPTIONS_KEYS.map_to_hash{|m| send(m)}
|
16
|
+
ensure
|
17
|
+
@connection, @current_region = nil, nil
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :current_region
|
21
|
+
|
22
|
+
DEFAULT_REGION_CAPTIONS = {
|
23
|
+
"us-east-1" => "US East" ,
|
24
|
+
"us-west-1" => "US West" ,
|
25
|
+
"eu-west-1" => "EU West" ,
|
26
|
+
"ap-southeast-1" => "Asia Pacific"
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
def regions
|
30
|
+
# ["eu-west-1", "us-east-1", "us-west-1", "ap-southeast-1"]
|
31
|
+
raw = @connection.describe_regions
|
32
|
+
raw.inject([]) do |dest, region|
|
33
|
+
dest << {"name" => region, "caption" => DEFAULT_REGION_CAPTIONS[region]}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def availability_zones
|
38
|
+
# [
|
39
|
+
# {:region_name=>"us-east-1", :zone_name=>"us-east-1a", :zone_state=>"available"},
|
40
|
+
# {:region_name=>"us-east-1", :zone_name=>"us-east-1b", :zone_state=>"available"},
|
41
|
+
# {:region_name=>"us-east-1", :zone_name=>"us-east-1c", :zone_state=>"available"},
|
42
|
+
# {:region_name=>"us-east-1", :zone_name=>"us-east-1d", :zone_state=>"available"}
|
43
|
+
# ]
|
44
|
+
raw = @connection.describe_availability_zones
|
45
|
+
raw.map{|h| h[:zone_name]}.sort
|
46
|
+
end
|
47
|
+
|
48
|
+
def key_pairs
|
49
|
+
# [{:aws_key_name=>"west-dev01", :aws_fingerprint=>"7c:89:2f:c9:4a:1c:02:65:1b:14:dc:a5:c9:a0:da:fb:46:08:4a:99"}]
|
50
|
+
raw = @connection.describe_key_pairs
|
51
|
+
raw.map{|h| h[:aws_key_name]}
|
52
|
+
end
|
53
|
+
|
54
|
+
def security_groups
|
55
|
+
# [
|
56
|
+
# { :aws_owner=>"892601002221", :aws_group_name=>"default", :aws_description=>"default group",
|
57
|
+
# :aws_perms=>[{:owner=>"892601002221", :group=>"default"}, {:from_port=>"22", :to_port=>"22", :cidr_ips=>"0.0.0.0/0", :protocol=>"tcp"}]},
|
58
|
+
# { :aws_owner=>"892601002221", :aws_group_name=>"ruby-dev", :aws_description=>"for developmewnt with ruby",
|
59
|
+
# :aws_perms=>[{:from_port=>"80", :to_port=>"80", :cidr_ips=>"0.0.0.0/0", :protocol=>"tcp"}]}
|
60
|
+
# ]
|
61
|
+
raw = @connection.describe_security_groups
|
62
|
+
raw.map{|h| h[:aws_group_name]}
|
63
|
+
end
|
64
|
+
|
65
|
+
def images
|
66
|
+
# [
|
67
|
+
# {
|
68
|
+
# :aws_id=>"ami-5189d814",
|
69
|
+
# :aws_architecture=>"i386", :root_device_type=>"instance-store",
|
70
|
+
# :root_device_name=>"/dev/sda1",
|
71
|
+
# :aws_location=>"akm2000-us-west-2/dev-20100521-01.manifest.xml",
|
72
|
+
# :aws_image_type=>"machine", :aws_state=>"available",
|
73
|
+
# :aws_owner=>"892601002221", :aws_is_public=>false,
|
74
|
+
# :aws_kernel_id=>"aki-773c6d32", :aws_ramdisk_id=>"ari-c12e7f84",
|
75
|
+
# },
|
76
|
+
# ]
|
77
|
+
saved_images = Tengine::Resource::VirtualServerImage.all
|
78
|
+
# raw_images = @connection.describe_images_by_owner('self')
|
79
|
+
raw_images = @connection.describe_images(saved_images.map(&:provided_id).uniq.compact) #クラスタに登録されているAMI
|
80
|
+
# raw_images += @connection.describe_images_by_executable_by("self") # 実行可能なAMI
|
81
|
+
amiid_to_hash = raw_images.inject({}){|d, hash| d[hash[:aws_id]] = hash; d}
|
82
|
+
result = saved_images.map do |saved_image|
|
83
|
+
if ami = amiid_to_hash[saved_image.provided_id]
|
84
|
+
{
|
85
|
+
'id' => saved_image.id,
|
86
|
+
'name' => ami[:aws_id],
|
87
|
+
'caption' => saved_image.description,
|
88
|
+
'aws_architecture' => ami[:aws_architecture],
|
89
|
+
'aws_arch_root_dev' => to_aws_arch_root_dev(ami),
|
90
|
+
}
|
91
|
+
else
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
result.compact.uniq
|
96
|
+
end
|
97
|
+
|
98
|
+
def instance_types
|
99
|
+
# rawなし
|
100
|
+
INSTANCE_TYPES
|
101
|
+
end
|
102
|
+
|
103
|
+
def kernel_ids
|
104
|
+
# [
|
105
|
+
# {
|
106
|
+
# :aws_id=>"aki-233c6d66",
|
107
|
+
# :aws_architecture=>"i386", :root_device_type=>"instance-store",
|
108
|
+
# :aws_location=>"ec2-paid-ibm-images-us-west-1/vmlinuz-2.6.16.60-0.29-xenpae.i386.manifest.xml",
|
109
|
+
# :aws_image_type=>"kernel", :aws_state=>"available", :aws_owner=>"470254534024",
|
110
|
+
# :aws_is_public=>true, :image_owner_alias=>"amazon",
|
111
|
+
# },
|
112
|
+
# ]
|
113
|
+
raw = amazon_images.select{|img| img[:aws_image_type] == 'kernel'}
|
114
|
+
raw.inject({}) do |dest, hash|
|
115
|
+
key = hash[:aws_architecture]
|
116
|
+
dest[key] ||= []
|
117
|
+
dest[key] << hash[:aws_id]
|
118
|
+
dest
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def ramdisk_ids
|
123
|
+
# [
|
124
|
+
# {
|
125
|
+
# :aws_id=>"ari-2d3c6d68",
|
126
|
+
# :aws_architecture=>"i386", :root_device_type=>"instance-store",
|
127
|
+
# :aws_location=>"ec2-paid-ibm-images-us-west-1/initrd-2.6.16.60-0.29-xenpae.i386.manifest.xml",
|
128
|
+
# :aws_image_type=>"ramdisk", :aws_state=>"available", :aws_owner=>"470254534024",
|
129
|
+
# :aws_is_public=>true, :image_owner_alias=>"amazon"
|
130
|
+
# },
|
131
|
+
# ]
|
132
|
+
raw = amazon_images.select{|img| img[:aws_image_type] == 'ramdisk'}
|
133
|
+
raw.inject({}) do |dest, hash|
|
134
|
+
key = hash[:aws_architecture]
|
135
|
+
dest[key] ||= []
|
136
|
+
dest[key] << hash[:aws_id]
|
137
|
+
dest
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def amazon_images
|
144
|
+
@amazon_images ||= @connection.describe_images_by_owner('amazon')
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def to_aws_arch_root_dev(hash)
|
149
|
+
"#{hash[:aws_architecture]}_#{hash[:root_device_type]}"
|
150
|
+
end
|
151
|
+
|
152
|
+
INSTANCE_TYPES = {
|
153
|
+
'i386_instance-store' => [
|
154
|
+
{ "value" => "m1.small" , "caption" => "Small" },
|
155
|
+
{ "value" => "c1.medium" , "caption" => "High-CPU Medium" },
|
156
|
+
].freeze,
|
157
|
+
'i386_ebs' => [
|
158
|
+
{ "value" => "t1.micro" , "caption" => "Micro" },
|
159
|
+
{ "value" => "m1.small" , "caption" => "Small" },
|
160
|
+
{ "value" => "c1.medium" , "caption" => "High-CPU Medium" },
|
161
|
+
].freeze,
|
162
|
+
'x86_64_instance-store' => [
|
163
|
+
{ "value" => "m1.large" , "caption" => "Large" },
|
164
|
+
{ "value" => "m1.xlarge" , "caption" => "Extra Large" },
|
165
|
+
{ "value" => "m2.xlarge" , "caption" => "High-Memory Extra Large" },
|
166
|
+
{ "value" => "m2.2xlarge", "caption" => "High-Memory Double Extra Large" },
|
167
|
+
{ "value" => "m2.4xlarge", "caption" => "High-Memory Quadruple Extra Large" },
|
168
|
+
{ "value" => "c1.xlarge" , "caption" => "High-CPU Extra Large" },
|
169
|
+
].freeze,
|
170
|
+
'x86_64_ebs' => [
|
171
|
+
{ "value" => "t1.micro" , "caption" => "Micro" },
|
172
|
+
{ "value" => "m1.large" , "caption" => "Large" },
|
173
|
+
{ "value" => "m1.xlarge" , "caption" => "Extra Large" },
|
174
|
+
{ "value" => "m2.xlarge" , "caption" => "High-Memory Extra Large" },
|
175
|
+
{ "value" => "m2.2xlarge", "caption" => "High-Memory Double Extra Large" },
|
176
|
+
{ "value" => "m2.4xlarge", "caption" => "High-Memory Quadruple Extra Large" },
|
177
|
+
{ "value" => "c1.xlarge" , "caption" => "High-CPU Extra Large" },
|
178
|
+
].freeze,
|
179
|
+
}.freeze
|
180
|
+
|
181
|
+
end
|
@@ -0,0 +1,309 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/resource_ec2'
|
3
|
+
|
4
|
+
require 'right_aws'
|
5
|
+
require 'tengine/support/core_ext/hash/keys'
|
6
|
+
|
7
|
+
|
8
|
+
class Tengine::ResourceEc2::Provider < Tengine::Resource::Provider
|
9
|
+
|
10
|
+
# モデルの継承時に問題が発生する場合があるので、collectionの呼び出しによる接続先の事前確定を行います。
|
11
|
+
# https://github.com/tengine/tengine/commit/9a986a3b52b18a95b1d5324b9731f720d087d1e4
|
12
|
+
collection
|
13
|
+
|
14
|
+
field :connection_settings, :type => Hash
|
15
|
+
|
16
|
+
def synchronize_physical_servers
|
17
|
+
synchronize_by(:physical_servers)
|
18
|
+
end
|
19
|
+
|
20
|
+
def synchronize_virtual_server_images
|
21
|
+
synchronize_by(:virtual_server_images)
|
22
|
+
end
|
23
|
+
|
24
|
+
def synchronize_virtual_servers
|
25
|
+
synchronize_by(:virtual_servers)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
class Synchronizer < Tengine::Resource::Provider::Synchronizer
|
31
|
+
end
|
32
|
+
|
33
|
+
class PhysicalServerSynchronizer < Synchronizer
|
34
|
+
fetch_known_target_method :describe_availability_zones
|
35
|
+
|
36
|
+
map(:provided_id, :zone_name)
|
37
|
+
map(:status , :zone_state)
|
38
|
+
map(:cpu_cores ) { 1000 }
|
39
|
+
map(:memory_size) { 1000 }
|
40
|
+
|
41
|
+
def attrs_to_create(properties)
|
42
|
+
result = super(properties)
|
43
|
+
# 初期登録時、default 値として name には一意な provided_id を name へ登録します
|
44
|
+
result[:name] = result[:provided_id]
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
def destroy_targets(targets)
|
49
|
+
targets.each do |target|
|
50
|
+
target.status = "not_found"
|
51
|
+
target.save!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class VirtualServerImageSynchronizer < Synchronizer
|
57
|
+
fetch_known_target_method :describe_images
|
58
|
+
map :provided_id , :aws_id
|
59
|
+
|
60
|
+
def attrs_to_create(properties)
|
61
|
+
result = super(properties)
|
62
|
+
# 初期登録時、default 値として name には一意な provided_id を name へ登録します
|
63
|
+
result[:name] = result[:provided_id]
|
64
|
+
result
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class VirtualServerSynchronizer < Synchronizer
|
69
|
+
fetch_known_target_method :describe_instances
|
70
|
+
|
71
|
+
map :provided_id , :aws_instance_id
|
72
|
+
map :provided_image_id, :aws_image_id
|
73
|
+
map :status , :aws_state
|
74
|
+
map(:host_server) do |props, provider|
|
75
|
+
provider.physical_servers.where(:provided_id => props[:aws_availability_zone]).first
|
76
|
+
end
|
77
|
+
map :addresses do |props, provider|
|
78
|
+
{
|
79
|
+
:dns_name => props.delete(:dns_name ),
|
80
|
+
:ip_address => props.delete(:ip_address ),
|
81
|
+
:private_dns_name => props.delete(:private_dns_name ),
|
82
|
+
:private_ip_address => props.delete(:private_ip_address),
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def attrs_to_create(properties)
|
87
|
+
result = super(properties)
|
88
|
+
result[:name] = result[:provided_id]
|
89
|
+
result
|
90
|
+
end
|
91
|
+
|
92
|
+
def mapped_attributes(properties)
|
93
|
+
properties.delete(:aws_state_code)
|
94
|
+
result = super(properties)
|
95
|
+
result
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def describe_availability_zones
|
100
|
+
# ec2.describe_availability_zones #=> [{:region_name=>"us-east-1",
|
101
|
+
# :zone_name=>"us-east-1a",
|
102
|
+
# :zone_state=>"available"}, ... ]
|
103
|
+
# http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeAvailabilityZones.html
|
104
|
+
connect{|conn| conn.describe_availability_zones }
|
105
|
+
end
|
106
|
+
|
107
|
+
def describe_images
|
108
|
+
connect{|conn| conn.describe_images_by_owner("self")}
|
109
|
+
end
|
110
|
+
|
111
|
+
def describe_instances
|
112
|
+
# http://rightscale.rubyforge.org/right_aws_gem_doc/
|
113
|
+
# ec2.describe_instances #=>
|
114
|
+
# [{:aws_image_id => "ami-e444444d",
|
115
|
+
# :aws_reason => "",
|
116
|
+
# :aws_state_code => "16",
|
117
|
+
# :aws_owner => "000000000888",
|
118
|
+
# :aws_instance_id => "i-123f1234",
|
119
|
+
# :aws_reservation_id => "r-aabbccdd",
|
120
|
+
# :aws_state => "running",
|
121
|
+
# :dns_name => "domU-12-34-67-89-01-C9.usma2.compute.amazonaws.com",
|
122
|
+
# :ssh_key_name => "staging",
|
123
|
+
# :aws_groups => ["default"],
|
124
|
+
# :private_dns_name => "domU-12-34-67-89-01-C9.usma2.compute.amazonaws.com",
|
125
|
+
# :aws_instance_type => "m1.small",
|
126
|
+
# :aws_launch_time => "2008-1-1T00:00:00.000Z"},
|
127
|
+
# :aws_availability_zone => "us-east-1b",
|
128
|
+
# :aws_kernel_id => "aki-ba3adfd3",
|
129
|
+
# :aws_ramdisk_id => "ari-badbad00",
|
130
|
+
# ..., {...}]
|
131
|
+
# http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeInstances.html
|
132
|
+
connect{|conn| conn.describe_instances }
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
register_synchronizers({
|
142
|
+
:physical_servers => PhysicalServerSynchronizer,
|
143
|
+
# :virtual_server_types => VirtualServerTypeSynchronizer,
|
144
|
+
:virtual_server_images => VirtualServerImageSynchronizer,
|
145
|
+
:virtual_servers => VirtualServerSynchronizer,
|
146
|
+
})
|
147
|
+
|
148
|
+
private
|
149
|
+
def update_physical_servers_by(hashs)
|
150
|
+
found_ids = []
|
151
|
+
hashs.each do |hash|
|
152
|
+
server = self.physical_servers.where(:provided_id => hash[:provided_id]).first
|
153
|
+
if server
|
154
|
+
server.update_attributes(:status => hash[:status])
|
155
|
+
else
|
156
|
+
server = self.physical_servers.create!(
|
157
|
+
:provided_id => hash[:provided_id],
|
158
|
+
:name => hash[:name],
|
159
|
+
:status => hash[:status])
|
160
|
+
end
|
161
|
+
found_ids << server.id
|
162
|
+
end
|
163
|
+
self.physical_servers.not_in(:_id => found_ids).update_all(:status => "not_found")
|
164
|
+
end
|
165
|
+
|
166
|
+
def update_virtual_servers_by(hashs)
|
167
|
+
found_ids = []
|
168
|
+
hashs.each do |hash|
|
169
|
+
server = self.virtual_servers.where(:provided_id => hash[:provided_id]).first
|
170
|
+
if server
|
171
|
+
server.update_attributes(hash)
|
172
|
+
else
|
173
|
+
server = self.virtual_servers.create!(hash.merge(:name => hash[:provided_id]))
|
174
|
+
end
|
175
|
+
found_ids << server.id
|
176
|
+
end
|
177
|
+
self.virtual_servers.not_in(:_id => found_ids).destroy_all
|
178
|
+
end
|
179
|
+
|
180
|
+
def update_virtual_server_images_by(hashs)
|
181
|
+
found_ids = []
|
182
|
+
hashs.each do |hash|
|
183
|
+
img = self.virtual_server_images.where(:provided_id => hash[:provided_id]).first
|
184
|
+
if img
|
185
|
+
img.update_attributes(hash)
|
186
|
+
else
|
187
|
+
img = self.virtual_server_images.create!(hash.merge(:name => hash[:provided_id]))
|
188
|
+
end
|
189
|
+
found_ids << img.id
|
190
|
+
end
|
191
|
+
self.virtual_server_images.not_in(:_id => found_ids).destroy_all
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
public
|
196
|
+
|
197
|
+
# @param [String] name Name template for created virtual servers
|
198
|
+
# @param [Tengine::Resource::VirtualServerImage] image Virtual server image object
|
199
|
+
# @param [Tengine::Resource::VirtualServerType] type Virtual server type object
|
200
|
+
# @param [String] physical Data center name to put virtual machines (availability zone)
|
201
|
+
# @param [String] description What this virtual server is
|
202
|
+
# @param [Numeric] min_count Minimum number of vortial servers to boot
|
203
|
+
# @param [Numeric] max_count Maximum number of vortial servers to boot
|
204
|
+
# @param [Array<Strng>] group_ids Array of names of security group IDs
|
205
|
+
# @param [Strng] key_name Name of root key to sue
|
206
|
+
# @param [Strng] user_data User-specified
|
207
|
+
# @param [Strng] kernel_id Kernel image ID
|
208
|
+
# @param [Strng] ramdisk_id Ramdisk image ID
|
209
|
+
# @return [Array<Tengine::Resource::VirtualServer>]
|
210
|
+
def create_virtual_servers name, image, type, physical, description, min_count, max_count, group_ids, key_name, user_data = "", kernel_id, ramdisk_id
|
211
|
+
connect {|conn|
|
212
|
+
results = conn.run_instances(
|
213
|
+
image.provided_id,
|
214
|
+
min_count,
|
215
|
+
max_count,
|
216
|
+
group_ids,
|
217
|
+
key_name,
|
218
|
+
user_data,
|
219
|
+
nil, # <- addressing_type
|
220
|
+
type.provided_id,
|
221
|
+
kernel_id,
|
222
|
+
ramdisk_id,
|
223
|
+
physical,
|
224
|
+
nil # <- block_device_mappings
|
225
|
+
)
|
226
|
+
yield if block_given? # テスト用のブロックの呼び出し
|
227
|
+
results.map.with_index {|hash, idx|
|
228
|
+
provided_id = hash.delete(:aws_instance_id)
|
229
|
+
if server = self.virtual_servers.where({:provided_id => provided_id}).first
|
230
|
+
server
|
231
|
+
else
|
232
|
+
# findではなくfirstで検索しているので、もしhost_server_provided_idで指定されるサーバが見つからなくても
|
233
|
+
# host_serverがnilとして扱われるが、仮想サーバ自身の登録は行われます
|
234
|
+
host_server = Tengine::Resource::PhysicalServer.by_provided_id(
|
235
|
+
[hash[:aws_availability_zone], physical].detect{|i| !i.blank?})
|
236
|
+
self.find_virtual_server_on_duplicaion_error(provided_id) do
|
237
|
+
self.virtual_servers.create!(
|
238
|
+
:name => sprintf("%s%03d", name, idx + 1), # 1 origin
|
239
|
+
:address_order => address_order.dup,
|
240
|
+
:description => description,
|
241
|
+
:provided_id => provided_id,
|
242
|
+
:provided_image_id => hash.delete(:aws_image_id),
|
243
|
+
:provided_type_id => hash.delete(:aws_instance_type),
|
244
|
+
:host_server_id => host_server ? host_server.id : nil,
|
245
|
+
:status => hash.delete(:aws_state),
|
246
|
+
:properties => hash,
|
247
|
+
:addresses => {
|
248
|
+
# :dns_name => hash.delete(:dns_name),
|
249
|
+
# :ip_address => hash.delete(:ip_address),
|
250
|
+
# :private_dns_name => hash.delete(:private_dns_name),
|
251
|
+
# :private_ip_address => hash.delete(:private_ip_address),
|
252
|
+
})
|
253
|
+
end
|
254
|
+
end
|
255
|
+
}
|
256
|
+
}
|
257
|
+
end
|
258
|
+
|
259
|
+
def terminate_virtual_servers servers
|
260
|
+
connect do |conn|
|
261
|
+
# http://rightscale.rubyforge.org/right_aws_gem_doc/classes/RightAws/Ec2.html#M000287
|
262
|
+
# http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-TerminateInstances.html
|
263
|
+
conn.terminate_instances(servers.map {|i| i.provided_id }).map do |hash|
|
264
|
+
serv = self.virtual_servers.where(:provided_id => hash[:aws_instance_id]).first
|
265
|
+
serv.update_attributes(:status => hash[:aws_current_state_name]) if serv
|
266
|
+
serv
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# 仮想サーバタイプの監視
|
272
|
+
def synchronize_virtual_server_types
|
273
|
+
# ec2から取得する情報はありません
|
274
|
+
end
|
275
|
+
|
276
|
+
private
|
277
|
+
def address_order
|
278
|
+
@@address_order ||= %w"private_ip_address private_dns_name ip_address dns_name".each(&:freeze).freeze
|
279
|
+
end
|
280
|
+
|
281
|
+
def connect
|
282
|
+
klass = (ENV['EC2_DUMMY'] == "true") ? Tengine::ResourceEc2::DummyConnection : RightAws::Ec2
|
283
|
+
connection_settings.stringify_keys! # DBに保存されるとSymbolのキーはStringに変換される
|
284
|
+
Tengine.logger.info("now connecting by using #{connection_settings.inspect}")
|
285
|
+
connection = klass.new(
|
286
|
+
access_key,
|
287
|
+
secret_access_key,
|
288
|
+
{
|
289
|
+
:logger => Tengine.logger,
|
290
|
+
:region => connection_settings['region']
|
291
|
+
}
|
292
|
+
)
|
293
|
+
yield connection
|
294
|
+
end
|
295
|
+
|
296
|
+
def access_key
|
297
|
+
connection_settings['access_key'] || read_file_if_exist(connection_settings['access_key_file'])
|
298
|
+
end
|
299
|
+
|
300
|
+
def secret_access_key
|
301
|
+
connection_settings['secret_access_key'] || read_file_if_exist(connection_settings['secret_access_key_file'])
|
302
|
+
end
|
303
|
+
|
304
|
+
def read_file_if_exist(filepath)
|
305
|
+
return nil unless filepath
|
306
|
+
File.read(File.expand_path(filepath)).strip # ~をホームディレクトリに展開するためにFile.expand_pathを使っています
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine_resource'
|
3
|
+
|
4
|
+
class Tengine::Resource::Provider::Ec2 < Tengine::Resource::Provider
|
5
|
+
|
6
|
+
field :connection_settings, :type => Hash
|
7
|
+
|
8
|
+
def update_physical_servers
|
9
|
+
connect do |conn|
|
10
|
+
# ec2.describe_availability_zones #=> [{:region_name=>"us-east-1",
|
11
|
+
# :zone_name=>"us-east-1a",
|
12
|
+
# :zone_state=>"available"}, ... ]
|
13
|
+
# http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeAvailabilityZones.html
|
14
|
+
hashs = conn.describe_availability_zones.map do |hash|
|
15
|
+
{
|
16
|
+
:provided_id => hash[:zone_name],
|
17
|
+
:name => hash[:zone_name],
|
18
|
+
:status => hash[:zone_state],
|
19
|
+
}
|
20
|
+
end
|
21
|
+
update_physical_servers_by(hashs)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_virtual_servers
|
26
|
+
connect do |conn|
|
27
|
+
# http://rightscale.rubyforge.org/right_aws_gem_doc/
|
28
|
+
# ec2.describe_instances #=>
|
29
|
+
# [{:aws_image_id => "ami-e444444d",
|
30
|
+
# :aws_reason => "",
|
31
|
+
# :aws_state_code => "16",
|
32
|
+
# :aws_owner => "000000000888",
|
33
|
+
# :aws_instance_id => "i-123f1234",
|
34
|
+
# :aws_reservation_id => "r-aabbccdd",
|
35
|
+
# :aws_state => "running",
|
36
|
+
# :dns_name => "domU-12-34-67-89-01-C9.usma2.compute.amazonaws.com",
|
37
|
+
# :ssh_key_name => "staging",
|
38
|
+
# :aws_groups => ["default"],
|
39
|
+
# :private_dns_name => "domU-12-34-67-89-01-C9.usma2.compute.amazonaws.com",
|
40
|
+
# :aws_instance_type => "m1.small",
|
41
|
+
# :aws_launch_time => "2008-1-1T00:00:00.000Z"},
|
42
|
+
# :aws_availability_zone => "us-east-1b",
|
43
|
+
# :aws_kernel_id => "aki-ba3adfd3",
|
44
|
+
# :aws_ramdisk_id => "ari-badbad00",
|
45
|
+
# ..., {...}]
|
46
|
+
# http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeInstances.html
|
47
|
+
hashs = conn.describe_instances.map do |hash|
|
48
|
+
result = {
|
49
|
+
:provided_id => hash.delete(:aws_instance_id),
|
50
|
+
:provided_image_id => hash.delete(:aws_image_id),
|
51
|
+
:status => hash.delete(:aws_state),
|
52
|
+
}
|
53
|
+
hash.delete(:aws_state_code)
|
54
|
+
result[:properties] = hash
|
55
|
+
result[:addresses] = {
|
56
|
+
:dns_name => hash.delete(:dns_name),
|
57
|
+
:ip_address => hash.delete(:ip_address),
|
58
|
+
:private_dns_name => hash.delete(:private_dns_name),
|
59
|
+
:private_ip_address => hash.delete(:private_ip_address),
|
60
|
+
}
|
61
|
+
result
|
62
|
+
end
|
63
|
+
update_virtual_servers_by(hashs)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def update_virtual_server_images
|
68
|
+
connect do |conn|
|
69
|
+
hashs = conn.describe_images.map do |hash|
|
70
|
+
{ :provided_id => hash.delete(:aws_id), }
|
71
|
+
end
|
72
|
+
update_virtual_server_images_by(hashs)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @param [String] name Name template for created virtual servers
|
77
|
+
# @param [Tengine::Resource::VirtualServerImage] image Virtual server image object
|
78
|
+
# @param [Tengine::Resource::VirtualServerType] type Virtual server type object
|
79
|
+
# @param [String] physical Data center name to put virtual machines (availability zone)
|
80
|
+
# @param [String] description What this virtual server is
|
81
|
+
# @param [Numeric] min_count Minimum number of vortial servers to boot
|
82
|
+
# @param [Numeric] max_count Maximum number of vortial servers to boot
|
83
|
+
# @param [Array<Strng>] group_ids Array of names of security group IDs
|
84
|
+
# @param [Strng] key_name Name of root key to sue
|
85
|
+
# @param [Strng] user_data User-specified
|
86
|
+
# @param [Strng] kernel_id Kernel image ID
|
87
|
+
# @param [Strng] ramdisk_id Ramdisk image ID
|
88
|
+
# @return [Array<Tengine::Resource::VirtualServer>]
|
89
|
+
def create_virtual_servers name, image, type, physical, description, min_count, max_count, group_ids, key_name, user_data = "", kernel_id, ramdisk_id
|
90
|
+
connect {|conn|
|
91
|
+
results = conn.run_instances(
|
92
|
+
image.provided_id,
|
93
|
+
min_count,
|
94
|
+
max_count,
|
95
|
+
group_ids,
|
96
|
+
key_name,
|
97
|
+
user_data,
|
98
|
+
nil, # <- addressing_type
|
99
|
+
type.provided_id,
|
100
|
+
kernel_id,
|
101
|
+
ramdisk_id,
|
102
|
+
physical,
|
103
|
+
nil # <- block_device_mappings
|
104
|
+
)
|
105
|
+
yield if block_given? # テスト用のブロックの呼び出し
|
106
|
+
results.map.with_index {|hash, idx|
|
107
|
+
provided_id = hash.delete(:aws_instance_id)
|
108
|
+
if server = self.virtual_servers.find(:first, :conditions => {:provided_id => provided_id})
|
109
|
+
server
|
110
|
+
else
|
111
|
+
host_server_provided_id = hash[:aws_availability_zone]
|
112
|
+
host_server_provided_id = physical if host_server_provided_id.nil? || host_server_provided_id.blank?
|
113
|
+
# findではなくfirstで検索しているので、もしhost_server_provided_idで指定されるサーバが見つからなくても
|
114
|
+
# host_serverがnilとして扱われるが、仮想サーバ自身の登録は行われます
|
115
|
+
host_server = (host_server_provided_id && !host_server_provided_id.blank?) ?
|
116
|
+
Tengine::Resource::PhysicalServer.first(:conditions => {:provided_id => host_server_provided_id}) : nil
|
117
|
+
begin
|
118
|
+
self.virtual_servers.create!(
|
119
|
+
:name => sprintf("%s%03d", name, idx + 1), # 1 origin
|
120
|
+
:address_order => address_order,
|
121
|
+
:description => description,
|
122
|
+
:provided_id => provided_id,
|
123
|
+
:provided_image_id => hash.delete(:aws_image_id),
|
124
|
+
:provided_type_id => hash.delete(:aws_instance_type),
|
125
|
+
:host_server_id => host_server ? host_server.id : nil,
|
126
|
+
:status => hash.delete(:aws_state),
|
127
|
+
:properties => hash,
|
128
|
+
:addresses => {
|
129
|
+
# :dns_name => hash.delete(:dns_name),
|
130
|
+
# :ip_address => hash.delete(:ip_address),
|
131
|
+
# :private_dns_name => hash.delete(:private_dns_name),
|
132
|
+
# :private_ip_address => hash.delete(:private_ip_address),
|
133
|
+
})
|
134
|
+
rescue Mongo::OperationFailure => e
|
135
|
+
raise e unless e.message =~ /E11000 duplicate key error/
|
136
|
+
self.virtual_servers.find(:first, :conditions => {:provided_id => provided_id}) or
|
137
|
+
raise "VirtualServer not found for #{provided_id}"
|
138
|
+
rescue Mongoid::Errors::Validations => e
|
139
|
+
raise e unless e.document.errors[:provided_id].any?{|s| s =~ /taken/}
|
140
|
+
self.virtual_servers.find(:first, :conditions => {:provided_id => provided_id}) or
|
141
|
+
raise "VirtualServer not found for #{provided_id}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
}
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
def terminate_virtual_servers servers
|
149
|
+
connect do |conn|
|
150
|
+
# http://rightscale.rubyforge.org/right_aws_gem_doc/classes/RightAws/Ec2.html#M000287
|
151
|
+
# http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-TerminateInstances.html
|
152
|
+
conn.terminate_instances(servers.map {|i| i.provided_id }).map do |hash|
|
153
|
+
serv = self.virtual_servers.where(:provided_id => hash[:aws_instance_id]).first
|
154
|
+
serv.update_attributes(:status => hash[:aws_current_state_name]) if serv
|
155
|
+
serv
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# 仮想サーバタイプの監視
|
161
|
+
def synchronize_virtual_server_types
|
162
|
+
# ec2から取得する情報はありません
|
163
|
+
end
|
164
|
+
|
165
|
+
# 物理サーバの監視
|
166
|
+
def synchronize_physical_servers ; raise NotImplementedError end
|
167
|
+
# 仮想サーバの監視
|
168
|
+
def synchronize_virtual_servers ; raise NotImplementedError end
|
169
|
+
# 仮想サーバイメージの監視
|
170
|
+
def synchronize_virtual_server_images ; raise NotImplementedError end
|
171
|
+
|
172
|
+
private
|
173
|
+
def address_order
|
174
|
+
@@address_order ||= %w"private_ip_address private_dns_name ip_address dns_name".each(&:freeze).freeze
|
175
|
+
end
|
176
|
+
|
177
|
+
def connect
|
178
|
+
klass = (ENV['EC2_DUMMY'] == "true") ? Tengine::Resource::Credential::Ec2::Dummy : RightAws::Ec2
|
179
|
+
connection = klass.new(
|
180
|
+
self.connection_settings[:access_key],
|
181
|
+
self.connection_settings[:secret_access_key],
|
182
|
+
{
|
183
|
+
:logger => Tengine.logger,
|
184
|
+
:region => self.connection_settings[:region]
|
185
|
+
}
|
186
|
+
)
|
187
|
+
yield connection
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine_resource_ec2'
|
3
|
+
|
4
|
+
module Tengine::ResourceEc2
|
5
|
+
autoload :Provider , 'tengine/resource_ec2/provider'
|
6
|
+
autoload :DummyConnection, 'tengine/resource_ec2/dummy_connection'
|
7
|
+
autoload :LaunchOptions , 'tengine/resource_ec2/launch_options'
|
8
|
+
end
|