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,58 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'active_support/core_ext/hash/keys'
3
+
4
+ # リソース制御ドライバ
5
+ driver :resource_control_driver do
6
+
7
+ on :'仮想サーバ起動リクエストイベント' do
8
+ prop = event.properties.dup
9
+
10
+ raise "malformed event (packet corruption?), provider not found. #{event.inspect}" unless pid = prop.delete("provider_id")
11
+ provider = Tengine::Resource::Provider.find(pid)
12
+
13
+ name = provider._type
14
+ raise "logical bug or DB corruption, unknown class #{name}" unless klass = ObjectSpace.each_object(Class).select {|i| i.name == name }.first
15
+
16
+ [["physical_server", Tengine::Resource::PhysicalServer],
17
+ ["virtual_server_image", Tengine::Resource::VirtualServerImage],
18
+ ["virtual_server_type", Tengine::Resource::VirtualServerType],
19
+ ].each do |(i, j)|
20
+ if k = prop.delete("#{i}_id")
21
+ prop[i] = j.find(k)
22
+ end
23
+ end
24
+
25
+ provider.becomes(klass).create_virtual_servers prop.symbolize_keys
26
+ end
27
+
28
+ on :'仮想サーバ停止リクエストイベント' do
29
+ prop = event.properties.dup
30
+
31
+ raise "malformed event (packet corruption?), provider not found. #{event.inspect}" unless pid = prop.delete("provider_id")
32
+ provider = Tengine::Resource::Provider.find(pid)
33
+
34
+ name = provider._type
35
+ raise "logical bug or DB corruption, unknown class #{name}" unless klass = ObjectSpace.each_object(Class).select {|i| i.name == name }.first
36
+
37
+ raise "malformed event (packet corruption?), no server to stop. #{event.inspect}" unless servers = prop.delete("virtual_servers")
38
+
39
+ servers.map! do |i|
40
+ Tengine::Resource::VirtualServer.find(i)
41
+ end
42
+
43
+ provider.becomes(klass).terminate_virtual_servers servers
44
+ end
45
+
46
+ on :'Tengine::Resource::VirtualServer.created.tengine_resource_watchd' # 仮想サーバ登録通知イベント
47
+ on :'Tengine::Resource::VirtualServer.updated.tengine_resource_watchd' # 仮想サーバ変更通知イベント
48
+ on :'Tengine::Resource::VirtualServer.destroyed.tengine_resource_watchd' # 仮想サーバ削除通知イベント
49
+ on :'Tengine::Resource::PhysicalServer.created.tengine_resource_watchd' # 物理サーバ登録通知イベント
50
+ on :'Tengine::Resource::PhysicalServer.updated.tengine_resource_watchd' # 物理サーバ変更通知イベント
51
+ on :'Tengine::Resource::PhysicalServer.destroyed.tengine_resource_watchd' # 物理サーバ削除通知イベント
52
+ on :'Tengine::Resource::VirtualServerImage.created.tengine_resource_watchd' # 仮想サーバイメージ登録通知イベント
53
+ on :'Tengine::Resource::VirtualServerImage.updated.tengine_resource_watchd' # 仮想サーバイメージ変更通知イベント
54
+ on :'Tengine::Resource::VirtualServerImage.destroyed.tengine_resource_watchd' # 仮想サーバイメージ削除通知イベント
55
+ on :'Tengine::Resource::VirtualServerType.created.tengine_resource_watchd' # 仮想サーバタイプ登録通知イベント
56
+ on :'Tengine::Resource::VirtualServerType.updated.tengine_resource_watchd' # 仮想サーバタイプ変更通知イベント
57
+ on :'Tengine::Resource::VirtualServerType.destroyed.tengine_resource_watchd' # 仮想サーバタイプ削除通知イベント
58
+ end
@@ -0,0 +1,134 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tempfile'
3
+ require 'net/ssh'
4
+ require 'active_support/core_ext/hash/keys'
5
+
6
+ class << Net::SSH
7
+ alias __tengine_resource_net_ssh_backed_up_start__ start
8
+ private :__tengine_resource_net_ssh_backed_up_start__
9
+
10
+ # Extended Net::SSH.start
11
+ #
12
+ # SYNOPSIS:
13
+ #
14
+ # A: Net::SSH.start(hostname, credential) {|ctx| ... }
15
+ # B: Net::SSH.start(hostname, user, credential) {|ctx| ... }
16
+ # C: Net::SSH.start(hostname, credential, other_opts) {|ctx| ... }
17
+ # D: Net::SSH.start(hostname, user, other_opts) {|ctx| ... }
18
+ # E: Net::SSH.start(hostname, other_opts) {|ctx| ... }
19
+ #
20
+ # ARGUMENTS:
21
+ #
22
+ # @param [String] hostname Secure Shell host to connect to.
23
+ # @param [String] user Account to use.
24
+ # @param [Tengine::Resource::Credential] credential Credential info.
25
+ # @param [Hash] other_opts Keyword arguments.
26
+ #
27
+ # DESCRIPTION:
28
+ #
29
+ # Orchestate those given arguments to start SSH connection.
30
+ #
31
+ # RETURNS:
32
+ #
33
+ # Yields or returns a Net::SSH::Connection.
34
+ #
35
+ def start host, obj1, obj2=nil, &block
36
+ user = nil
37
+ hash = nil
38
+
39
+ case obj1
40
+ when String
41
+ user = obj1
42
+ when Hash
43
+ hash = obj1.symbolize_keys
44
+ when Tengine::Resource::Credential
45
+ hash = obj1.auth_values.symbolize_keys
46
+ else
47
+ raise TypeError, "#{obj1.class} not expected (expected String)"
48
+ end
49
+
50
+ hash ||= Hash.new
51
+ case obj2
52
+ when NilClass
53
+ # OK, takes nothing
54
+ when Hash
55
+ hash.merge!(obj2.symbolize_keys) {|k, v1, v2|
56
+ raise ArgumentError, "#{k} specified twice in both credential and hash arguments"
57
+ }
58
+ when Tengine::Resource::Credential
59
+ hash.merge! obj2.auth_values.symbolize_keys {|k, v1, v2|
60
+ raise ArgumentError, "#{k} specified twice in both credential and hash arguments"
61
+ }
62
+ else
63
+ raise TypeError, "#{obj1.class} not expected (expected #{String})"
64
+ end
65
+
66
+ u2 = hash.delete(:username)
67
+ k2 = hash.delete(:private_keys)
68
+
69
+ raise ArgumentError, "username specified twice in both ordinal and optional arguments" if user and u2
70
+ user ||= u2
71
+ raise ArgumentError, "username mandatory" unless user
72
+
73
+ argh = %w[
74
+ auth_methods
75
+ compression
76
+ compression_level
77
+ config
78
+ encryption
79
+ forward_agent
80
+ global_known_hosts_file
81
+ hmac
82
+ host_key
83
+ host_key_alias
84
+ host_name
85
+ kex
86
+ keys
87
+ key_data
88
+ keys_only
89
+ logger
90
+ paranoid
91
+ passphrase
92
+ password
93
+ port
94
+ properties
95
+ proxy
96
+ rekey_blocks_limit
97
+ rekey_limit
98
+ rekey_packet_limit
99
+ timeout
100
+ user
101
+ user_known_hosts_file
102
+ verbose
103
+ ].map(&:intern).inject(Hash.new) do |r, k|
104
+ r[k] = hash.delete(k) if hash.has_key? k
105
+ r
106
+ end
107
+
108
+ raise ArgumentError, "unknown optional argument(s): #{hash.keys.join(', ')}" unless hash.empty?
109
+
110
+ if k2
111
+ k2 = [k2] unless k2.is_a? Array
112
+ Dir.mktmpdir(nil, File.expand_path("../../../../tmp", __FILE__)) do |dir|
113
+ begin
114
+ k3 = k2.map do |k|
115
+ fp = Tempfile.new("pk", dir)
116
+ fp.write(k)
117
+ fp.chmod(0400)
118
+ fp.flush
119
+ fp # no close
120
+ end
121
+ k4 = k3.map {|i| File.expand_path(i.path) }
122
+ argh[:keys] ||= []
123
+ argh[:keys].concat k4
124
+
125
+ return __tengine_resource_net_ssh_backed_up_start__ host, user, argh, &block
126
+ ensure
127
+ k3.each {|i| i.close(:real) }
128
+ end
129
+ end
130
+ else
131
+ return __tengine_resource_net_ssh_backed_up_start__ host, user, argh, &block
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,25 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'mongoid'
3
+
4
+ require 'yaml'
5
+ require 'tengine_event'
6
+ require 'tengine/support/yaml_with_erb'
7
+
8
+ # http://mongoid.org/docs/callbacks/observers.html
9
+ class Tengine::Resource::Observer < Mongoid::Observer
10
+ include Tengine::Event::ModelNotifiable
11
+
12
+ prefix = "tengine/resource/"
13
+ observe *%w[physical_server virtual_server virtual_server_image virtual_server_type].map{|name| :"#{prefix}#{name}" }
14
+
15
+ def event_sender
16
+ @event_sender = Tengine::Event.default_sender
17
+ end
18
+
19
+ SUFFIX = "tengine_resource_watchd".freeze
20
+
21
+ def event_type_name_suffix
22
+ SUFFIX
23
+ end
24
+
25
+ end
@@ -0,0 +1,7 @@
1
+ class Tengine::Resource::PhysicalServer < Tengine::Resource::Server
2
+ field :cpu_cores, :type => Integer
3
+ field :memory_size, :type => Integer
4
+
5
+ belongs_to :provider, :index => true, :inverse_of => :physical_servers,
6
+ :class_name => "Tengine::Resource::Provider"
7
+ end
@@ -0,0 +1,82 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'mongoid'
3
+
4
+ class Tengine::Resource::Provider
5
+ autoload :Ec2, 'tengine/resource/provider/ec2'
6
+ autoload :Wakame, 'tengine/resource/provider/wakame'
7
+
8
+ include Mongoid::Document
9
+ include Mongoid::Timestamps
10
+ include Tengine::Core::Validation
11
+ include Tengine::Core::FindByName
12
+
13
+ field :name, :type => String
14
+ field :description, :type => String
15
+ field :polling_interval, :type => Integer, :default => 10 # プロバイダへの問い合わせ間隔
16
+ field :retry_interval, :type => Integer, :default => 10 # プロバイダへの問い合わせリトライ間隔
17
+ field :retry_count, :type => Integer, :default => 30 # プロバイダへの問い合わせリトライ回数
18
+ field :properties, :type => Hash, :default => {}
19
+
20
+ validates :name, :presence => true, :uniqueness => true, :format => BASE_NAME.options
21
+ index :name, :unique => true
22
+
23
+ with_options(:inverse_of => :provider, :dependent => :destroy) do |c|
24
+ c.has_many :physical_servers , :class_name => "Tengine::Resource::PhysicalServer"
25
+ c.has_many :virtual_servers , :class_name => "Tengine::Resource::VirtualServer"
26
+ c.has_many :virtual_server_images , :class_name => "Tengine::Resource::VirtualServerImage"
27
+ c.has_many :virtual_server_types , :class_name => "Tengine::Resource::VirtualServerType"
28
+ end
29
+
30
+ # 仮想サーバタイプの監視
31
+ def virtual_server_type_watch ; raise NotImplementedError end
32
+ # 物理サーバの監視
33
+ def physical_server_watch ; raise NotImplementedError end
34
+ # 仮想サーバの監視
35
+ def virtual_server_watch ; raise NotImplementedError end
36
+ # 仮想サーバイメージの監視
37
+ def virtual_server_image_watch ; raise NotImplementedError end
38
+
39
+ def update_physical_servers ; raise NotImplementedError end
40
+ def update_virtual_servers ; raise NotImplementedError end
41
+ def update_virtual_server_imagess; raise NotImplementedError end
42
+
43
+ private
44
+ def update_physical_servers_by(hashs)
45
+ found_ids = []
46
+ hashs.each do |hash|
47
+ server = self.physical_servers.where(:provided_id => hash[:provided_id]).first
48
+ if server
49
+ server.update_attributes(:status => hash[:status])
50
+ else
51
+ server = self.physical_servers.create!(
52
+ :provided_id => hash[:provided_id],
53
+ :name => hash[:name],
54
+ :status => hash[:status])
55
+ end
56
+ found_ids << server.id
57
+ end
58
+ self.physical_servers.not_in(:_id => found_ids).update_all(:status => "not_found")
59
+ end
60
+
61
+ def update_virtual_servers_by(hashs)
62
+ found_ids = []
63
+ hashs.each do |hash|
64
+ server = self.virtual_servers.where(:provided_id => hash[:provided_id]).first
65
+ if server
66
+ server.update_attributes(hash)
67
+ else
68
+ server = self.virtual_servers.create!(hash.merge(:name => hash[:provided_id]))
69
+ end
70
+ found_ids << server.id
71
+ end
72
+ self.virtual_servers.not_in(:_id => found_ids).destroy_all
73
+ end
74
+
75
+ class << self
76
+ def find_or_create_by_name!(attrs)
77
+ result = self.first(:conditions => {:name => attrs[:name]})
78
+ result ||= self.create!(attrs)
79
+ result
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,187 @@
1
+ # -*- coding: utf-8 -*-
2
+ class Tengine::Resource::Provider::Ec2 < Tengine::Resource::Provider
3
+
4
+ field :connection_settings, :type => Hash
5
+
6
+ def update_physical_servers
7
+ connect do |conn|
8
+ # ec2.describe_availability_zones #=> [{:region_name=>"us-east-1",
9
+ # :zone_name=>"us-east-1a",
10
+ # :zone_state=>"available"}, ... ]
11
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeAvailabilityZones.html
12
+ hashs = conn.describe_availability_zones.map do |hash|
13
+ {
14
+ :provided_id => hash[:zone_name],
15
+ :name => hash[:zone_name],
16
+ :status => hash[:zone_state],
17
+ }
18
+ end
19
+ update_physical_servers_by(hashs)
20
+ end
21
+ end
22
+
23
+ def update_virtual_servers
24
+ connect do |conn|
25
+ # http://rightscale.rubyforge.org/right_aws_gem_doc/
26
+ # ec2.describe_instances #=>
27
+ # [{:aws_image_id => "ami-e444444d",
28
+ # :aws_reason => "",
29
+ # :aws_state_code => "16",
30
+ # :aws_owner => "000000000888",
31
+ # :aws_instance_id => "i-123f1234",
32
+ # :aws_reservation_id => "r-aabbccdd",
33
+ # :aws_state => "running",
34
+ # :dns_name => "domU-12-34-67-89-01-C9.usma2.compute.amazonaws.com",
35
+ # :ssh_key_name => "staging",
36
+ # :aws_groups => ["default"],
37
+ # :private_dns_name => "domU-12-34-67-89-01-C9.usma2.compute.amazonaws.com",
38
+ # :aws_instance_type => "m1.small",
39
+ # :aws_launch_time => "2008-1-1T00:00:00.000Z"},
40
+ # :aws_availability_zone => "us-east-1b",
41
+ # :aws_kernel_id => "aki-ba3adfd3",
42
+ # :aws_ramdisk_id => "ari-badbad00",
43
+ # ..., {...}]
44
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeInstances.html
45
+ hashs = conn.describe_instances.map do |hash|
46
+ result = {
47
+ :provided_id => hash.delete(:aws_instance_id),
48
+ :provided_image_id => hash.delete(:aws_image_id),
49
+ :status => hash.delete(:aws_state),
50
+ }
51
+ hash.delete(:aws_state_code)
52
+ result[:properties] = hash
53
+ result[:addresses] = {
54
+ :dns_name => hash.delete(:dns_name),
55
+ :ip_address => hash.delete(:ip_address),
56
+ :private_dns_name => hash.delete(:private_dns_name),
57
+ :private_ip_address => hash.delete(:private_ip_address),
58
+ }
59
+ result
60
+ end
61
+ update_virtual_servers_by(hashs)
62
+ end
63
+ end
64
+
65
+ def update_virtual_server_images
66
+ connect do |conn|
67
+ hashs = conn.describe_images.map do |hash|
68
+ { :provided_id => hash.delete(:aws_id), }
69
+ end
70
+ update_virtual_server_images_by(hashs)
71
+ end
72
+ end
73
+
74
+ # @param [String] name Name template for created virtual servers
75
+ # @param [Tengine::Resource::VirtualServerImage] image Virtual server image object
76
+ # @param [Tengine::Resource::VirtualServerType] type Virtual server type object
77
+ # @param [String] physical Data center name to put virtual machines (availability zone)
78
+ # @param [String] description What this virtual server is
79
+ # @param [Numeric] min_count Minimum number of vortial servers to boot
80
+ # @param [Numeric] max_count Maximum number of vortial servers to boot
81
+ # @param [Array<Strng>] group_ids Array of names of security group IDs
82
+ # @param [Strng] key_name Name of root key to sue
83
+ # @param [Strng] user_data User-specified
84
+ # @param [Strng] kernel_id Kernel image ID
85
+ # @param [Strng] ramdisk_id Ramdisk image ID
86
+ # @return [Array<Tengine::Resource::VirtualServer>]
87
+ def create_virtual_servers name, image, type, physical, description, min_count, max_count, group_ids, key_name, user_data = "", kernel_id, ramdisk_id
88
+ connect {|conn|
89
+ results = conn.run_instances(
90
+ image.provided_id,
91
+ min_count,
92
+ max_count,
93
+ group_ids,
94
+ key_name,
95
+ user_data,
96
+ nil, # <- addressing_type
97
+ type.provided_id,
98
+ kernel_id,
99
+ ramdisk_id,
100
+ physical,
101
+ nil # <- block_device_mappings
102
+ )
103
+ yield if block_given? # テスト用のブロックの呼び出し
104
+ results.map.with_index {|hash, idx|
105
+ provided_id = hash.delete(:aws_instance_id)
106
+ if server = self.virtual_servers.find(:first, :conditions => {:provided_id => provided_id})
107
+ server
108
+ else
109
+ host_server_provided_id = hash[:aws_availability_zone]
110
+ host_server_provided_id = physical if host_server_provided_id.nil? || host_server_provided_id.blank?
111
+ # findではなくfirstで検索しているので、もしhost_server_provided_idで指定されるサーバが見つからなくても
112
+ # host_serverがnilとして扱われるが、仮想サーバ自身の登録は行われます
113
+ host_server = (host_server_provided_id && !host_server_provided_id.blank?) ?
114
+ Tengine::Resource::PhysicalServer.first(:conditions => {:provided_id => host_server_provided_id}) : nil
115
+ begin
116
+ self.virtual_servers.create!(
117
+ :name => sprintf("%s%03d", name, idx + 1), # 1 origin
118
+ :address_order => address_order,
119
+ :description => description,
120
+ :provided_id => provided_id,
121
+ :provided_image_id => hash.delete(:aws_image_id),
122
+ :provided_type_id => hash.delete(:aws_instance_type),
123
+ :host_server_id => host_server ? host_server.id : nil,
124
+ :status => hash.delete(:aws_state),
125
+ :properties => hash,
126
+ :addresses => {
127
+ # :dns_name => hash.delete(:dns_name),
128
+ # :ip_address => hash.delete(:ip_address),
129
+ # :private_dns_name => hash.delete(:private_dns_name),
130
+ # :private_ip_address => hash.delete(:private_ip_address),
131
+ })
132
+ rescue Mongo::OperationFailure => e
133
+ raise e unless e.message =~ /E11000 duplicate key error/
134
+ self.virtual_servers.find(:first, :conditions => {:provided_id => provided_id}) or
135
+ raise "VirtualServer not found for #{provided_id}"
136
+ rescue Mongoid::Errors::Validations => e
137
+ raise e unless e.document.errors[:provided_id].any?{|s| s =~ /taken/}
138
+ self.virtual_servers.find(:first, :conditions => {:provided_id => provided_id}) or
139
+ raise "VirtualServer not found for #{provided_id}"
140
+ end
141
+ end
142
+ }
143
+ }
144
+ end
145
+
146
+ def terminate_virtual_servers servers
147
+ connect do |conn|
148
+ # http://rightscale.rubyforge.org/right_aws_gem_doc/classes/RightAws/Ec2.html#M000287
149
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-TerminateInstances.html
150
+ conn.terminate_instances(servers.map {|i| i.provided_id }).map do |hash|
151
+ serv = self.virtual_servers.where(:provided_id => hash[:aws_instance_id]).first
152
+ serv.update_attributes(:status => hash[:aws_current_state_name]) if serv
153
+ serv
154
+ end
155
+ end
156
+ end
157
+
158
+ # 仮想サーバタイプの監視
159
+ def virtual_server_type_watch
160
+ # ec2から取得する情報はありません
161
+ end
162
+
163
+ # 物理サーバの監視
164
+ def physical_server_watch ; raise NotImplementedError end
165
+ # 仮想サーバの監視
166
+ def virtual_server_watch ; raise NotImplementedError end
167
+ # 仮想サーバイメージの監視
168
+ def virtual_server_image_watch ; raise NotImplementedError end
169
+
170
+ private
171
+ def address_order
172
+ @@address_order ||= %w"private_ip_address private_dns_name ip_address dns_name".each(&:freeze).freeze
173
+ end
174
+
175
+ def connect
176
+ klass = (ENV['EC2_DUMMY'] == "true") ? Tengine::Resource::Credential::Ec2::Dummy : RightAws::Ec2
177
+ connection = klass.new(
178
+ self.connection_settings[:access_key],
179
+ self.connection_settings[:secret_access_key],
180
+ {
181
+ :logger => Tengine.logger,
182
+ :region => self.connection_settings[:region]
183
+ }
184
+ )
185
+ yield connection
186
+ end
187
+ end