tengine_resource_ec2 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|