ironfan 5.0.11 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.gitmodules +3 -0
- data/Gemfile +8 -26
- data/Gemfile.lock +38 -41
- data/NOTES-REALM.md +172 -0
- data/Rakefile +19 -77
- data/config/ubuntu12.04-ironfan.erb +7 -0
- data/ironfan.gemspec +28 -225
- data/lib/chef/cluster_knife.rb +26 -0
- data/lib/chef/knife/bootstrap/ubuntu12.04-ironfan.erb +7 -0
- data/lib/chef/knife/cluster_bootstrap.rb +1 -3
- data/lib/chef/knife/cluster_diff.rb +2 -8
- data/lib/chef/knife/cluster_kick.rb +1 -3
- data/lib/chef/knife/cluster_kill.rb +1 -2
- data/lib/chef/knife/cluster_launch.rb +17 -34
- data/lib/chef/knife/cluster_list.rb +6 -5
- data/lib/chef/knife/cluster_proxy.rb +1 -3
- data/lib/chef/knife/cluster_pry.rb +1 -2
- data/lib/chef/knife/cluster_show.rb +6 -7
- data/lib/chef/knife/cluster_ssh.rb +10 -8
- data/lib/chef/knife/cluster_start.rb +1 -2
- data/lib/chef/knife/cluster_stop.rb +1 -2
- data/lib/chef/knife/cluster_sync.rb +2 -3
- data/lib/chef/knife/ironfan_knife_common.rb +58 -18
- data/lib/chef/knife/ironfan_script.rb +0 -3
- data/lib/ironfan/broker/computer.rb +14 -11
- data/lib/ironfan/broker.rb +17 -12
- data/lib/ironfan/cookbook_requirements.rb +155 -0
- data/lib/ironfan/dsl/cloud.rb +2 -0
- data/lib/ironfan/dsl/cluster.rb +25 -15
- data/lib/ironfan/dsl/component.rb +12 -15
- data/lib/ironfan/dsl/compute.rb +10 -8
- data/lib/ironfan/dsl/ec2.rb +2 -26
- data/lib/ironfan/dsl/facet.rb +16 -14
- data/lib/ironfan/dsl/openstack.rb +147 -0
- data/lib/ironfan/dsl/realm.rb +23 -16
- data/lib/ironfan/dsl/security_group.rb +29 -0
- data/lib/ironfan/dsl/server.rb +14 -5
- data/lib/ironfan/dsl/static.rb +63 -0
- data/lib/ironfan/dsl/vsphere.rb +1 -0
- data/lib/ironfan/dsl.rb +1 -134
- data/lib/ironfan/headers.rb +19 -0
- data/lib/ironfan/provider/chef/node.rb +3 -2
- data/lib/ironfan/provider/ec2/machine.rb +10 -14
- data/lib/ironfan/provider/ec2/security_group.rb +58 -43
- data/lib/ironfan/provider/openstack/elastic_ip.rb +96 -0
- data/lib/ironfan/provider/openstack/keypair.rb +78 -0
- data/lib/ironfan/provider/openstack/machine.rb +371 -0
- data/lib/ironfan/provider/openstack/security_group.rb +224 -0
- data/lib/ironfan/provider/openstack.rb +69 -0
- data/lib/ironfan/provider/static/machine.rb +192 -0
- data/lib/ironfan/provider/static.rb +23 -0
- data/lib/ironfan/provider.rb +58 -1
- data/lib/ironfan/requirements.rb +17 -1
- data/lib/ironfan/version.rb +3 -0
- data/lib/ironfan.rb +107 -172
- data/spec/chef/cluster_bootstrap_spec.rb +2 -7
- data/spec/chef/cluster_launch_spec.rb +1 -2
- data/spec/fixtures/realms/samurai.rb +26 -0
- data/spec/integration/minimal-chef-repo/clusters/.gitkeep +0 -0
- data/spec/integration/minimal-chef-repo/config/.gitkeep +0 -0
- data/spec/integration/minimal-chef-repo/knife/credentials/.gitignore +1 -0
- data/spec/integration/minimal-chef-repo/knife/credentials/certificates/.gitkeep +0 -0
- data/spec/integration/minimal-chef-repo/knife/credentials/client_keys/.gitkeep +0 -0
- data/spec/integration/minimal-chef-repo/knife/credentials/data_bag_keys/.gitkeep +0 -0
- data/spec/integration/minimal-chef-repo/knife/credentials/ec2_certs/.gitkeep +0 -0
- data/spec/integration/minimal-chef-repo/knife/credentials/ec2_keys/.gitkeep +0 -0
- data/spec/integration/minimal-chef-repo/knife/credentials/ironfantest-validator.pem +27 -0
- data/spec/integration/minimal-chef-repo/knife/credentials/ironfantester.pem +27 -0
- data/spec/integration/minimal-chef-repo/tasks/.gitkeep +0 -0
- data/spec/ironfan/cluster_spec.rb +1 -2
- data/spec/ironfan/diff_spec.rb +0 -2
- data/spec/ironfan/dsl_spec.rb +6 -3
- data/spec/ironfan/ec2/cloud_provider_spec.rb +17 -18
- data/spec/ironfan/ec2/elb_spec.rb +44 -41
- data/spec/ironfan/ec2/security_group_spec.rb +45 -47
- data/spec/ironfan/manifest_spec.rb +0 -1
- data/spec/ironfan/plugin_spec.rb +55 -40
- data/spec/ironfan/realm_spec.rb +42 -30
- data/spec/spec_helper.rb +17 -31
- data/spec/{spec_helper → support}/dummy_chef.rb +0 -0
- data/spec/{spec_helper → support}/dummy_diff_drawer.rb +0 -0
- metadata +78 -155
- data/.rspec +0 -2
- data/.yardopts +0 -19
- data/VERSION +0 -2
- data/chefignore +0 -41
- data/notes/Future-development-proposals.md +0 -266
- data/notes/Home.md +0 -55
- data/notes/INSTALL-cloud_setup.md +0 -103
- data/notes/INSTALL.md +0 -134
- data/notes/Ironfan-Roadmap.md +0 -70
- data/notes/Upgrading-to-v4.md +0 -66
- data/notes/advanced-superpowers.md +0 -16
- data/notes/aws_servers.jpg +0 -0
- data/notes/aws_user_key.png +0 -0
- data/notes/cookbook-versioning.md +0 -11
- data/notes/core_concepts.md +0 -200
- data/notes/declaring_volumes.md +0 -3
- data/notes/design_notes-aspect_oriented_devops.md +0 -36
- data/notes/design_notes-ci_testing.md +0 -169
- data/notes/design_notes-cookbook_event_ordering.md +0 -249
- data/notes/design_notes-meta_discovery.md +0 -59
- data/notes/ec2-pricing_and_capacity.md +0 -75
- data/notes/ec2-pricing_and_capacity.numbers +0 -0
- data/notes/homebase-layout.txt +0 -102
- data/notes/knife-cluster-commands.md +0 -21
- data/notes/named-cloud-objects.md +0 -11
- data/notes/opscode_org_key.png +0 -0
- data/notes/opscode_user_key.png +0 -0
- data/notes/philosophy.md +0 -13
- data/notes/rake_tasks.md +0 -24
- data/notes/renamed-recipes.txt +0 -142
- data/notes/silverware.md +0 -85
- data/notes/style_guide.md +0 -300
- data/notes/tips_and_troubleshooting.md +0 -92
- data/notes/walkthrough-hadoop.md +0 -168
- data/notes/walkthrough-web.md +0 -166
- data/spec/fixtures/gunbai.rb +0 -24
- data/spec/test_config.rb +0 -20
- data/tasks/chef_config.rake +0 -38
@@ -21,7 +21,16 @@ module Ironfan
|
|
21
21
|
def self.expected_ids(computer)
|
22
22
|
return unless computer.server
|
23
23
|
ec2 = computer.server.cloud(:ec2)
|
24
|
-
|
24
|
+
|
25
|
+
server_groups = computer.server.security_groups
|
26
|
+
cloud_groups = ec2.security_groups
|
27
|
+
|
28
|
+
result = []
|
29
|
+
[server_groups, cloud_groups].each do |container|
|
30
|
+
container.keys.each { |name| result.push( group_name_with_vpc(name,ec2.vpc) )}
|
31
|
+
end
|
32
|
+
return result.uniq
|
33
|
+
|
25
34
|
end
|
26
35
|
|
27
36
|
def name()
|
@@ -83,44 +92,46 @@ module Ironfan
|
|
83
92
|
|
84
93
|
# Iterate over all of the security group information, keeping track of
|
85
94
|
# any groups that must exist and any authorizations that must be ensured
|
86
|
-
cloud.security_groups.
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
95
|
+
[computer.server.security_groups, cloud.security_groups].each do |container|
|
96
|
+
container.values.each do |dsl_group|
|
97
|
+
|
98
|
+
groups_to_create << dsl_group.name
|
99
|
+
|
100
|
+
groups_to_create << dsl_group.group_authorized.map do |other_group|
|
101
|
+
most_appropriate_group_name(other_group, cloud.vpc)
|
102
|
+
end
|
103
|
+
|
104
|
+
groups_to_create << dsl_group.group_authorized_by.map do |other_group|
|
105
|
+
most_appropriate_group_name(other_group, cloud.vpc)
|
106
|
+
end
|
107
|
+
|
108
|
+
authorizations_to_ensure << dsl_group.group_authorized.map do |other_group|
|
109
|
+
{
|
110
|
+
:grantor => most_appropriate_group_name(dsl_group.name, cloud.vpc),
|
111
|
+
:grantee => most_appropriate_group_name(other_group, cloud.vpc),
|
112
|
+
:grantee_type => :group,
|
113
|
+
:range => WIDE_OPEN,
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
authorizations_to_ensure << dsl_group.group_authorized_by.map do |other_group|
|
118
|
+
{
|
119
|
+
:grantor => most_appropriate_group_name(other_group, cloud.vpc),
|
120
|
+
:grantee => most_appropriate_group_name(dsl_group.name, cloud.vpc),
|
121
|
+
:grantee_type => :group,
|
122
|
+
:range => WIDE_OPEN,
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
authorizations_to_ensure << dsl_group.range_authorizations.map do |range_auth|
|
127
|
+
range, cidr, protocol = range_auth
|
128
|
+
{
|
129
|
+
:grantor => group_name_with_vpc(dsl_group.name, cloud.vpc),
|
130
|
+
:grantee => { :cidr_ip => cidr, :ip_protocol => protocol },
|
131
|
+
:grantee_type => :cidr,
|
132
|
+
:range => range,
|
133
|
+
}
|
134
|
+
end
|
124
135
|
end
|
125
136
|
end
|
126
137
|
end
|
@@ -180,15 +191,19 @@ module Ironfan
|
|
180
191
|
#
|
181
192
|
# Utility
|
182
193
|
#
|
183
|
-
def self.ensure_groups
|
194
|
+
def self.ensure_groups computer
|
184
195
|
return unless Ec2.applicable computer
|
185
196
|
# Ensure the security_groups include those for cluster & facet
|
186
197
|
# FIXME: This violates the DSL's immutability; it should be
|
187
198
|
# something calculated from within the DSL construction
|
188
199
|
Ironfan.todo("CODE SMELL: violation of DSL immutability: #{caller}")
|
189
|
-
|
190
|
-
|
191
|
-
|
200
|
+
server = computer.server
|
201
|
+
cluster_name = "#{computer.server.realm_name}-#{computer.server.cluster_name}"
|
202
|
+
server.security_group computer.server.realm_name
|
203
|
+
realm_group = server.security_group cluster_name
|
204
|
+
realm_group.authorized_by_group realm_group.name
|
205
|
+
facet_name = "#{computer.server.realm_name}-#{computer.server.cluster_name}-#{computer.server.facet_name}"
|
206
|
+
server.security_group facet_name
|
192
207
|
end
|
193
208
|
|
194
209
|
# Try an authorization, ignoring duplicates (this is easier than correlating).
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Ironfan
|
2
|
+
class Provider
|
3
|
+
class OpenStack
|
4
|
+
class ElasticIp < Ironfan::Provider::Resource
|
5
|
+
delegate :addresses, :associate_address,
|
6
|
+
:allocate_address, :auto_elastic_ip, :destroy,
|
7
|
+
:domain, :domain=, :describe_addresses, :disassociate_address,
|
8
|
+
:domain, :id, :network_interface_id, :network_interface_id=,
|
9
|
+
:save, :server=,
|
10
|
+
:server, :server_id, :server_id=,
|
11
|
+
:to => :adaptee
|
12
|
+
|
13
|
+
def self.shared?() true; end
|
14
|
+
def self.multiple?() false; end
|
15
|
+
def self.resource_type() :elastic_ip; end
|
16
|
+
def self.expected_ids(computer) [ computer.server.openstack.elastic_ip ]; end
|
17
|
+
|
18
|
+
def public_ip() adaptee.ip ; end
|
19
|
+
def name() adaptee.ip ; end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Discovery
|
23
|
+
#
|
24
|
+
|
25
|
+
def self.load!(cluster=nil)
|
26
|
+
OpenStack.connection.addresses.each do |eip|
|
27
|
+
register eip
|
28
|
+
|
29
|
+
# The rest of this definition shows relevant information when -VV
|
30
|
+
# is passed to knife and aids in troubleshooting any refusal to
|
31
|
+
# attach Elastic IPs
|
32
|
+
Chef::Log.debug( "OpenStack Pool: #{eip.pool}" )
|
33
|
+
if eip.ip.nil?
|
34
|
+
Chef::Log.debug( "no Elastic IPs currently allocated" )
|
35
|
+
else
|
36
|
+
Chef::Log.debug( "available ip match: #{eip.ip}" )
|
37
|
+
Chef::Log.debug( "available allocation_id match: #{eip.id}" )
|
38
|
+
end
|
39
|
+
Chef::Log.debug( "----------------------" )
|
40
|
+
end
|
41
|
+
|
42
|
+
cluster.servers.each do |s|
|
43
|
+
next if s.openstack.elastic_ip.nil?
|
44
|
+
if recall? s.openstack.elastic_ip
|
45
|
+
Chef::Log.debug( "Cluster elastic_ip matches #{s.openstack.elastic_ip}" )
|
46
|
+
else
|
47
|
+
Chef::Log.debug( "No matching Elastic IP for #{s.openstack.elastic_ip}" )
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Manipulation
|
55
|
+
#
|
56
|
+
|
57
|
+
def self.save!(computer)
|
58
|
+
return unless computer.created?
|
59
|
+
return unless elastic_ip = computer.server.openstack.elastic_ip
|
60
|
+
return unless recall? elastic_ip
|
61
|
+
# also, in the case of VPC Elastic IPs, can discover and use allocation_id to attach a VPC Elastic IP.
|
62
|
+
return unless computer.server.openstack.methods.include?(:elastic_ip)
|
63
|
+
if ( computer.server.openstack.elastic_ip.nil?)
|
64
|
+
if computer.server.addresses.nil?
|
65
|
+
OpenStack.connection.allocate_address
|
66
|
+
load!
|
67
|
+
elastic_ip = computer.server.addresses.first.public_ip
|
68
|
+
Chef::Log.debug( "allocating new Elastic IP address" )
|
69
|
+
else
|
70
|
+
# Second, :elastic_ip is set, has an address available to use but has no set value available in facet definition.
|
71
|
+
elastic_ip = computer.server.addresses.first.public_ip
|
72
|
+
Chef::Log.debug( "using first available Elastic IP address" )
|
73
|
+
end
|
74
|
+
elsif ( !computer.server.openstack.elastic_ip.nil? or cloud.vpc.nil? )
|
75
|
+
# Third, :elastic_ip is set, has an address available to use, has a set value in facet definition and is not VPC.
|
76
|
+
elastic_ip = computer.server.openstack.elastic_ip
|
77
|
+
Chef::Log.debug( "using requested Elastic IP address" )
|
78
|
+
elsif ( computer.server.opentsack.elastic_ip.nil? )
|
79
|
+
# Fourth, is exactly like Third but on a VPC domain. (this is functionaility for attaching VPC Elastic IPS)
|
80
|
+
allocation_id = computer.server.openstack.allocation_id
|
81
|
+
Chef::Log.debug( "using Elastic IP address matched to given Allocation ID" )
|
82
|
+
else
|
83
|
+
ui.fatal("You have set both :elastic_ip and :auto_elastic_ip in your facet definition; which are mutually exclusive.")
|
84
|
+
end
|
85
|
+
Ironfan.step(computer.name, "associating Elastic IP #{elastic_ip}", :blue)
|
86
|
+
Ironfan.unless_dry_run do
|
87
|
+
Ironfan.safely do
|
88
|
+
allocation_id = recall(elastic_ip).id
|
89
|
+
OpenSTack.connection.associate_address( computer.machine.id, elastic_ip, nil, allocation_id )
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Ironfan
|
2
|
+
class Provider
|
3
|
+
class OpenStack
|
4
|
+
|
5
|
+
class Keypair < Ironfan::Provider::Resource
|
6
|
+
delegate :_dump, :collection, :collection=, :connection,
|
7
|
+
:connection=, :destroy, :fingerprint, :fingerprint=, :identity,
|
8
|
+
:identity=, :name, :name=, :new_record?, :public_key,
|
9
|
+
:public_key=, :reload, :requires, :requires_one, :save,
|
10
|
+
:symbolize_keys, :wait_for, :writable?, :write,
|
11
|
+
:to => :adaptee
|
12
|
+
|
13
|
+
field :key_filename, String, :default => ->{ "#{Keypair.key_dir}/#{name}.pem" }
|
14
|
+
|
15
|
+
def self.shared? ; true ; end
|
16
|
+
def self.multiple? ; false ; end
|
17
|
+
def self.resource_type ; :keypair ; end
|
18
|
+
def self.expected_ids(computer)
|
19
|
+
[computer.server.cluster_name]
|
20
|
+
end
|
21
|
+
|
22
|
+
def private_key
|
23
|
+
File.open(key_filename, "rb").read
|
24
|
+
end
|
25
|
+
|
26
|
+
def private_key=(body=nil)
|
27
|
+
File.open(key_filename, "w", 0600){|f| f.print( body ) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
"<%-15s %-12s>" % [self.class.handle, name]
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Discovery
|
36
|
+
#
|
37
|
+
def self.load!(cluster=nil)
|
38
|
+
OpenStack.connection.key_pairs.each do |keypair|
|
39
|
+
register keypair unless keypair.blank?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def receive_adaptee(obj)
|
44
|
+
obj = Openstack.connection.key_pairs.new(obj) if obj.is_a?(Hash)
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Manipulation
|
50
|
+
#
|
51
|
+
|
52
|
+
def self.prepare!(computers)
|
53
|
+
return if computers.empty?
|
54
|
+
name = computers.values[0].server.cluster_name
|
55
|
+
return if recall? name
|
56
|
+
Ironfan.step(name, "creating key pair for #{name}", :blue)
|
57
|
+
result = OpenStack.connection.create_key_pair(name)
|
58
|
+
private_key = result.body["keypair"]["private_key"]
|
59
|
+
load! # Reload to get the native object
|
60
|
+
recall(name).private_key = private_key
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Utility
|
65
|
+
#
|
66
|
+
|
67
|
+
def self.key_dir
|
68
|
+
return Chef::Config.openstack_key_dir if Chef::Config.openstack_key_dir
|
69
|
+
dir = "#{ENV['HOME']}/.chef/credentials/openstack_keys"
|
70
|
+
warn "Please set 'openstack_key_dir' in your knife.rb. Will use #{dir} as a default"
|
71
|
+
dir
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,371 @@
|
|
1
|
+
module Ironfan
|
2
|
+
class Provider
|
3
|
+
class OpenStack
|
4
|
+
|
5
|
+
class Machine < Ironfan::IaasProvider::Machine
|
6
|
+
delegate :_dump, :addresses, :ami_launch_index, :ami_launch_index=,
|
7
|
+
:architecture, :architecture=, :availability_zone,
|
8
|
+
:availability_zone=, :block_device_mapping, :block_device_mapping=,
|
9
|
+
:client_token, :client_token=, :collection, :collection=,
|
10
|
+
:connection, :connection=, :console_output,
|
11
|
+
:destroy,
|
12
|
+
:ebs_optimized, :flavor, :flavor=,
|
13
|
+
:iam_instance_profile,
|
14
|
+
:iam_instance_profile=, :iam_instance_profile_arn=,
|
15
|
+
:iam_instance_profile_name=, :id, :id=, :identity, :identity=,
|
16
|
+
:image, :image=, :instance_initiated_shutdown_behavior,
|
17
|
+
:instance_initiated_shutdown_behavior=, :ip_address, :kernel_id,
|
18
|
+
:kernel_id=, :key_name, :key_name=, :key_pair, :key_pair=,
|
19
|
+
:monitor=, :monitoring, :monitoring=, :network_interfaces,
|
20
|
+
:network_interfaces=, :new_record?, :password, :password=,
|
21
|
+
:placement_group, :placement_group=, :platform, :platform=,
|
22
|
+
:private_key, :private_key=,
|
23
|
+
:private_key_path, :private_key_path=, :product_codes,
|
24
|
+
:product_codes=,
|
25
|
+
:public_key, :public_key=, :public_key_path, :public_key_path=,
|
26
|
+
:ramdisk_id, :ramdisk_id=, :ready?, :reason, :reason=, :reboot,
|
27
|
+
:reload, :requires, :requires_one, :root_device_name,
|
28
|
+
:root_device_name=, :root_device_type, :root_device_type=, :save,
|
29
|
+
:scp, :scp_download, :scp_upload, :security_group_ids,
|
30
|
+
:security_group_ids=, :setup, :ssh, :ssh_port, :sshable?, :start,
|
31
|
+
:state, :state=, :state_reason, :state_reason=, :stop, :subnet_id,
|
32
|
+
:subnet_id=, :symbolize_keys, :tenancy, :tenancy=,
|
33
|
+
:user_data, :user_data=, :username, :username=, :volumes,
|
34
|
+
:wait_for, :name, :name=,
|
35
|
+
:metadata, :metadata=,
|
36
|
+
:to => :adaptee
|
37
|
+
|
38
|
+
def self.shared?() false; end
|
39
|
+
def self.multiple?() false; end
|
40
|
+
# def self.resource_type() Ironfan::IaasProvider::Machine; end
|
41
|
+
def self.resource_type() :machine; end
|
42
|
+
def self.expected_ids(computer) [computer.server.full_name]; end
|
43
|
+
|
44
|
+
def tags
|
45
|
+
t = metadata.to_hash.update({"Name" => @adaptee.name})
|
46
|
+
return t.keys.inject({}) {|h,k| h[k]=t[k]; h[k.to_sym]=t[k]; h}
|
47
|
+
end
|
48
|
+
|
49
|
+
def vpc_id
|
50
|
+
return nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def created_at
|
54
|
+
return @adaptee.created
|
55
|
+
end
|
56
|
+
|
57
|
+
def flavor_id
|
58
|
+
# sometimes flavor comes back empty - especially right after the machine has been launched
|
59
|
+
return flavor && flavor["id"]
|
60
|
+
end
|
61
|
+
|
62
|
+
def flavor_name
|
63
|
+
fl = OpenStack.flavor_id_hash[ flavor_id ]
|
64
|
+
fl && fl.name
|
65
|
+
end
|
66
|
+
|
67
|
+
def image_id
|
68
|
+
return image[:id]
|
69
|
+
end
|
70
|
+
|
71
|
+
def groups ; Array(@adaptee.security_groups) ; end
|
72
|
+
|
73
|
+
def public_hostname ; private_ip_address ; end
|
74
|
+
def dns_name ; public_ip_address ; end
|
75
|
+
|
76
|
+
def keypair ; key_pair ; end
|
77
|
+
|
78
|
+
def created?
|
79
|
+
not ['HARD_DELETED', 'SOFT_DELETED', ].include? state
|
80
|
+
end
|
81
|
+
def pending?
|
82
|
+
state == "BUILD"
|
83
|
+
end
|
84
|
+
def running?
|
85
|
+
state == "ACTIVE"
|
86
|
+
end
|
87
|
+
def stopping?
|
88
|
+
state == "STOPPING"
|
89
|
+
end
|
90
|
+
|
91
|
+
def stopped?
|
92
|
+
state == "STOPPED"
|
93
|
+
end
|
94
|
+
|
95
|
+
def error?
|
96
|
+
state == "ERROR"
|
97
|
+
end
|
98
|
+
|
99
|
+
def start
|
100
|
+
machine = self
|
101
|
+
adaptee.start
|
102
|
+
adaptee.wait_for{ machine.pending? or machine.running? or machine.error? }
|
103
|
+
end
|
104
|
+
|
105
|
+
def stop
|
106
|
+
machine = self
|
107
|
+
adaptee.stop
|
108
|
+
adaptee.wait_for{ machine.stopping? or machine.stopped? }
|
109
|
+
end
|
110
|
+
|
111
|
+
def perform_after_launch_tasks?
|
112
|
+
true
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_display(style,values={})
|
116
|
+
# style == :minimal
|
117
|
+
values["State"] = (state || "unknown").to_sym
|
118
|
+
values["MachineID"] = id
|
119
|
+
values["Public IP"] = public_ip_address
|
120
|
+
values["Private IP"] = private_ip_address
|
121
|
+
values["Created On"] = created_at.to_date
|
122
|
+
return values if style == :minimal
|
123
|
+
|
124
|
+
# style == :default
|
125
|
+
values["Flavor"] = flavor_name
|
126
|
+
values["AZ"] = availability_zone
|
127
|
+
return values if style == :default
|
128
|
+
|
129
|
+
# style == :expanded
|
130
|
+
values["Image"] = image_id
|
131
|
+
#values["Volumes"] = volumes.map(&:id).join(', ')
|
132
|
+
values["SSH Key"] = key_name
|
133
|
+
values
|
134
|
+
end
|
135
|
+
|
136
|
+
def ssh_key
|
137
|
+
keypair = cloud.keypair || computer.server.cluster_name
|
138
|
+
end
|
139
|
+
|
140
|
+
def private_ip_address
|
141
|
+
adaptee.private_ip_address rescue nil
|
142
|
+
end
|
143
|
+
|
144
|
+
def public_ip_address
|
145
|
+
adaptee.floating_ip_address rescue nil
|
146
|
+
end
|
147
|
+
|
148
|
+
def to_s
|
149
|
+
"<%-15s %-12s %-25s %-25s %-15s %-15s %-12s %-12s %s:%s>" % [
|
150
|
+
self.class.handle, id, created_at, name, private_ip_address, public_ip_address, flavor_name, availability_zone, key_name, groups.join(',') ]
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# Discovery
|
155
|
+
#
|
156
|
+
def self.load!(cluster=nil)
|
157
|
+
OpenStack.connection.servers.each do |fs|
|
158
|
+
machine = new(:adaptee => fs)
|
159
|
+
if (not machine.created?)
|
160
|
+
next unless Ironfan.chef_config[:include_terminated]
|
161
|
+
remember machine, :append_id => "terminated:#{machine.id}"
|
162
|
+
elsif recall? machine.name
|
163
|
+
machine.bogus << :duplicate_machines
|
164
|
+
recall(machine.name).bogus << :duplicate_machines
|
165
|
+
remember machine, :append_id => "duplicate:#{machine.id}"
|
166
|
+
else # never seen it
|
167
|
+
remember machine
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def receive_adaptee(obj)
|
173
|
+
obj = OpenStack.connection.servers.new(obj) if obj.is_a?(Hash)
|
174
|
+
super
|
175
|
+
end
|
176
|
+
|
177
|
+
# Find active machines that haven't matched, but should have,
|
178
|
+
# make sure all bogus machines have a computer to attach to
|
179
|
+
# for display purposes
|
180
|
+
def self.validate_resources!(computers)
|
181
|
+
recall.each_value do |machine|
|
182
|
+
next unless machine.users.empty? and machine.name
|
183
|
+
if machine.name.match("^#{computers.cluster.name}-")
|
184
|
+
machine.bogus << :unexpected_machine
|
185
|
+
end
|
186
|
+
next unless machine.bogus?
|
187
|
+
fake = Ironfan::Broker::Computer.new
|
188
|
+
fake[:machine] = machine
|
189
|
+
fake.name = machine.name
|
190
|
+
machine.users << fake
|
191
|
+
computers << fake
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
#
|
196
|
+
# Manipulation
|
197
|
+
#
|
198
|
+
def self.create!(computer)
|
199
|
+
Ironfan.todo("CODE SMELL: overly large method: #{caller}")
|
200
|
+
return if computer.machine? and computer.machine.created?
|
201
|
+
Ironfan.step(computer.name,"creating cloud machine", :green)
|
202
|
+
#
|
203
|
+
errors = lint(computer)
|
204
|
+
if errors.present? then raise ArgumentError, "Failed validation: #{errors.inspect}" ; end
|
205
|
+
#
|
206
|
+
launch_desc = launch_description(computer)
|
207
|
+
Chef::Log.debug(JSON.pretty_generate(launch_desc))
|
208
|
+
|
209
|
+
# tag the computer correctly
|
210
|
+
tags = {
|
211
|
+
'cluster' => computer.server.cluster_name,
|
212
|
+
'facet' => computer.server.facet_name,
|
213
|
+
'index' => computer.server.index.to_s,
|
214
|
+
'name' => computer.name,
|
215
|
+
'creator' => Chef::Config.username
|
216
|
+
}
|
217
|
+
|
218
|
+
Ironfan.safely do
|
219
|
+
fog_server = OpenStack.connection.servers.create(launch_desc)
|
220
|
+
machine = Machine.new(:adaptee => fog_server)
|
221
|
+
computer.machine = machine
|
222
|
+
remember machine, :id => computer.name
|
223
|
+
|
224
|
+
Ironfan.step(fog_server.id,"waiting for machine to be ready", :gray)
|
225
|
+
Ironfan.tell_you_thrice :name => fog_server.id,
|
226
|
+
:problem => "server unavailable",
|
227
|
+
:error_class => Fog::Errors::Error do
|
228
|
+
fog_server.wait_for { state == "ACTIVE" }
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
computer.machine.metadata.set(tags)
|
234
|
+
|
235
|
+
#OpenStack.ensure_tags(tags, computer.machine)
|
236
|
+
|
237
|
+
# no volumes at the momnt
|
238
|
+
|
239
|
+
# register the new volumes for later save!, and tag appropriately
|
240
|
+
#computer.machine.volumes.each do |v|
|
241
|
+
# Ironfan.todo "CODE SMELL: Machine is too familiar with EbsVolume problems"
|
242
|
+
# ebs_vol = OpenStack::EbsVolume.register v
|
243
|
+
# drive = computer.drives.values.select do |drive|
|
244
|
+
# drive.volume.device == ebs_vol.device
|
245
|
+
# end.first
|
246
|
+
# drive.disk = ebs_vol
|
247
|
+
#
|
248
|
+
# vol_name = "#{computer.name}-#{drive.volume.name}"
|
249
|
+
# tags['server'] = computer.name
|
250
|
+
# tags['name'] = vol_name
|
251
|
+
# tags['Name'] = vol_name
|
252
|
+
# tags['mount_point'] = drive.volume.mount_point
|
253
|
+
# tags['device'] = drive.volume.device
|
254
|
+
# OpenStack.ensure_tags(tags,ebs_vol)
|
255
|
+
#end
|
256
|
+
end
|
257
|
+
|
258
|
+
# @returns [Hash{String, Array}] of 'what you did wrong' => [relevant, info]
|
259
|
+
def self.lint(computer)
|
260
|
+
cloud = computer.server.cloud(:openstack)
|
261
|
+
info = [computer.name, cloud.inspect]
|
262
|
+
errors = {}
|
263
|
+
server_errors = computer.server.lint
|
264
|
+
errors["Unhappy Server"] = server_errors if server_errors.present?
|
265
|
+
errors["No AMI found"] = info if cloud.image_id.blank?
|
266
|
+
errors['Missing client'] = info unless computer.client?
|
267
|
+
errors['Missing private_key'] = computer.client unless computer.private_key
|
268
|
+
#
|
269
|
+
#all_asserted_regions = [OpenStack.connection.region, cloud.region, Chef::Config[:knife][:region], Ironfan.chef_config[:region]].compact.uniq
|
270
|
+
#errors["mismatched region"] = all_asserted_regions unless all_asserted_regions.count == 1
|
271
|
+
#
|
272
|
+
errors
|
273
|
+
end
|
274
|
+
|
275
|
+
def self.launch_description(computer)
|
276
|
+
cloud = computer.server.cloud(:openstack)
|
277
|
+
user_data_hsh = {
|
278
|
+
:chef_server => Chef::Config[:chef_server_url],
|
279
|
+
:node_name => computer.name,
|
280
|
+
:organization => Chef::Config[:organization],
|
281
|
+
:cluster_name => computer.server.cluster_name,
|
282
|
+
:facet_name => computer.server.facet_name,
|
283
|
+
:facet_index => computer.server.index,
|
284
|
+
:client_key => computer.private_key
|
285
|
+
}
|
286
|
+
|
287
|
+
# main machine info
|
288
|
+
# note that Fog does not actually create tags when it creates a
|
289
|
+
# server; they and permanence are applied during sync
|
290
|
+
description = {
|
291
|
+
:image_ref => cloud.image_id,
|
292
|
+
:flavor_ref => OpenStack.flavor_hash[cloud.flavor].id,
|
293
|
+
#:vpc_id => cloud.vpc,
|
294
|
+
#:subnet_id => cloud.subnet,
|
295
|
+
:key_name => cloud.ssh_key_name(computer),
|
296
|
+
:user_data => JSON.pretty_generate(user_data_hsh),
|
297
|
+
#:block_device_mapping => block_device_mapping(computer),
|
298
|
+
:availability_zone => cloud.default_availability_zone,
|
299
|
+
#:monitoring => cloud.monitoring,
|
300
|
+
:name => computer.name,
|
301
|
+
}
|
302
|
+
|
303
|
+
description[:security_groups] = (computer.server.security_groups.keys + cloud.security_groups.keys).uniq
|
304
|
+
|
305
|
+
#description[:iam_server_certificates] = cloud.iam_server_certificates.values.map do |cert|
|
306
|
+
# IamServerCertificate.recall(IamServerCertificate.full_name(computer, cert))
|
307
|
+
#end.compact.map(&:name)
|
308
|
+
|
309
|
+
#description[:elastic_load_balancers] = cloud.elastic_load_balancers.values.map do |elb|
|
310
|
+
# ElasticLoadBalancer.recall(ElasticLoadBalancer.full_name(computer, elb))
|
311
|
+
#end.compact.map(&:name)
|
312
|
+
|
313
|
+
#if cloud.flavor_info[:placement_groupable]
|
314
|
+
# ui.warn "1.3.1 and earlier versions of Fog don't correctly support placement groups, so your nodes will land willy-nilly. We're working on a fix"
|
315
|
+
# description[:placement] = { 'groupName' => cloud.placement_group.to_s }
|
316
|
+
#end
|
317
|
+
#if cloud.flavor_info[:ebs_optimizable]
|
318
|
+
# description[:ebs_optimized] = cloud.ebs_optimized
|
319
|
+
#end
|
320
|
+
description
|
321
|
+
end
|
322
|
+
|
323
|
+
# An array of hashes with dorky-looking keys, just like Fog wants it.
|
324
|
+
def self.block_device_mapping(computer)
|
325
|
+
Ironfan.todo "CODE SMELL: Machine is too familiar with EbsVolume problems"
|
326
|
+
computer.drives.values.map do |drive|
|
327
|
+
next if drive.disk # Don't create any disc already satisfied
|
328
|
+
volume = drive.volume or next
|
329
|
+
hsh = { 'DeviceName' => volume.device }
|
330
|
+
if volume.attachable == 'ephemeral'
|
331
|
+
hsh['VirtualName'] = drive.name
|
332
|
+
# if set for creation at launch (and not already created)
|
333
|
+
elsif drive.node[:volume_id].blank? && volume.create_at_launch
|
334
|
+
if volume.snapshot_id.blank? && volume.size.blank?
|
335
|
+
raise "Must specify a size or a snapshot ID for #{volume}"
|
336
|
+
end
|
337
|
+
hsh['Ebs.SnapshotId'] = volume.snapshot_id if volume.snapshot_id.present?
|
338
|
+
hsh['Ebs.VolumeSize'] = volume.size.to_s if volume.size.present?
|
339
|
+
hsh['Ebs.DeleteOnTermination'] = (not volume.keep).to_s
|
340
|
+
else next
|
341
|
+
end
|
342
|
+
hsh
|
343
|
+
end.compact
|
344
|
+
end
|
345
|
+
|
346
|
+
def self.destroy!(computer)
|
347
|
+
return unless computer.machine?
|
348
|
+
forget computer.machine.name
|
349
|
+
computer.machine.destroy
|
350
|
+
computer.machine.reload # show the node as shutting down
|
351
|
+
end
|
352
|
+
|
353
|
+
def self.save!(computer)
|
354
|
+
return unless computer.machine?
|
355
|
+
# the EC2 API does not surface disable_api_termination as a value, so we
|
356
|
+
# have to set it every time.
|
357
|
+
permanent = computer.server.cloud(:openstack).permanent
|
358
|
+
return unless computer.created?
|
359
|
+
#Ironfan.step(computer.name, "setting termination flag #{permanent}", :blue)
|
360
|
+
#Ironfan.unless_dry_run do
|
361
|
+
# Ironfan.safely do
|
362
|
+
# OpenStack.connection.modify_instance_attribute( computer.machine.id,
|
363
|
+
# {'DisableApiTermination.Value' => computer.permanent?, })
|
364
|
+
# end
|
365
|
+
#end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|