kytoon 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +29 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +81 -0
- data/Rakefile +35 -0
- data/VERSION +1 -0
- data/config/server_group_vpc.json +14 -0
- data/config/server_group_xen.json +24 -0
- data/lib/kytoon.rb +8 -0
- data/lib/kytoon/providers/cloud_servers_vpc.rb +6 -0
- data/lib/kytoon/providers/cloud_servers_vpc/client.rb +197 -0
- data/lib/kytoon/providers/cloud_servers_vpc/connection.rb +148 -0
- data/lib/kytoon/providers/cloud_servers_vpc/server.rb +121 -0
- data/lib/kytoon/providers/cloud_servers_vpc/server_group.rb +401 -0
- data/lib/kytoon/providers/cloud_servers_vpc/ssh_public_key.rb +29 -0
- data/lib/kytoon/providers/cloud_servers_vpc/vpn_network_interface.rb +33 -0
- data/lib/kytoon/providers/xenserver.rb +1 -0
- data/lib/kytoon/providers/xenserver/server_group.rb +371 -0
- data/lib/kytoon/server_group.rb +46 -0
- data/lib/kytoon/ssh_util.rb +23 -0
- data/lib/kytoon/util.rb +118 -0
- data/lib/kytoon/version.rb +8 -0
- data/lib/kytoon/vpn/vpn_connection.rb +46 -0
- data/lib/kytoon/vpn/vpn_network_manager.rb +237 -0
- data/lib/kytoon/vpn/vpn_openvpn.rb +112 -0
- data/lib/kytoon/xml_util.rb +15 -0
- data/rake/kytoon.rake +115 -0
- data/test/client_test.rb +111 -0
- data/test/helper.rb +18 -0
- data/test/server_group_test.rb +253 -0
- data/test/server_test.rb +69 -0
- data/test/ssh_util_test.rb +22 -0
- data/test/test_helper.rb +194 -0
- data/test/test_kytoon.rb +7 -0
- data/test/util_test.rb +23 -0
- data/test/vpn_network_manager_test.rb +40 -0
- 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
|