tengine_resource 0.5.13

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.
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