kytoon 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.document +5 -0
  2. data/Gemfile +15 -0
  3. data/Gemfile.lock +29 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +81 -0
  6. data/Rakefile +35 -0
  7. data/VERSION +1 -0
  8. data/config/server_group_vpc.json +14 -0
  9. data/config/server_group_xen.json +24 -0
  10. data/lib/kytoon.rb +8 -0
  11. data/lib/kytoon/providers/cloud_servers_vpc.rb +6 -0
  12. data/lib/kytoon/providers/cloud_servers_vpc/client.rb +197 -0
  13. data/lib/kytoon/providers/cloud_servers_vpc/connection.rb +148 -0
  14. data/lib/kytoon/providers/cloud_servers_vpc/server.rb +121 -0
  15. data/lib/kytoon/providers/cloud_servers_vpc/server_group.rb +401 -0
  16. data/lib/kytoon/providers/cloud_servers_vpc/ssh_public_key.rb +29 -0
  17. data/lib/kytoon/providers/cloud_servers_vpc/vpn_network_interface.rb +33 -0
  18. data/lib/kytoon/providers/xenserver.rb +1 -0
  19. data/lib/kytoon/providers/xenserver/server_group.rb +371 -0
  20. data/lib/kytoon/server_group.rb +46 -0
  21. data/lib/kytoon/ssh_util.rb +23 -0
  22. data/lib/kytoon/util.rb +118 -0
  23. data/lib/kytoon/version.rb +8 -0
  24. data/lib/kytoon/vpn/vpn_connection.rb +46 -0
  25. data/lib/kytoon/vpn/vpn_network_manager.rb +237 -0
  26. data/lib/kytoon/vpn/vpn_openvpn.rb +112 -0
  27. data/lib/kytoon/xml_util.rb +15 -0
  28. data/rake/kytoon.rake +115 -0
  29. data/test/client_test.rb +111 -0
  30. data/test/helper.rb +18 -0
  31. data/test/server_group_test.rb +253 -0
  32. data/test/server_test.rb +69 -0
  33. data/test/ssh_util_test.rb +22 -0
  34. data/test/test_helper.rb +194 -0
  35. data/test/test_kytoon.rb +7 -0
  36. data/test/util_test.rb +23 -0
  37. data/test/vpn_network_manager_test.rb +40 -0
  38. metadata +247 -0
@@ -0,0 +1,148 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'openssl'
5
+
6
+ module Kytoon
7
+
8
+ module Providers
9
+
10
+ module CloudServersVPC
11
+
12
+ class Connection
13
+
14
+ MULTI_PART_BOUNDARY="jtZ!pZ1973um"
15
+
16
+ @@http=nil
17
+ @@auth_user=nil
18
+ @@auth_password=nil
19
+
20
+ def self.init_connection
21
+
22
+ configs=Util.load_configs
23
+
24
+ base_url = configs["cloud_servers_vpc_url"]
25
+ @@auth_user = configs["cloud_servers_vpc_username"]
26
+ @@auth_password = configs["cloud_servers_vpc_password"]
27
+
28
+ ssl_key = configs["ssl_key"]
29
+ ssl_cert = configs["ssl_cert"]
30
+ ssl_ca_cert = configs["ssl_ca_cert"]
31
+
32
+ url=URI.parse(base_url)
33
+ @@http = Net::HTTP.new(url.host,url.port)
34
+
35
+ if base_url =~ /^https/
36
+ @@http.use_ssl = true
37
+ if ssl_ca_cert then
38
+ @@http.verify_mode = OpenSSL::SSL::VERIFY_PEER
39
+ else
40
+ @@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
41
+ end
42
+
43
+ if ssl_key then
44
+ pkey_data=IO.read(ssl_key)
45
+ if pkey_data =~ /^-----BEGIN RSA PRIVATE KEY-----/
46
+ @@http.key=OpenSSL::PKey::RSA.new(pkey_data)
47
+ else
48
+ @@http.key=OpenSSL::PKey::DSA.new(pkey_data)
49
+ end
50
+ end
51
+ @@http.cert=OpenSSL::X509::Certificate.new(IO.read(ssl_cert)) if ssl_cert
52
+ @@http.ca_file=ssl_ca_cert if ssl_ca_cert
53
+ end
54
+
55
+ end
56
+
57
+ def self.file_upload(url_path, file_data={}, post_data={})
58
+ init_connection if @@http.nil?
59
+ req = Net::HTTP::Post.new(url_path)
60
+
61
+ post_arr=[]
62
+ post_data.each_pair do |key, value|
63
+ post_arr << "--#{MULTI_PART_BOUNDARY}\r\n"
64
+ post_arr << "Content-Disposition: form-data; name=\"#{key}\"\r\n"
65
+ post_arr << "\r\n"
66
+ post_arr << value
67
+ post_arr << "\r\n"
68
+ end
69
+
70
+ file_data.each_pair do |name, file|
71
+ post_arr << "--#{MULTI_PART_BOUNDARY}\r\n"
72
+ post_arr << "Content-Disposition: form-data; name=\"#{name}\"; filename=\"#{File.basename(file)}\"\r\n"
73
+ post_arr << "Content-Type: text/plain\r\n"
74
+ post_arr << "\r\n"
75
+ post_arr << File.read(file)
76
+ post_arr << "\r\n--#{MULTI_PART_BOUNDARY}--\r\n"
77
+ end
78
+ post_arr << "--#{MULTI_PART_BOUNDARY}--\r\n\r\n"
79
+
80
+ req.body=post_arr.join
81
+
82
+ req.basic_auth @@auth_user, @@auth_password if @@auth_user and @@auth_password
83
+ req["Content-Type"] = "multipart/form-data, boundary=#{MULTI_PART_BOUNDARY}"
84
+
85
+ response = @@http.request(req)
86
+ case response
87
+ when Net::HTTPSuccess
88
+ return response.body
89
+ else
90
+ puts response.body
91
+ response.error!
92
+ end
93
+ end
94
+
95
+ def self.post(url_path, post_data)
96
+ init_connection if @@http.nil?
97
+ req = Net::HTTP::Post.new(url_path)
98
+ if post_data.kind_of?(String) then
99
+ req.body=post_data
100
+ elsif post_data.kind_of?(Hash) then
101
+ req.form_data=post_data
102
+ else
103
+ raise "Invalid post data type."
104
+ end
105
+ req.basic_auth @@auth_user, @@auth_password if @@auth_user and @@auth_password
106
+ response = @@http.request(req)
107
+ case response
108
+ when Net::HTTPSuccess
109
+ return response.body
110
+ else
111
+ puts response.body
112
+ response.error!
113
+ end
114
+ end
115
+
116
+ def self.get(url_path)
117
+ init_connection if @@http.nil?
118
+ req = Net::HTTP::Get.new(url_path)
119
+ req.basic_auth @@auth_user, @@auth_password if @@auth_user and @@auth_password
120
+ response = @@http.request(req)
121
+ case response
122
+ when Net::HTTPSuccess
123
+ return response.body
124
+ else
125
+ response.error!
126
+ end
127
+ end
128
+
129
+ def self.delete(url_path)
130
+ init_connection if @@http.nil?
131
+ req = Net::HTTP::Delete.new(url_path)
132
+ req.basic_auth @@auth_user, @@auth_password if @@auth_user and @@auth_password
133
+ response = @@http.request(req)
134
+ case response
135
+ when Net::HTTPSuccess
136
+ return response.body
137
+ else
138
+ response.error!
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+
146
+ end
147
+
148
+ end
@@ -0,0 +1,121 @@
1
+ module Kytoon
2
+
3
+ module Providers
4
+
5
+ module CloudServersVPC
6
+
7
+ class Server
8
+
9
+ attr_accessor :id
10
+ attr_accessor :name
11
+ attr_accessor :description
12
+ attr_accessor :external_ip_addr
13
+ attr_accessor :internal_ip_addr
14
+ attr_accessor :cloud_server_id_number
15
+ attr_accessor :flavor_id
16
+ attr_accessor :image_id
17
+ attr_accessor :server_group_id
18
+ attr_accessor :openvpn_server
19
+ attr_accessor :retry_count
20
+ attr_accessor :error_message
21
+ attr_accessor :status
22
+ attr_accessor :admin_password
23
+
24
+ def initialize(options={})
25
+ @id=options[:id].to_i
26
+ @name=options[:name]
27
+ @description=options[:description] or @description=@name
28
+ @external_ip_addr=options[:external_ip_addr]
29
+ @internal_ip_addr=options[:internal_ip_addr]
30
+ @cloud_server_id_number=options[:cloud_server_id_number]
31
+ @flavor_id=options[:flavor_id]
32
+ @image_id=options[:image_id]
33
+ @admin_password=options[:admin_password]
34
+ @server_group_id=options[:server_group_id].to_i
35
+ @openvpn_server = [true, "true"].include?(options[:openvpn_server])
36
+ @retry_count=options[:retry_count].to_i or 0
37
+ @error_message=options[:error_message]
38
+ @status=options[:status]
39
+ end
40
+
41
+ def openvpn_server?
42
+ return @openvpn_server
43
+ end
44
+
45
+ def to_xml
46
+
47
+ xml = Builder::XmlMarkup.new
48
+ xml.tag! "server" do |server|
49
+ server.id(@id)
50
+ server.name(@name)
51
+ server.description(@description)
52
+ server.status(@status) if @status
53
+ server.tag! "external-ip-addr", @external_ip_addr if @external_ip_addr
54
+ server.tag! "internal-ip-addr", @internal_ip_addr if @internal_ip_addr
55
+ server.tag! "cloud-server-id-number", @cloud_server_id_number if @cloud_server_id_number
56
+ server.tag! "flavor-id", @flavor_id
57
+ server.tag! "image-id", @image_id
58
+ server.tag! "admin-password", @admin_password
59
+ server.tag! "server-group-id", @server_group_id
60
+ server.tag! "openvpn-server", "true" if openvpn_server?
61
+ server.tag! "error-message", @error_message if @error_message
62
+ end
63
+ xml.target!
64
+
65
+ end
66
+
67
+ def self.from_xml(xml)
68
+
69
+ server=nil
70
+ dom = REXML::Document.new(xml)
71
+ REXML::XPath.each(dom, "/*") do |sg_xml|
72
+
73
+ server=Server.new(
74
+ :id => XMLUtil.element_text(sg_xml, "id").to_i,
75
+ :name => XMLUtil.element_text(sg_xml, "name"),
76
+ :flavor_id => XMLUtil.element_text(sg_xml, "flavor-id"),
77
+ :image_id => XMLUtil.element_text(sg_xml, "image-id"),
78
+ :admin_password => XMLUtil.element_text(sg_xml, "admin-password"),
79
+ :description => XMLUtil.element_text(sg_xml, "description"),
80
+ :cloud_server_id_number => XMLUtil.element_text(sg_xml, "cloud-server-id-number"),
81
+ :description => XMLUtil.element_text(sg_xml, "description"),
82
+ :external_ip_addr => XMLUtil.element_text(sg_xml, "external-ip-addr"),
83
+ :internal_ip_addr => XMLUtil.element_text(sg_xml, "internal-ip-addr"),
84
+ :server_group_id => XMLUtil.element_text(sg_xml, "server-group-id"),
85
+ :openvpn_server => XMLUtil.element_text(sg_xml, "openvpn_server"),
86
+ :retry_count => XMLUtil.element_text(sg_xml, "retry-count"),
87
+ :error_message => XMLUtil.element_text(sg_xml, "error-message"),
88
+ :status => XMLUtil.element_text(sg_xml, "status")
89
+ )
90
+ end
91
+
92
+ server
93
+
94
+ end
95
+
96
+ def rebuild
97
+
98
+ raise "Error: Rebuilding the OpenVPN server is not supported at this time." if openvpn_server?
99
+
100
+ Connection.post("/servers/#{@id}/rebuild", {})
101
+
102
+ end
103
+
104
+ def delete
105
+ Connection.delete("/servers/#{@id}.xml")
106
+ end
107
+
108
+ def self.create(server)
109
+
110
+ xml=Connection.post("/servers.xml", server.to_xml)
111
+ server=Server.from_xml(xml)
112
+
113
+ end
114
+
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,401 @@
1
+ require 'json'
2
+ require 'builder'
3
+ require 'fileutils'
4
+ require 'rexml/document'
5
+ require 'rexml/xpath'
6
+
7
+ module Kytoon
8
+
9
+ module Providers
10
+
11
+ module CloudServersVPC
12
+
13
+ class ServerGroup
14
+
15
+ @@data_dir=File.join(KYTOON_PROJECT, "tmp", "cloud_servers_vpc")
16
+
17
+ def self.data_dir
18
+ @@data_dir
19
+ end
20
+
21
+ def self.data_dir=(dir)
22
+ @@data_dir=dir
23
+ end
24
+
25
+ CONFIG_FILE = KYTOON_PROJECT + File::SEPARATOR + "config" + File::SEPARATOR + "server_group.json"
26
+
27
+ attr_accessor :id
28
+ attr_accessor :name
29
+ attr_accessor :description
30
+ attr_accessor :domain_name
31
+ attr_accessor :vpn_device
32
+ attr_accessor :vpn_proto
33
+ attr_accessor :vpn_network
34
+ attr_accessor :vpn_subnet
35
+ attr_accessor :owner_name
36
+
37
+ attr_reader :ssh_public_keys
38
+
39
+ def initialize(options={})
40
+ @id=options[:id]
41
+ @name=options[:name]
42
+ @description=options[:description]
43
+ @domain_name=options[:domain_name]
44
+ @vpn_device=options[:vpn_device] or @vpn_device="tun"
45
+ @vpn_proto=options[:vpn_proto] or @vpn_proto="tcp"
46
+ @vpn_network=options[:vpn_network] or @vpn_network="172.19.0.0"
47
+ @vpn_subnet=options[:vpn_subnet] or @vpn_subnet="255.255.128.0"
48
+ @owner_name=options[:owner_name] or @owner_name=ENV['USER']
49
+
50
+ @servers=[]
51
+ @clients=[]
52
+ @ssh_public_keys=[]
53
+ end
54
+
55
+ def server(name)
56
+ @servers.select {|s| s.name == name}[0] if @servers.size > 0
57
+ end
58
+
59
+ def servers
60
+ @servers
61
+ end
62
+
63
+ def client(name)
64
+ @clients.select {|s| s.name == name}[0] if @clients.size > 0
65
+ end
66
+
67
+ def clients
68
+ @clients
69
+ end
70
+
71
+ def vpn_gateway_name
72
+ @servers.select {|s| s.openvpn_server? }[0].name if @servers.size > 0
73
+ end
74
+
75
+ def gateway_ip
76
+ @servers.select {|s| s.openvpn_server? }[0].external_ip_addr if @servers.size > 0
77
+ end
78
+
79
+ def ssh_public_keys
80
+ @ssh_public_keys
81
+ end
82
+
83
+ # generate a Server Group XML from server_group.json
84
+ def self.from_json(json)
85
+
86
+ json_hash=JSON.parse(json)
87
+
88
+ sg=ServerGroup.new(
89
+ :name => json_hash["name"],
90
+ :description => json_hash["description"],
91
+ :domain_name => json_hash["domain_name"],
92
+ :vpn_device => json_hash["vpn_device"],
93
+ :vpn_proto => json_hash["vpn_proto"],
94
+ :vpn_network => json_hash["vpn_network"],
95
+ :vpn_subnet => json_hash["vpn_subnet"]
96
+ )
97
+ json_hash["servers"].each_pair do |server_name, server_config|
98
+ sg.servers << Server.new(
99
+ :name => server_name,
100
+ :description => server_config["description"],
101
+ :flavor_id => server_config["flavor_id"],
102
+ :image_id => server_config["image_id"],
103
+ :openvpn_server => server_config["openvpn_server"]
104
+ )
105
+ end
106
+
107
+ # automatically add a key for the current user
108
+ sg.ssh_public_keys << SshPublicKey.new(
109
+ :description => "#{ENV['USER']}'s public key",
110
+ :public_key => Util.load_public_key
111
+
112
+ )
113
+
114
+ return sg
115
+
116
+ end
117
+
118
+ def to_xml
119
+
120
+ xml = Builder::XmlMarkup.new
121
+ xml.tag! "server-group" do |sg|
122
+ sg.id(@id)
123
+ sg.name(@name)
124
+ sg.description(@description)
125
+ sg.tag! "owner-name", @owner_name
126
+ sg.tag! "domain-name", @domain_name
127
+ sg.tag! "vpn-device", @vpn_device if @vpn_device != "tun"
128
+ sg.tag! "vpn-proto", @vpn_proto if @vpn_proto != "tcp"
129
+ sg.tag! "vpn-network", @vpn_network
130
+ sg.tag! "vpn-subnet", @vpn_subnet
131
+ sg.servers("type" => "array") do |xml_servers|
132
+ self.servers.each do |server|
133
+ xml_servers.server do |xml_server|
134
+ xml_server.id(server.id)
135
+ xml_server.name(server.name)
136
+ xml_server.description(server.description)
137
+ xml_server.tag! "flavor-id", server.flavor_id
138
+ xml_server.tag! "image-id", server.image_id
139
+ if server.admin_password then
140
+ xml_server.tag! "admin-password", server.admin_password
141
+ end
142
+ xml_server.tag! "cloud-server-id-number", server.cloud_server_id_number if server.cloud_server_id_number
143
+ xml_server.tag! "status", server.status if server.status
144
+ xml_server.tag! "external-ip-addr", server.external_ip_addr if server.external_ip_addr
145
+ xml_server.tag! "internal-ip-addr", server.internal_ip_addr if server.internal_ip_addr
146
+ xml_server.tag! "error-message", server.error_message if server.error_message
147
+ xml_server.tag! "retry-count", server.retry_count if server.retry_count
148
+ if server.openvpn_server?
149
+ xml_server.tag! "openvpn-server", "true", { "type" => "boolean"}
150
+ end
151
+ end
152
+ end
153
+ end
154
+ sg.tag! "ssh-public-keys", { "type" => "array"} do |xml_ssh_pub_keys|
155
+ self.ssh_public_keys.each do |ssh_public_key|
156
+ xml_ssh_pub_keys.tag! "ssh-public-key" do |xml_ssh_pub_key|
157
+ xml_ssh_pub_key.description ssh_public_key.description
158
+ xml_ssh_pub_key.tag! "public-key", ssh_public_key.public_key
159
+ end
160
+ end
161
+ end
162
+ sg.tag! "clients", { "type" => "array"} do |xml_clients|
163
+ self.clients.each do |client|
164
+ xml_clients.tag! "client" do |xml_client|
165
+ xml_client.id client.id
166
+ xml_client.name client.name
167
+ xml_client.description client.description
168
+ xml_client.status client.status
169
+ end
170
+ end
171
+ end
172
+
173
+ end
174
+ xml.target!
175
+
176
+ end
177
+
178
+ def self.from_xml(xml)
179
+
180
+ sg=nil
181
+ dom = REXML::Document.new(xml)
182
+ REXML::XPath.each(dom, "/server-group") do |sg_xml|
183
+ sg=ServerGroup.new(
184
+ :name => XMLUtil.element_text(sg_xml, "name"),
185
+ :id => XMLUtil.element_text(sg_xml, "id").to_i,
186
+ :owner_name => XMLUtil.element_text(sg_xml, "owner-name"),
187
+ :domain_name => XMLUtil.element_text(sg_xml, "domain-name"),
188
+ :description => XMLUtil.element_text(sg_xml, "description"),
189
+ :vpn_device => XMLUtil.element_text(sg_xml, "vpn-device"),
190
+ :vpn_proto => XMLUtil.element_text(sg_xml, "vpn-proto"),
191
+ :vpn_network => XMLUtil.element_text(sg_xml, "vpn-network"),
192
+ :vpn_subnet => XMLUtil.element_text(sg_xml, "vpn-subnet")
193
+ )
194
+ REXML::XPath.each(dom, "//server") do |server_xml|
195
+
196
+ server=Server.new(
197
+ :id => XMLUtil.element_text(server_xml, "id").to_i,
198
+ :name => XMLUtil.element_text(server_xml, "name"),
199
+ :cloud_server_id_number => XMLUtil.element_text(server_xml, "cloud-server-id-number"),
200
+ :status => XMLUtil.element_text(server_xml, "status"),
201
+ :external_ip_addr => XMLUtil.element_text(server_xml, "external-ip-addr"),
202
+ :internal_ip_addr => XMLUtil.element_text(server_xml, "internal-ip-addr"),
203
+ :error_message => XMLUtil.element_text(server_xml, "error-message"),
204
+ :image_id => XMLUtil.element_text(server_xml, "image-id"),
205
+ :admin_password => XMLUtil.element_text(server_xml, "admin-password"),
206
+ :flavor_id => XMLUtil.element_text(server_xml, "flavor-id"),
207
+ :retry_count => XMLUtil.element_text(server_xml, "retry-count"),
208
+ :openvpn_server => XMLUtil.element_text(server_xml, "openvpn-server")
209
+ )
210
+ sg.servers << server
211
+ end
212
+ REXML::XPath.each(dom, "//client") do |client_xml|
213
+
214
+ client=Client.new(
215
+ :id => XMLUtil.element_text(client_xml, "id").to_i,
216
+ :name => XMLUtil.element_text(client_xml, "name"),
217
+ :description => XMLUtil.element_text(client_xml, "description"),
218
+ :status => XMLUtil.element_text(client_xml, "status")
219
+ )
220
+ sg.clients << client
221
+ end
222
+
223
+ end
224
+
225
+ sg
226
+
227
+ end
228
+
229
+ def pretty_print
230
+
231
+ puts "Group ID: #{@id}"
232
+ puts "name: #{@name}"
233
+ puts "description: #{@description}"
234
+ puts "domain name: #{@domain_name}"
235
+ puts "VPN gateway IP: #{self.gateway_ip}"
236
+ puts "Servers:"
237
+ servers.each do |server|
238
+ puts "\tname: #{server.name} (id: #{server.id})"
239
+ puts "\tstatus: #{server.status}"
240
+ if server.openvpn_server?
241
+ puts "\tOpenVPN server: #{server.openvpn_server?}"
242
+ end
243
+ if server.error_message then
244
+ puts "\tlast error message: #{server.error_message}"
245
+ end
246
+ puts "\t--"
247
+ end
248
+
249
+ end
250
+
251
+ def server_names
252
+
253
+ names=[]
254
+
255
+ servers.each do |server|
256
+ if block_given? then
257
+ yield server.name
258
+ else
259
+ names << server.name
260
+ end
261
+ end
262
+
263
+ names
264
+
265
+ end
266
+
267
+ def cache_to_disk
268
+ FileUtils.mkdir_p(@@data_dir)
269
+ File.open(File.join(@@data_dir, "#{@id}.xml"), 'w') do |f|
270
+ f.chmod(0600)
271
+ f.write(self.to_xml)
272
+ end
273
+ end
274
+
275
+ def delete
276
+ Connection.delete("/server_groups/#{@id}.xml")
277
+ out_file=File.join(@@data_dir, "#{@id}.xml")
278
+ File.delete(out_file) if File.exists?(out_file)
279
+ end
280
+
281
+ # Poll the server group until it is online.
282
+ # :timeout - max number of seconds to wait before raising an exception.
283
+ # Defaults to 1500
284
+ def poll_until_online(options={})
285
+
286
+ timeout=options[:timeout] or timeout = ENV['TIMEOUT']
287
+ if timeout.nil? or timeout.empty? then
288
+ timeout=1500 # defaults to 25 minutes
289
+ end
290
+
291
+ online = false
292
+ count=0
293
+ until online or (count*20) >= timeout.to_i do
294
+ count+=1
295
+ begin
296
+ sg=ServerGroup.get(:id => @id, :source => "remote")
297
+
298
+ online=true
299
+ sg.servers.each do |server|
300
+ if ["Pending", "Rebuilding"].include?(server.status) then
301
+ online=false
302
+ end
303
+ if server.status == "Failed" then
304
+ raise "Failed to create server group with the following message: #{server.error_message}"
305
+ end
306
+ end
307
+ if not online
308
+ yield sg if block_given?
309
+ sleep 20
310
+ end
311
+ rescue EOFError
312
+ end
313
+ end
314
+ if (count*20) >= timeout.to_i then
315
+ raise "Timeout waiting for server groups to come online."
316
+ end
317
+
318
+ end
319
+
320
+ def self.create(sg)
321
+
322
+ xml=Connection.post("/server_groups.xml", sg.to_xml)
323
+ sg=ServerGroup.from_xml(xml)
324
+
325
+ old_group_xml=nil
326
+ vpn_gateway=nil
327
+ sg.poll_until_online do |server_group|
328
+ if old_group_xml != server_group.to_xml then
329
+ old_group_xml = server_group.to_xml
330
+ vpn_gateway = server_group.gateway_ip if server_group.gateway_ip
331
+ if not vpn_gateway.nil? and not vpn_gateway.empty? then
332
+ SshUtil.remove_known_hosts_ip(vpn_gateway)
333
+ end
334
+ server_group.pretty_print
335
+ end
336
+ end
337
+ sg=ServerGroup.get(:id => sg.id, :source => "remote")
338
+ sg.cache_to_disk
339
+ puts "Server group online."
340
+ sg
341
+
342
+ end
343
+
344
+ # Get a server group. The following options are available:
345
+ #
346
+ # :id - The ID of the server group to get. Defaults to ENV['GROUP_ID']
347
+ # :source - valid options are 'cache' and 'remote'
348
+ def self.get(options={})
349
+
350
+ source = options[:source] or source = "cache"
351
+ id = options[:id]
352
+ if id.nil? then
353
+ group = ServerGroup.most_recent
354
+ raise "No recent server group files exist." if group.nil?
355
+ id = group.id
356
+ end
357
+
358
+ if source == "remote" then
359
+ xml=Connection.get("/server_groups/#{id}.xml")
360
+ ServerGroup.from_xml(xml)
361
+ elsif source == "cache" then
362
+ out_file = File.join(@@data_dir, "#{id}.xml")
363
+ raise "No server group files exist." if not File.exists?(out_file)
364
+ ServerGroup.from_xml(IO.read(out_file))
365
+ else
366
+ raise "Invalid get :source specified."
367
+ end
368
+
369
+ end
370
+
371
+ # :source - valid options are 'remote' and 'cache'
372
+ def self.index(options={})
373
+
374
+ source = options[:source] or source = "cache"
375
+ server_groups=[]
376
+ Dir[File.join(ServerGroup.data_dir, '*.xml')].each do |file|
377
+ server_groups << ServerGroup.from_xml(IO.read(file))
378
+ end
379
+ server_groups
380
+
381
+ end
382
+
383
+ def self.most_recent
384
+ server_groups=[]
385
+ Dir[File.join(@@data_dir, "*.xml")].each do |file|
386
+ server_groups << ServerGroup.from_xml(IO.read(file))
387
+ end
388
+ if server_groups.size > 0 then
389
+ return server_groups.sort { |a,b| b.id <=> a.id }[0]
390
+ else
391
+ nil
392
+ end
393
+ end
394
+
395
+ end
396
+
397
+ end
398
+
399
+ end
400
+
401
+ end