chef-vpc-toolkit 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +14 -0
- data/COPYING +1 -1
- data/README.rdoc +29 -7
- data/VERSION +1 -1
- data/config/server_group.json +2 -2
- data/lib/chef-vpc-toolkit.rb +7 -2
- data/lib/chef-vpc-toolkit/chef-0.9.bash +1 -0
- data/lib/chef-vpc-toolkit/chef_bootstrap/fedora.bash +3 -3
- data/lib/chef-vpc-toolkit/chef_bootstrap/ubuntu.bash +1 -0
- data/lib/chef-vpc-toolkit/chef_installer.rb +8 -1
- data/lib/chef-vpc-toolkit/cloud-servers-vpc/client.rb +186 -0
- data/lib/chef-vpc-toolkit/cloud-servers-vpc/connection.rb +146 -0
- data/lib/chef-vpc-toolkit/cloud-servers-vpc/server.rb +113 -0
- data/lib/chef-vpc-toolkit/cloud-servers-vpc/server_group.rb +387 -0
- data/lib/chef-vpc-toolkit/cloud-servers-vpc/ssh_public_key.rb +25 -0
- data/lib/chef-vpc-toolkit/cloud-servers-vpc/vpn_network_interface.rb +29 -0
- data/lib/chef-vpc-toolkit/util.rb +20 -16
- data/lib/chef-vpc-toolkit/vpn_network_manager.rb +16 -16
- data/lib/chef-vpc-toolkit/xml_util.rb +15 -0
- data/rake/chef_vpc_toolkit.rake +194 -155
- data/test/client_test.rb +108 -0
- data/test/server_group_test.rb +259 -0
- data/test/server_test.rb +66 -0
- data/test/test_helper.rb +21 -0
- data/test/util_test.rb +7 -0
- data/test/vpn_network_manager_test.rb +7 -5
- metadata +17 -8
- data/lib/chef-vpc-toolkit/cloud_servers_vpc.rb +0 -393
- data/lib/chef-vpc-toolkit/http_util.rb +0 -118
- data/test/cloud_servers_vpc_test.rb +0 -129
@@ -0,0 +1,113 @@
|
|
1
|
+
module ChefVPCToolkit
|
2
|
+
|
3
|
+
module CloudServersVPC
|
4
|
+
|
5
|
+
class Server
|
6
|
+
|
7
|
+
attr_accessor :id
|
8
|
+
attr_accessor :name
|
9
|
+
attr_accessor :description
|
10
|
+
attr_accessor :external_ip_addr
|
11
|
+
attr_accessor :internal_ip_addr
|
12
|
+
attr_accessor :cloud_server_id_number
|
13
|
+
attr_accessor :flavor_id
|
14
|
+
attr_accessor :image_id
|
15
|
+
attr_accessor :server_group_id
|
16
|
+
attr_accessor :openvpn_server
|
17
|
+
attr_accessor :retry_count
|
18
|
+
attr_accessor :error_message
|
19
|
+
attr_accessor :status
|
20
|
+
|
21
|
+
def initialize(options={})
|
22
|
+
@id=options[:id].to_i
|
23
|
+
@name=options[:name]
|
24
|
+
@description=options[:description] or @description=@name
|
25
|
+
@external_ip_addr=options[:external_ip_addr]
|
26
|
+
@internal_ip_addr=options[:internal_ip_addr]
|
27
|
+
@cloud_server_id_number=options[:cloud_server_id_number].to_i
|
28
|
+
@flavor_id=options[:flavor_id].to_i
|
29
|
+
@image_id=options[:image_id].to_i
|
30
|
+
@server_group_id=options[:server_group_id].to_i
|
31
|
+
@openvpn_server = [true, "true"].include?(options[:openvpn_server])
|
32
|
+
@retry_count=options[:retry_count].to_i or 0
|
33
|
+
@error_message=options[:error_message]
|
34
|
+
@status=options[:status]
|
35
|
+
end
|
36
|
+
|
37
|
+
def openvpn_server?
|
38
|
+
return @openvpn_server
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_xml
|
42
|
+
|
43
|
+
xml = Builder::XmlMarkup.new
|
44
|
+
xml.tag! "server" do |server|
|
45
|
+
server.id(@id)
|
46
|
+
server.name(@name)
|
47
|
+
server.description(@description)
|
48
|
+
server.status(@status) if @status
|
49
|
+
server.tag! "external-ip-addr", @external_ip_addr if @external_ip_addr
|
50
|
+
server.tag! "internal-ip-addr", @internal_ip_addr if @internal_ip_addr
|
51
|
+
server.tag! "cloud-server-id-number", @cloud_server_id_number if @cloud_server_id_number
|
52
|
+
server.tag! "flavor-id", @flavor_id
|
53
|
+
server.tag! "image-id", @image_id
|
54
|
+
server.tag! "server-group-id", @server_group_id
|
55
|
+
server.tag! "openvpn-server", "true" if openvpn_server?
|
56
|
+
server.tag! "error-message", @error_message if @error_message
|
57
|
+
end
|
58
|
+
xml.target!
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.from_xml(xml)
|
63
|
+
|
64
|
+
server=nil
|
65
|
+
dom = REXML::Document.new(xml)
|
66
|
+
REXML::XPath.each(dom, "/*") do |sg_xml|
|
67
|
+
|
68
|
+
server=Server.new(
|
69
|
+
:id => XMLUtil.element_text(sg_xml, "id").to_i,
|
70
|
+
:name => XMLUtil.element_text(sg_xml, "name"),
|
71
|
+
:flavor_id => XMLUtil.element_text(sg_xml, "flavor-id"),
|
72
|
+
:image_id => XMLUtil.element_text(sg_xml, "image-id"),
|
73
|
+
:description => XMLUtil.element_text(sg_xml, "description"),
|
74
|
+
:cloud_server_id_number => XMLUtil.element_text(sg_xml, "cloud-server-id-number"),
|
75
|
+
:description => XMLUtil.element_text(sg_xml, "description"),
|
76
|
+
:external_ip_addr => XMLUtil.element_text(sg_xml, "external-ip-addr"),
|
77
|
+
:internal_ip_addr => XMLUtil.element_text(sg_xml, "internal-ip-addr"),
|
78
|
+
:server_group_id => XMLUtil.element_text(sg_xml, "server-group-id"),
|
79
|
+
:openvpn_server => XMLUtil.element_text(sg_xml, "openvpn_server"),
|
80
|
+
:retry_count => XMLUtil.element_text(sg_xml, "retry-count"),
|
81
|
+
:error_message => XMLUtil.element_text(sg_xml, "error-message"),
|
82
|
+
:status => XMLUtil.element_text(sg_xml, "status")
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
server
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
def rebuild
|
91
|
+
|
92
|
+
raise "Error: Rebuilding the OpenVPN server is not supported at this time." if openvpn_server?
|
93
|
+
|
94
|
+
Connection.post("/servers/#{@id}/rebuild", {})
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
def delete
|
99
|
+
Connection.delete("/servers/#{@id}.xml")
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.create(server)
|
103
|
+
|
104
|
+
xml=Connection.post("/servers.xml", server.to_xml)
|
105
|
+
server=Server.from_xml(xml)
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
@@ -0,0 +1,387 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'builder'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'rexml/document'
|
5
|
+
require 'rexml/xpath'
|
6
|
+
|
7
|
+
module ChefVPCToolkit
|
8
|
+
|
9
|
+
module CloudServersVPC
|
10
|
+
|
11
|
+
class ServerGroup
|
12
|
+
|
13
|
+
@@data_dir=File.join(CHEF_VPC_PROJECT, "tmp", "server_groups")
|
14
|
+
|
15
|
+
def self.data_dir
|
16
|
+
@@data_dir
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.data_dir=(dir)
|
20
|
+
@@data_dir=dir
|
21
|
+
end
|
22
|
+
|
23
|
+
CONFIG_FILE = CHEF_VPC_PROJECT + File::SEPARATOR + "config" + File::SEPARATOR + "server_group.json"
|
24
|
+
|
25
|
+
attr_accessor :id
|
26
|
+
attr_accessor :name
|
27
|
+
attr_accessor :description
|
28
|
+
attr_accessor :domain_name
|
29
|
+
attr_accessor :vpn_network
|
30
|
+
attr_accessor :vpn_subnet
|
31
|
+
attr_accessor :owner_name
|
32
|
+
|
33
|
+
attr_reader :ssh_public_keys
|
34
|
+
|
35
|
+
def initialize(options={})
|
36
|
+
@id=options[:id]
|
37
|
+
@name=options[:name]
|
38
|
+
@description=options[:description]
|
39
|
+
@domain_name=options[:domain_name]
|
40
|
+
@vpn_network=options[:vpn_network] or @vpn_network="172.19.0.0"
|
41
|
+
@vpn_subnet=options[:vpn_subnet] or @vpn_subnet="255.255.128.0"
|
42
|
+
@owner_name=options[:owner_name] or @owner_name=ENV['USER']
|
43
|
+
|
44
|
+
@servers=[]
|
45
|
+
@ssh_public_keys=[]
|
46
|
+
end
|
47
|
+
|
48
|
+
def server(name)
|
49
|
+
@servers.select {|s| s.name == name}[0] if @servers.size > 0
|
50
|
+
end
|
51
|
+
|
52
|
+
def servers
|
53
|
+
@servers
|
54
|
+
end
|
55
|
+
|
56
|
+
def vpn_gateway_name
|
57
|
+
@servers.select {|s| s.openvpn_server? }[0].name if @servers.size > 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def vpn_gateway_ip
|
61
|
+
@servers.select {|s| s.openvpn_server? }[0].external_ip_addr if @servers.size > 0
|
62
|
+
end
|
63
|
+
|
64
|
+
def ssh_public_keys
|
65
|
+
@ssh_public_keys
|
66
|
+
end
|
67
|
+
|
68
|
+
# generate a Server Group XML from server_group.json
|
69
|
+
def self.from_json_config(json)
|
70
|
+
|
71
|
+
json_hash=JSON.parse(json)
|
72
|
+
|
73
|
+
sg=ServerGroup.new(
|
74
|
+
:name => json_hash["name"],
|
75
|
+
:description => json_hash["description"],
|
76
|
+
:domain_name => json_hash["domain_name"],
|
77
|
+
:vpn_network => json_hash["vpn_network"],
|
78
|
+
:vpn_subnet => json_hash["vpn_subnet"]
|
79
|
+
)
|
80
|
+
json_hash["servers"].each_pair do |server_name, server_config|
|
81
|
+
sg.servers << Server.new(
|
82
|
+
:name => server_name,
|
83
|
+
:description => server_config["description"],
|
84
|
+
:flavor_id => server_config["flavor_id"],
|
85
|
+
:image_id => server_config["image_id"],
|
86
|
+
:openvpn_server => server_config["openvpn_server"]
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
# automatically add a key for the current user
|
91
|
+
sg.ssh_public_keys << SshPublicKey.new(
|
92
|
+
:description => "#{ENV['USER']}'s public key",
|
93
|
+
:public_key => Util.load_public_key
|
94
|
+
|
95
|
+
)
|
96
|
+
|
97
|
+
return sg
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_xml
|
102
|
+
|
103
|
+
xml = Builder::XmlMarkup.new
|
104
|
+
xml.tag! "server-group" do |sg|
|
105
|
+
sg.id(@id)
|
106
|
+
sg.name(@name)
|
107
|
+
sg.description(@description)
|
108
|
+
sg.tag! "owner-name", @owner_name
|
109
|
+
sg.tag! "domain-name", @domain_name
|
110
|
+
sg.tag! "vpn-network", @vpn_network
|
111
|
+
sg.tag! "vpn-subnet", @vpn_subnet
|
112
|
+
sg.servers("type" => "array") do |xml_servers|
|
113
|
+
self.servers.each do |server|
|
114
|
+
xml_servers.server do |xml_server|
|
115
|
+
xml_server.id(server.id)
|
116
|
+
xml_server.name(server.name)
|
117
|
+
xml_server.description(server.description)
|
118
|
+
xml_server.tag! "flavor-id", server.flavor_id
|
119
|
+
xml_server.tag! "image-id", server.image_id
|
120
|
+
xml_server.tag! "cloud-server-id-number", server.cloud_server_id_number if server.cloud_server_id_number
|
121
|
+
xml_server.tag! "status", server.status if server.status
|
122
|
+
xml_server.tag! "external-ip-addr", server.external_ip_addr if server.external_ip_addr
|
123
|
+
xml_server.tag! "internal-ip-addr", server.internal_ip_addr if server.internal_ip_addr
|
124
|
+
xml_server.tag! "error-message", server.error_message if server.error_message
|
125
|
+
xml_server.tag! "retry-count", server.retry_count if server.retry_count
|
126
|
+
if server.openvpn_server?
|
127
|
+
xml_server.tag! "openvpn-server", "true", { "type" => "boolean"}
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
sg.tag! "ssh-public-keys", { "type" => "array"} do |xml_ssh_pub_keys|
|
133
|
+
self.ssh_public_keys.each do |ssh_public_key|
|
134
|
+
xml_ssh_pub_keys.tag! "ssh-public-key" do |xml_ssh_pub_key|
|
135
|
+
xml_ssh_pub_key.description ssh_public_key.description
|
136
|
+
xml_ssh_pub_key.tag! "public-key", ssh_public_key.public_key
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
xml.target!
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.from_xml(xml)
|
146
|
+
|
147
|
+
sg=nil
|
148
|
+
dom = REXML::Document.new(xml)
|
149
|
+
REXML::XPath.each(dom, "/server-group") do |sg_xml|
|
150
|
+
sg=ServerGroup.new(
|
151
|
+
:name => XMLUtil.element_text(sg_xml, "name"),
|
152
|
+
:id => XMLUtil.element_text(sg_xml, "id").to_i,
|
153
|
+
:domain_name => XMLUtil.element_text(sg_xml, "domain-name"),
|
154
|
+
:description => XMLUtil.element_text(sg_xml, "description"),
|
155
|
+
:vpn_network => XMLUtil.element_text(sg_xml, "vpn-network"),
|
156
|
+
:vpn_subnet => XMLUtil.element_text(sg_xml, "vpn-subnet")
|
157
|
+
)
|
158
|
+
REXML::XPath.each(dom, "//server") do |server_xml|
|
159
|
+
|
160
|
+
server=Server.new(
|
161
|
+
:id => XMLUtil.element_text(server_xml, "id").to_i,
|
162
|
+
:name => XMLUtil.element_text(server_xml, "name"),
|
163
|
+
:cloud_server_id_number => XMLUtil.element_text(server_xml, "cloud-server-id-number"),
|
164
|
+
:status => XMLUtil.element_text(server_xml, "status"),
|
165
|
+
:external_ip_addr => XMLUtil.element_text(server_xml, "external-ip-addr"),
|
166
|
+
:internal_ip_addr => XMLUtil.element_text(server_xml, "internal-ip-addr"),
|
167
|
+
:error_message => XMLUtil.element_text(server_xml, "error-message"),
|
168
|
+
:image_id => XMLUtil.element_text(server_xml, "image-id"),
|
169
|
+
:flavor_id => XMLUtil.element_text(server_xml, "flavor-id"),
|
170
|
+
:retry_count => XMLUtil.element_text(server_xml, "retry-count"),
|
171
|
+
:openvpn_server => XMLUtil.element_text(server_xml, "openvpn-server")
|
172
|
+
)
|
173
|
+
sg.servers << server
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
sg
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
def pretty_print
|
182
|
+
|
183
|
+
puts "Group ID: #{@id}"
|
184
|
+
puts "name: #{@name}"
|
185
|
+
puts "description: #{@description}"
|
186
|
+
puts "domain name: #{@domain_name}"
|
187
|
+
puts "VPN gateway IP: #{self.vpn_gateway_ip}"
|
188
|
+
puts "Servers:"
|
189
|
+
servers.each do |server|
|
190
|
+
puts "\tname: #{server.name} (id: #{server.id})"
|
191
|
+
puts "\tstatus: #{server.status}"
|
192
|
+
if server.openvpn_server?
|
193
|
+
puts "\tOpenVPN server: #{server.openvpn_server?}"
|
194
|
+
end
|
195
|
+
if server.error_message then
|
196
|
+
puts "\tlast error message: #{server.error_message}"
|
197
|
+
end
|
198
|
+
puts "\t--"
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
def server_names
|
204
|
+
|
205
|
+
names=[]
|
206
|
+
|
207
|
+
servers.each do |server|
|
208
|
+
if block_given? then
|
209
|
+
yield server.name
|
210
|
+
else
|
211
|
+
names << server.name
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
names
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
def cache_to_disk
|
220
|
+
FileUtils.mkdir_p(@@data_dir)
|
221
|
+
File.open(File.join(@@data_dir, "#{@id}.xml"), 'w') do |f|
|
222
|
+
f.chmod(0600)
|
223
|
+
f.write(self.to_xml)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def delete
|
228
|
+
|
229
|
+
Connection.delete("/server_groups/#{@id}.xml")
|
230
|
+
out_file=File.join(@@data_dir, "#{@id}.xml")
|
231
|
+
File.delete(out_file) if File.exists?(out_file)
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
# Poll the server group until it is online.
|
236
|
+
# :timeout - max number of seconds to wait before raising an exception.
|
237
|
+
# Defaults to 1500
|
238
|
+
def poll_until_online(options={})
|
239
|
+
|
240
|
+
timeout=options[:timeout] or timeout = ENV['TIMEOUT']
|
241
|
+
if timeout.nil? or timeout.empty? then
|
242
|
+
timeout=1500 # defaults to 25 minutes
|
243
|
+
end
|
244
|
+
|
245
|
+
online = false
|
246
|
+
count=0
|
247
|
+
until online or (count*20) >= timeout.to_i do
|
248
|
+
count+=1
|
249
|
+
begin
|
250
|
+
sg=ServerGroup.fetch(:id => @id, :source => "remote")
|
251
|
+
|
252
|
+
online=true
|
253
|
+
sg.servers.each do |server|
|
254
|
+
if ["Pending", "Rebuilding"].include?(server.status) then
|
255
|
+
online=false
|
256
|
+
end
|
257
|
+
if server.status == "Failed" then
|
258
|
+
raise "Failed to create server group with the following message: #{server.error_message}"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
if not online
|
262
|
+
yield sg if block_given?
|
263
|
+
sleep 20
|
264
|
+
end
|
265
|
+
rescue EOFError
|
266
|
+
end
|
267
|
+
end
|
268
|
+
if (count*20) >= timeout.to_i then
|
269
|
+
raise "Timeout waiting for server groups to come online."
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
def self.create(sg)
|
275
|
+
|
276
|
+
xml=Connection.post("/server_groups.xml", sg.to_xml)
|
277
|
+
sg=ServerGroup.from_xml(xml)
|
278
|
+
sg.cache_to_disk
|
279
|
+
sg
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
# Fetch a server group. The following options are available:
|
284
|
+
#
|
285
|
+
# :id - The ID of the server group to fetch. Defaults to ENV['GROUP_ID']
|
286
|
+
# :source - valid options are 'remote' and 'cache'
|
287
|
+
def self.fetch(options={})
|
288
|
+
|
289
|
+
source = options[:source] or source = "remote"
|
290
|
+
id=options[:id] or id = ENV['GROUP_ID']
|
291
|
+
if id.nil? then
|
292
|
+
group=ServerGroup.most_recent
|
293
|
+
raise "No server group files exist." if group.nil?
|
294
|
+
id=group.id
|
295
|
+
end
|
296
|
+
|
297
|
+
if source == "remote" then
|
298
|
+
xml=Connection.get("/server_groups/#{id}.xml")
|
299
|
+
ServerGroup.from_xml(xml)
|
300
|
+
elsif source == "cache" then
|
301
|
+
out_file=File.join(@@data_dir, "#{id}.xml")
|
302
|
+
raise "No server group files exist." if not File.exists?(out_file)
|
303
|
+
ServerGroup.from_xml(IO.read(out_file))
|
304
|
+
else
|
305
|
+
raise "Invalid fetch :source specified."
|
306
|
+
end
|
307
|
+
|
308
|
+
end
|
309
|
+
|
310
|
+
# :source - valid options are 'remote' and 'cache'
|
311
|
+
def self.list(options={})
|
312
|
+
|
313
|
+
source = options[:source] or source = "cache"
|
314
|
+
server_groups=[]
|
315
|
+
if source == "remote" then
|
316
|
+
xml=Connection.get("/server_groups.xml")
|
317
|
+
dom = REXML::Document.new(xml)
|
318
|
+
REXML::XPath.each(dom, "//server-group") do |group_xml|
|
319
|
+
server_groups << ServerGroup.from_xml(group_xml.to_s)
|
320
|
+
end
|
321
|
+
else
|
322
|
+
Dir[File.join(ServerGroup.data_dir, '*.xml')].each do |file|
|
323
|
+
server_groups << ServerGroup.from_xml(IO.read(file))
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
server_groups
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
def self.most_recent
|
332
|
+
server_groups=[]
|
333
|
+
Dir[File.join(@@data_dir, "*.xml")].each do |file|
|
334
|
+
server_groups << ServerGroup.from_xml(IO.read(file))
|
335
|
+
end
|
336
|
+
if server_groups.size > 0 then
|
337
|
+
server_groups.sort { |a,b| b.id <=> a.id }[0]
|
338
|
+
else
|
339
|
+
nil
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def os_types
|
344
|
+
|
345
|
+
os_types={}
|
346
|
+
self.servers.each do |server|
|
347
|
+
os_type = case server.image_id
|
348
|
+
when 51 # Centos 5.5
|
349
|
+
"centos"
|
350
|
+
when 187811 # Centos 5.4
|
351
|
+
"centos"
|
352
|
+
when 71 # Fedora 14
|
353
|
+
"fedora"
|
354
|
+
when 53 # Fedora 13
|
355
|
+
"fedora"
|
356
|
+
when 17 # Fedora 12
|
357
|
+
"fedora"
|
358
|
+
when 14 # RHEL 5.4
|
359
|
+
"rhel"
|
360
|
+
when 62 # RHEL 5.5
|
361
|
+
"rhel"
|
362
|
+
when 69 # Ubuntu 10.10
|
363
|
+
"ubuntu"
|
364
|
+
when 49 # Ubuntu 10.04
|
365
|
+
"ubuntu"
|
366
|
+
when 14362 # Ubuntu 9.10
|
367
|
+
"ubuntu"
|
368
|
+
when 8 # Ubuntu 9.04
|
369
|
+
"ubuntu"
|
370
|
+
else
|
371
|
+
"unknown"
|
372
|
+
end
|
373
|
+
if block_given? then
|
374
|
+
yield server.name, os_type
|
375
|
+
else
|
376
|
+
os_types.store(server.name, os_type)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
os_types
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
end
|
384
|
+
|
385
|
+
end
|
386
|
+
|
387
|
+
end
|