tengine_resource 0.5.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +30 -0
  4. data/Gemfile.lock +106 -0
  5. data/README.rdoc +20 -0
  6. data/Rakefile +42 -0
  7. data/VERSION +1 -0
  8. data/bin/tengine_resource_watchd +8 -0
  9. data/config/.gitignore +2 -0
  10. data/config/watchd.yml.erb.example +52 -0
  11. data/lib/tengine/resource.rb +29 -0
  12. data/lib/tengine/resource/config.rb +6 -0
  13. data/lib/tengine/resource/config/resource.rb +194 -0
  14. data/lib/tengine/resource/credential.rb +156 -0
  15. data/lib/tengine/resource/credential/ec2.rb +5 -0
  16. data/lib/tengine/resource/credential/ec2/dummy.rb +148 -0
  17. data/lib/tengine/resource/credential/ec2/launch_options.rb +179 -0
  18. data/lib/tengine/resource/drivers/resource_control_driver.rb +58 -0
  19. data/lib/tengine/resource/net_ssh.rb +134 -0
  20. data/lib/tengine/resource/observer.rb +25 -0
  21. data/lib/tengine/resource/physical_server.rb +7 -0
  22. data/lib/tengine/resource/provider.rb +82 -0
  23. data/lib/tengine/resource/provider/ec2.rb +187 -0
  24. data/lib/tengine/resource/provider/wakame.rb +615 -0
  25. data/lib/tengine/resource/server.rb +62 -0
  26. data/lib/tengine/resource/virtual_server.rb +62 -0
  27. data/lib/tengine/resource/virtual_server_image.rb +34 -0
  28. data/lib/tengine/resource/virtual_server_type.rb +21 -0
  29. data/lib/tengine/resource/watcher.rb +121 -0
  30. data/lib/tengine_resource.rb +4 -0
  31. data/spec/fixtures/goku_at_ec2_ap_northeast.rb +177 -0
  32. data/spec/mongoid.yml +35 -0
  33. data/spec/spec_helper.rb +40 -0
  34. data/spec/support/ec2.rb +129 -0
  35. data/spec/support/mongo_index_key_log.rb +91 -0
  36. data/spec/tengine/resource/bugfix/watcher_for_wakame_spec.rb +232 -0
  37. data/spec/tengine/resource/credential_spec.rb +205 -0
  38. data/spec/tengine/resource/drivers/resource_control_driver_spec.rb +84 -0
  39. data/spec/tengine/resource/net_ssh_spec.rb +148 -0
  40. data/spec/tengine/resource/physical_server_spec.rb +47 -0
  41. data/spec/tengine/resource/provider/ec2_spec.rb +473 -0
  42. data/spec/tengine/resource/provider/test_files/describe_host_nodes.json +22 -0
  43. data/spec/tengine/resource/provider/test_files/describe_images.json +23 -0
  44. data/spec/tengine/resource/provider/test_files/describe_instance_specs.json +23 -0
  45. data/spec/tengine/resource/provider/test_files/describe_instances.json +56 -0
  46. data/spec/tengine/resource/provider/test_files/run_instances.json +30 -0
  47. data/spec/tengine/resource/provider/test_files/terminate_instances.json +3 -0
  48. data/spec/tengine/resource/provider/wakame/00_describe_host_nodes_0_physical_servers.json +8 -0
  49. data/spec/tengine/resource/provider/wakame/01_describe_host_nodes_10_physical_servers.json +139 -0
  50. data/spec/tengine/resource/provider/wakame/02_describe_host_nodes_60_physical_servers.json +795 -0
  51. data/spec/tengine/resource/provider/wakame/10_describe_instances_0_virtual_servers.json +8 -0
  52. data/spec/tengine/resource/provider/wakame/11_describe_instances_10_virtual_servers.json +469 -0
  53. data/spec/tengine/resource/provider/wakame/12_describe_instances_after_run_instances.json +280 -0
  54. data/spec/tengine/resource/provider/wakame/13_describe_instances_after_terminate_instances.json +279 -0
  55. data/spec/tengine/resource/provider/wakame/20_describe_images_0_virtual_server_images.json +8 -0
  56. data/spec/tengine/resource/provider/wakame/21_describe_images_5_virtual_server_images.json +84 -0
  57. data/spec/tengine/resource/provider/wakame/22_describe_images_60_virtual_server_images.json +856 -0
  58. data/spec/tengine/resource/provider/wakame/30_describe_instance_specs_0_virtual_server_specs.json +8 -0
  59. data/spec/tengine/resource/provider/wakame/31_describe_instance_specs_4_virtual_server_specs.json +66 -0
  60. data/spec/tengine/resource/provider/wakame/40_run_instances_0_virtual_servers.json +1 -0
  61. data/spec/tengine/resource/provider/wakame/41_run_instances_1_virtual_servers.json +22 -0
  62. data/spec/tengine/resource/provider/wakame/42_run_instances_5_virtual_servers.json +106 -0
  63. data/spec/tengine/resource/provider/wakame/50_terminate_instances_0_virtual_servers.json +1 -0
  64. data/spec/tengine/resource/provider/wakame/51_terminate_instances_3_virtual_servers.json +5 -0
  65. data/spec/tengine/resource/provider/wakame/sync_physical_servers_spec.rb +114 -0
  66. data/spec/tengine/resource/provider/wakame/sync_virtual_server_images_spec.rb +116 -0
  67. data/spec/tengine/resource/provider/wakame/sync_virtual_server_types_spec.rb +116 -0
  68. data/spec/tengine/resource/provider/wakame/sync_virtual_servers_spec.rb +216 -0
  69. data/spec/tengine/resource/provider/wakame_api_spec.rb +319 -0
  70. data/spec/tengine/resource/provider/wakame_spec.rb +339 -0
  71. data/spec/tengine/resource/provider_spec.rb +252 -0
  72. data/spec/tengine/resource/server_spec.rb +195 -0
  73. data/spec/tengine/resource/test_files/.gitignore +6 -0
  74. data/spec/tengine/resource/test_files/00_describe_host_nodes_0_physical_servers.json +8 -0
  75. data/spec/tengine/resource/test_files/01_describe_host_nodes_10_physical_servers.json +139 -0
  76. data/spec/tengine/resource/test_files/02_describe_host_nodes_60_physical_servers.json +795 -0
  77. data/spec/tengine/resource/test_files/10_describe_instances_0_virtual_servers.json +8 -0
  78. data/spec/tengine/resource/test_files/11_describe_instances_10_virtual_servers.json +469 -0
  79. data/spec/tengine/resource/test_files/12_describe_instances_after_run_instances.json +280 -0
  80. data/spec/tengine/resource/test_files/13_describe_instances_after_terminate_instances.json +279 -0
  81. data/spec/tengine/resource/test_files/14_describe_instances_after_run_1_instance.json +55 -0
  82. data/spec/tengine/resource/test_files/20_describe_images_0_virtual_server_images.json +8 -0
  83. data/spec/tengine/resource/test_files/21_describe_images_5_virtual_server_images.json +84 -0
  84. data/spec/tengine/resource/test_files/22_describe_images_60_virtual_server_images.json +856 -0
  85. data/spec/tengine/resource/test_files/30_describe_instance_specs_0_virtual_server_specs.json +8 -0
  86. data/spec/tengine/resource/test_files/31_describe_instance_specs_4_virtual_server_specs.json +66 -0
  87. data/spec/tengine/resource/test_files/40_run_instances_0_virtual_servers.json +1 -0
  88. data/spec/tengine/resource/test_files/41_run_instances_1_virtual_servers.json +22 -0
  89. data/spec/tengine/resource/test_files/42_run_instances_5_virtual_servers.json +106 -0
  90. data/spec/tengine/resource/test_files/43_run_instances_1_virtual_servers_without_aws_availability_zone.json +22 -0
  91. data/spec/tengine/resource/test_files/50_terminate_instances_0_virtual_servers.json +1 -0
  92. data/spec/tengine/resource/test_files/51_terminate_instances_3_virtual_servers.json +5 -0
  93. data/spec/tengine/resource/virtual_server_image_spec.rb +94 -0
  94. data/spec/tengine/resource/virtual_server_spec.rb +116 -0
  95. data/spec/tengine/resource/virtual_server_type_spec.rb +4 -0
  96. data/spec/tengine/resource/watcher_spec.rb +1026 -0
  97. data/spec/tengine_resource_spec.rb +5 -0
  98. data/tengine_resource.gemspec +171 -0
  99. data/tmp/log/.gitignore +1 -0
  100. metadata +286 -0
@@ -0,0 +1,156 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'mongoid'
3
+ require 'selectable_attr'
4
+
5
+ class Tengine::Resource::Credential
6
+ autoload :Ec2, 'tengine/resource/credential/ec2'
7
+
8
+ include Mongoid::Document
9
+ include Mongoid::Timestamps
10
+ include Tengine::Core::SelectableAttr
11
+ include Tengine::Core::CollectionAccessible
12
+ include Tengine::Core::Validation
13
+ include Tengine::Core::FindByName
14
+
15
+ field :name, :type => String
16
+ field :description, :type => String
17
+ field :auth_type_cd, :type => String
18
+ field :auth_values, :type => Hash
19
+ map_yaml_accessor :auth_values
20
+
21
+ # これは秋間のselectable_attrを使ってます
22
+ # see http://github.com/akm/selectable_attr
23
+ # http://github.com/akm/selectable_attr_rails
24
+ # EC2での認証については以下などを参照してください。
25
+ # http://builder.japan.zdnet.com/member/u502383/blog/2008/08/08/entry_27012840/
26
+ selectable_attr :auth_type_cd do
27
+ entry "01", :ssh_password , "SSHパスワード認証" , :for_launch => false
28
+ entry "02", :ssh_public_key, "SSH公開鍵認証" , :for_launch => false
29
+ # entry "03", :ec2_access_key, "EC2 アクセスキー認証", :for_launch => true
30
+ # entry "04", :ec2_x509_cert, "EC2 X.509認証"
31
+ # entry "05", :tama, "Tama", :for_launch => true
32
+ end
33
+
34
+ validates :name, :presence => true, :uniqueness => true, :format => BASE_NAME.options
35
+ validates :auth_type_cd, :presence => true
36
+
37
+ index :name, :unique => true
38
+
39
+ index([ [:_id, Mongo::ASCENDING], [:auth_type_cd, Mongo::ASCENDING], ])
40
+ index([ [:_id, Mongo::ASCENDING], [:auth_type_cd, Mongo::DESCENDING], ])
41
+ index([ [:_id, Mongo::ASCENDING], [:description, Mongo::ASCENDING], ])
42
+ index([ [:_id, Mongo::ASCENDING], [:description, Mongo::DESCENDING], ])
43
+
44
+ before_validation :prepare_auth_values_default # auth_valuesの各値がnilならデフォルト値を設定します
45
+ validate{|c| c.validate_auth_values}
46
+
47
+ SECRET_AUTH_VALUES_KEYS = %w[password passphrase private_keys secret_access_key]
48
+
49
+ def secure_auth_values
50
+ if result = auth_values.stringify_keys!
51
+ SECRET_AUTH_VALUES_KEYS.each{|key| result.delete(key)}
52
+ end
53
+ result
54
+ end
55
+
56
+ class AuthField
57
+ attr_reader :name, :type_key, :optional, :default
58
+ def initialize(name, type_key, options = {})
59
+ @name, @type_key = name.to_s, type_key.to_sym
60
+ @options = options || {}
61
+ @optional = !!@options[:optional]
62
+ @default = @options[:default]
63
+ end
64
+ def validate(credential, hash)
65
+ unless optional
66
+ credential.errors.add(:auth_values, "#{name.inspect} can't be blank") if hash[name].blank?
67
+ end
68
+ end
69
+ def optional?
70
+ @optional
71
+ end
72
+
73
+ def eql?(other)
74
+ self.name <=> other.to_s
75
+ end
76
+ def hash
77
+ self.name.hash
78
+ end
79
+ def to_s
80
+ self.name
81
+ end
82
+ end
83
+
84
+ AUTH_TYPE_KEY_TO_FIELDS = {
85
+ # Net::SSHにはたくさんのオプションがあります
86
+ # http://net-ssh.rubyforge.org/ssh/v2/api/classes/Net/SSH.html
87
+
88
+ # {:username => "goku", :password =>"xxx"}
89
+ :ssh_password => [
90
+ AuthField.new(:username, :string),
91
+ AuthField.new(:password, :secret),
92
+ ].freeze,
93
+
94
+ # {:username => "goku", :private_keys =>"xxx", :passphrase => "xxxx"}
95
+ :ssh_public_key => [
96
+ AuthField.new(:username , :string),
97
+ AuthField.new(:private_keys, :text),
98
+ AuthField.new(:passphrase , :secret, :optional => true),
99
+ ].freeze,
100
+
101
+ # {:access_key => "xxxxx", :secret_access_key =>"xxxxx"}
102
+ :ec2_access_key => [
103
+ AuthField.new(:access_key, :string),
104
+ AuthField.new(:secret_access_key, :string),
105
+ AuthField.new(:default_region, :string, :default => "us-east-1"),
106
+ ].freeze,
107
+ }.freeze
108
+
109
+ def validate_auth_values
110
+ if auth_type_key
111
+ hash = self.auth_values.stringify_keys!
112
+ fields = AUTH_TYPE_KEY_TO_FIELDS[auth_type_key]
113
+ fields.each do |field|
114
+ field.validate(self, hash)
115
+ end
116
+ end
117
+ end
118
+
119
+ ALL_FILED_NAMES = AUTH_TYPE_KEY_TO_FIELDS.values.flatten.map(&:name)
120
+ def prepare_auth_values_default
121
+ if auth_type_key
122
+ hash = self.auth_values.stringify_keys!
123
+ fields = AUTH_TYPE_KEY_TO_FIELDS[auth_type_key]
124
+ fields.each do |field|
125
+ if default_value = field.default
126
+ hash[field.name] ||= default_value
127
+ end
128
+ end
129
+ (ALL_FILED_NAMES - AUTH_TYPE_KEY_TO_FIELDS[auth_type_key].map(&:name)).each do |field_name|
130
+ hash.delete(field_name)
131
+ hash.delete(field_name.to_s)
132
+ end
133
+ end
134
+ end
135
+
136
+ def for_launch?
137
+ raise NotImplementedError, "deprecated API"
138
+ end
139
+
140
+ def launch_options(connect_options = {})
141
+ raise NotImplementedError, "deprecated API"
142
+ end
143
+
144
+ def connect(*args, &block)
145
+ raise NotImplementedError, "deprecated API"
146
+ end
147
+
148
+ class << self
149
+ def find_or_create_by_name!(attrs = {}, &block)
150
+ result = self.first(:conditions => {:name => attrs[:name]})
151
+ result ||= self.create!(attrs)
152
+ result
153
+ end
154
+ end
155
+
156
+ end
@@ -0,0 +1,5 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Tengine::Resource::Credential::Ec2
3
+ autoload :Dummy, 'tengine/resource/credential/ec2/dummy'
4
+ autoload :LaunchOptions, 'tengine/resource/credential/ec2/launch_options'
5
+ end
@@ -0,0 +1,148 @@
1
+ # -*- coding: utf-8 -*-
2
+ # EC2に実際には接続しないであたかも接続しているかのように見せるためのダミーです。
3
+ #
4
+ # 環境変数EC2_DUMMYがtrueの場合に、RightAws::Ec2の代わりにこのクラスからインスタンスが生成されます。
5
+ # 起動してから環境変数EC2_DUMMY_INTERVALで指定された秒数が経過したインスタンス(のダミー)はちゃんとrunningになるようになっています。
6
+ #
7
+ # [10/08/19 13:06:41] akimatter: モックはtestでしか使えません。
8
+ # [10/08/19 13:08:47] akimatter: developmentでは、RightAws::Ec2のインスタンスの代わりのダミー(一般的にはモックって言うけど、specのモックと区別して敢えてダミーします)を使うようにするべきかも。
9
+ # [10/08/19 13:09:19] akimatter: ただし、developmentでもダミーではなくEC2にちゃんと繋ぎたい場合もあるので、
10
+ # [10/08/19 13:09:41] akimatter: 環境変数か何かの設定でこれらの動作が変わる方が良いかと思います。
11
+ class Tengine::Resource::Credential::Ec2::Dummy
12
+ cattr_accessor :last_instance_index
13
+ @@last_instance_index = 0
14
+
15
+ # クラス変数で生成しているインスタンスを保持したいところですが、
16
+ # それだとdevelopmentモードでクラスがロードされる度に保持している内容がクリア
17
+ # されてしまうのでNGでした。
18
+ # 他に影響を与えないように実装する手段が他に思いつかなかったので、グローバル変数を使います。
19
+ # cattr_accessor :instances
20
+ # @@instances = {}
21
+ def self.instances
22
+ $ec2_dummy_instances ||= {}
23
+ end
24
+ def self.instances=(value)
25
+ $ec2_dummy_instances = value
26
+ end
27
+
28
+ def instances
29
+ self.class.instances
30
+ end
31
+
32
+ def initialize(access_key, secret_access_key, options = {})
33
+ @access_key, @secret_access_key = access_key, secret_access_key
34
+ @options = options || {}
35
+ end
36
+
37
+ STATIC_ATTRS = {
38
+ :aws_reservation_id=>"r-71a46435",
39
+ :aws_owner=>"000000000888",
40
+ :aws_ramdisk_id=>"ari-c12e7f84",
41
+ :aws_product_codes=>[],
42
+ :monitoring_state=>"disabled",
43
+ :aws_instance_type=>"m1.small",
44
+ :root_device_type=>"instance-store",
45
+ :aws_reason=>"",
46
+ :aws_kernel_id=>"aki-773c6d32",
47
+ :aws_availability_zone=>"us-west-1a"
48
+ }
49
+
50
+ def launch_instances(image_id, options)
51
+ launch_time = Time.zone.now
52
+ count = options.delete(:min_count) || 1
53
+ options.delete(:max_count) # :max_countは無視
54
+ idx = 0
55
+ result = []
56
+ count.times do
57
+ instance_index = (self.class.last_instance_index += 1)
58
+ instanceid = "i-DMY%05d" % instance_index
59
+ instance_hash = {
60
+ :aws_instance_id => instanceid,
61
+ :aws_image_id => image_id,
62
+ :state_reason_code=>"pending",
63
+ :ssh_key_name => options[:key_name],
64
+ :aws_groups => options[:group_ids],
65
+ :state_reason_message=>"pending",
66
+ :aws_state_code=>0,
67
+ :dns_name=>"",
68
+ :private_dns_name => "",
69
+ :aws_launch_time => launch_time.iso8601,
70
+ :aws_state => "pending",
71
+ :ami_launch_index => idx.to_s,
72
+ }.update(STATIC_ATTRS.dup)
73
+ idx += 1
74
+ result << instance_hash
75
+ instances[instanceid] = instance_hash
76
+ end
77
+ result
78
+ end
79
+
80
+ def describe_instances(instance_ids = [])
81
+ update_instances
82
+ if instance_ids.empty?
83
+ instances.values.sort_by{|hash| hash[:aws_instance_id]}
84
+ else
85
+ instance_ids.map{|id| instances[id]}
86
+ end
87
+ end
88
+
89
+ def terminate_instances(instance_ids)
90
+ update_instances
91
+ instance_ids.each do |instance_id|
92
+ update_status_running(instance_id) # 停止する前に一度はrunningにしておく
93
+ hash = instances[instance_id]
94
+ hash.update(
95
+ :state_reason_code=>"Client.UserInitiatedShutdown",
96
+ :state_reason_message=>"Client.UserInitiatedShutdown: User initiated shutdown",
97
+ :aws_state_code=>48,
98
+ :dns_name=>"",
99
+ :private_dns_name=>"",
100
+ :aws_state=>"terminated"
101
+ )
102
+ hash.delete(:ip_address)
103
+ hash.delete(:private_ip_address)
104
+ end
105
+ instance_ids.map do |instance_id|
106
+ {
107
+ :aws_prev_state_name => "running",
108
+ :aws_instance_id => instance_id,
109
+ :aws_current_state_code => 32,
110
+ :aws_current_state_name => "shutting-down",
111
+ :aws_prev_state_code => 16
112
+ }
113
+ end
114
+ end
115
+
116
+ private
117
+ UPDATE_INTERVAL = (ENV['EC2_DUMMY_INTERVAL'] || 30).to_i.seconds
118
+
119
+ def update_instances
120
+ t = Time.zone.now
121
+ instances.each do |instanceid, hash|
122
+ next unless hash[:aws_state] == "pending"
123
+ launch_time = Time.zone.parse(hash[:aws_launch_time])
124
+ next if (t - launch_time) < UPDATE_INTERVAL
125
+ update_status_running(instanceid)
126
+ end
127
+ end
128
+
129
+ def update_status_running(instance_id)
130
+ hash = instances[instance_id]
131
+ unless hash
132
+ raise ArgumentError, "No instance found for #{instance_id.inspect}. " << instances.keys.map(&:to_s).join("\n") << " exist."
133
+ end
134
+ instance_index = hash[:aws_instance_id].sub(/^i\-DMY/, '').to_i
135
+ hash.update(
136
+ :aws_state_code=>16,
137
+ :dns_name=>"ec2-184-72-20-#{instance_index}.us-west-1.compute.amazonaws.com",
138
+ :ip_address=>"184.72.20.#{instance_index}",
139
+ :private_dns_name=>"ip-10-162-153-#{instance_index}.us-west-1.compute.internal",
140
+ :private_ip_address=>"10.162.153.#{instance_index}",
141
+ :aws_state=>"running",
142
+ :architecture=>"i386"
143
+ )
144
+ hash.delete(:state_reason_message)
145
+ hash.delete(:state_reason_code)
146
+ end
147
+
148
+ end
@@ -0,0 +1,179 @@
1
+ # -*- coding: utf-8 -*-
2
+ class Tengine::Resource::Credential::Ec2::LaunchOptions
3
+
4
+ def initialize(credential)
5
+ @credential = credential
6
+ end
7
+
8
+ LAUNCH_OPTIONS_KEYS = %w(current_region regions availability_zones key_pairs
9
+ security_groups images instance_types kernel_ids ramdisk_ids)
10
+
11
+ def launch_options(connection, current_region)
12
+ @connection, @current_region = connection, current_region
13
+ LAUNCH_OPTIONS_KEYS.inject({}){|dest, m| dest[m] = send(m); dest}
14
+ ensure
15
+ @connection, @current_region = nil, nil
16
+ end
17
+
18
+ attr_reader :current_region
19
+
20
+ DEFAULT_REGION_CAPTIONS = {
21
+ "us-east-1" => "US East" ,
22
+ "us-west-1" => "US West" ,
23
+ "eu-west-1" => "EU West" ,
24
+ "ap-southeast-1" => "Asia Pacific"
25
+ }.freeze
26
+
27
+ def regions
28
+ # ["eu-west-1", "us-east-1", "us-west-1", "ap-southeast-1"]
29
+ raw = @connection.describe_regions
30
+ raw.inject([]) do |dest, region|
31
+ dest << {"name" => region, "caption" => DEFAULT_REGION_CAPTIONS[region]}
32
+ end
33
+ end
34
+
35
+ def availability_zones
36
+ # [
37
+ # {:region_name=>"us-east-1", :zone_name=>"us-east-1a", :zone_state=>"available"},
38
+ # {:region_name=>"us-east-1", :zone_name=>"us-east-1b", :zone_state=>"available"},
39
+ # {:region_name=>"us-east-1", :zone_name=>"us-east-1c", :zone_state=>"available"},
40
+ # {:region_name=>"us-east-1", :zone_name=>"us-east-1d", :zone_state=>"available"}
41
+ # ]
42
+ raw = @connection.describe_availability_zones
43
+ raw.map{|h| h[:zone_name]}.sort
44
+ end
45
+
46
+ def key_pairs
47
+ # [{: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"}]
48
+ raw = @connection.describe_key_pairs
49
+ raw.map{|h| h[:aws_key_name]}
50
+ end
51
+
52
+ def security_groups
53
+ # [
54
+ # { :aws_owner=>"892601002221", :aws_group_name=>"default", :aws_description=>"default group",
55
+ # :aws_perms=>[{:owner=>"892601002221", :group=>"default"}, {:from_port=>"22", :to_port=>"22", :cidr_ips=>"0.0.0.0/0", :protocol=>"tcp"}]},
56
+ # { :aws_owner=>"892601002221", :aws_group_name=>"ruby-dev", :aws_description=>"for developmewnt with ruby",
57
+ # :aws_perms=>[{:from_port=>"80", :to_port=>"80", :cidr_ips=>"0.0.0.0/0", :protocol=>"tcp"}]}
58
+ # ]
59
+ raw = @connection.describe_security_groups
60
+ raw.map{|h| h[:aws_group_name]}
61
+ end
62
+
63
+ def images
64
+ # [
65
+ # {
66
+ # :aws_id=>"ami-5189d814",
67
+ # :aws_architecture=>"i386", :root_device_type=>"instance-store",
68
+ # :root_device_name=>"/dev/sda1",
69
+ # :aws_location=>"akm2000-us-west-2/dev-20100521-01.manifest.xml",
70
+ # :aws_image_type=>"machine", :aws_state=>"available",
71
+ # :aws_owner=>"892601002221", :aws_is_public=>false,
72
+ # :aws_kernel_id=>"aki-773c6d32", :aws_ramdisk_id=>"ari-c12e7f84",
73
+ # },
74
+ # ]
75
+ saved_images = Tengine::Resource::VirtualServerImage.all
76
+ # raw_images = @connection.describe_images_by_owner('self')
77
+ raw_images = @connection.describe_images(saved_images.map(&:provided_id).uniq.compact) #クラスタに登録されているAMI
78
+ # raw_images += @connection.describe_images_by_executable_by("self") # 実行可能なAMI
79
+ amiid_to_hash = raw_images.inject({}){|d, hash| d[hash[:aws_id]] = hash; d}
80
+ result = saved_images.map do |saved_image|
81
+ if ami = amiid_to_hash[saved_image.provided_id]
82
+ {
83
+ 'id' => saved_image.id,
84
+ 'name' => ami[:aws_id],
85
+ 'caption' => saved_image.description,
86
+ 'aws_architecture' => ami[:aws_architecture],
87
+ 'aws_arch_root_dev' => to_aws_arch_root_dev(ami),
88
+ }
89
+ else
90
+ nil
91
+ end
92
+ end
93
+ result.compact.uniq
94
+ end
95
+
96
+ def instance_types
97
+ # rawなし
98
+ INSTANCE_TYPES
99
+ end
100
+
101
+ def kernel_ids
102
+ # [
103
+ # {
104
+ # :aws_id=>"aki-233c6d66",
105
+ # :aws_architecture=>"i386", :root_device_type=>"instance-store",
106
+ # :aws_location=>"ec2-paid-ibm-images-us-west-1/vmlinuz-2.6.16.60-0.29-xenpae.i386.manifest.xml",
107
+ # :aws_image_type=>"kernel", :aws_state=>"available", :aws_owner=>"470254534024",
108
+ # :aws_is_public=>true, :image_owner_alias=>"amazon",
109
+ # },
110
+ # ]
111
+ raw = amazon_images.select{|img| img[:aws_image_type] == 'kernel'}
112
+ raw.inject({}) do |dest, hash|
113
+ key = hash[:aws_architecture]
114
+ dest[key] ||= []
115
+ dest[key] << hash[:aws_id]
116
+ dest
117
+ end
118
+ end
119
+
120
+ def ramdisk_ids
121
+ # [
122
+ # {
123
+ # :aws_id=>"ari-2d3c6d68",
124
+ # :aws_architecture=>"i386", :root_device_type=>"instance-store",
125
+ # :aws_location=>"ec2-paid-ibm-images-us-west-1/initrd-2.6.16.60-0.29-xenpae.i386.manifest.xml",
126
+ # :aws_image_type=>"ramdisk", :aws_state=>"available", :aws_owner=>"470254534024",
127
+ # :aws_is_public=>true, :image_owner_alias=>"amazon"
128
+ # },
129
+ # ]
130
+ raw = amazon_images.select{|img| img[:aws_image_type] == 'ramdisk'}
131
+ raw.inject({}) do |dest, hash|
132
+ key = hash[:aws_architecture]
133
+ dest[key] ||= []
134
+ dest[key] << hash[:aws_id]
135
+ dest
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ def amazon_images
142
+ @amazon_images ||= @connection.describe_images_by_owner('amazon')
143
+ end
144
+
145
+
146
+ def to_aws_arch_root_dev(hash)
147
+ "#{hash[:aws_architecture]}_#{hash[:root_device_type]}"
148
+ end
149
+
150
+ INSTANCE_TYPES = {
151
+ 'i386_instance-store' => [
152
+ { "value" => "m1.small" , "caption" => "Small" },
153
+ { "value" => "c1.medium" , "caption" => "High-CPU Medium" },
154
+ ].freeze,
155
+ 'i386_ebs' => [
156
+ { "value" => "t1.micro" , "caption" => "Micro" },
157
+ { "value" => "m1.small" , "caption" => "Small" },
158
+ { "value" => "c1.medium" , "caption" => "High-CPU Medium" },
159
+ ].freeze,
160
+ 'x86_64_instance-store' => [
161
+ { "value" => "m1.large" , "caption" => "Large" },
162
+ { "value" => "m1.xlarge" , "caption" => "Extra Large" },
163
+ { "value" => "m2.xlarge" , "caption" => "High-Memory Extra Large" },
164
+ { "value" => "m2.2xlarge", "caption" => "High-Memory Double Extra Large" },
165
+ { "value" => "m2.4xlarge", "caption" => "High-Memory Quadruple Extra Large" },
166
+ { "value" => "c1.xlarge" , "caption" => "High-CPU Extra Large" },
167
+ ].freeze,
168
+ 'x86_64_ebs' => [
169
+ { "value" => "t1.micro" , "caption" => "Micro" },
170
+ { "value" => "m1.large" , "caption" => "Large" },
171
+ { "value" => "m1.xlarge" , "caption" => "Extra Large" },
172
+ { "value" => "m2.xlarge" , "caption" => "High-Memory Extra Large" },
173
+ { "value" => "m2.2xlarge", "caption" => "High-Memory Double Extra Large" },
174
+ { "value" => "m2.4xlarge", "caption" => "High-Memory Quadruple Extra Large" },
175
+ { "value" => "c1.xlarge" , "caption" => "High-CPU Extra Large" },
176
+ ].freeze,
177
+ }.freeze
178
+
179
+ end