chef-vpc-toolkit 2.1.0 → 2.2.0
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.
- 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
|