kytoon 1.0.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.
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,15 @@
1
+ module Kytoon
2
+
3
+ module XMLUtil
4
+
5
+ def self.element_text(dom, name)
6
+ if dom.elements[name]
7
+ return dom.elements[name].text
8
+ else
9
+ return nil
10
+ end
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,115 @@
1
+ include Kytoon
2
+
3
+ namespace :group do
4
+ TMP_SG=File.join(KYTOON_PROJECT, 'tmp', 'server_groups')
5
+
6
+ directory TMP_SG
7
+
8
+ task :init => [TMP_SG] do
9
+ ServerGroup.init
10
+ end
11
+
12
+ desc "Create a new group of cloud servers"
13
+ task :create => [ "init" ] do
14
+ sg = ServerGroup.create
15
+ puts "Server group ID #{sg.id} created."
16
+ end
17
+
18
+ desc "List existing cloud server groups."
19
+ task :list => "init" do
20
+
21
+ server_groups=nil
22
+ server_groups=ServerGroup.index(:source => "cache")
23
+ if server_groups.size > 0
24
+ puts "Server groups:"
25
+ server_groups.sort { |a,b| b.id <=> a.id }.each do |sg|
26
+ gw=sg.gateway_ip.nil? ? "" : " (#{sg.gateway_ip})"
27
+ puts "\t :id => #{sg.id}, :name => #{sg.name} #{gw}"
28
+ end
29
+ else
30
+ puts "No server groups."
31
+ end
32
+
33
+ end
34
+
35
+ desc "Print information for a cloud server group"
36
+ task :show => [ "init" ] do
37
+ sg = ServerGroup.get
38
+ sg.pretty_print
39
+ end
40
+
41
+ desc "Delete a cloud server group"
42
+ task :delete => ["init"] do
43
+
44
+ sg = ServerGroup.get
45
+ puts "Deleting cloud server group ID: #{sg.id}."
46
+ sg.delete
47
+ SshUtil.remove_known_hosts_ip(sg.gateway_ip)
48
+
49
+ end
50
+
51
+ desc "Print the VPN gateway IP address"
52
+ task :gateway_ip do
53
+ group = ServerGroup.get
54
+ puts group.gateway_ip
55
+ end
56
+
57
+ end
58
+
59
+ desc "SSH into the most recently created VPN gateway server."
60
+ task :ssh => 'group:init' do
61
+
62
+ sg=ServerGroup.get
63
+ args=ARGV[1, ARGV.length].join(" ")
64
+ if (ARGV[1] and ARGV[1] =~ /^GROUP_.*/) and (ARGV[2] and ARGV[2] =~ /^GROUP_.*/)
65
+ args=ARGV[3, ARGV.length].join(" ")
66
+ elsif ARGV[1] and ARGV[1] =~ /^GROUP_.*/
67
+ args=ARGV[2, ARGV.length].join(" ")
68
+ end
69
+ exec("ssh -o \"StrictHostKeyChecking no\" root@#{sg.gateway_ip} #{args}")
70
+ end
71
+
72
+ desc "Print help and usage information"
73
+ task :usage do
74
+
75
+ puts ""
76
+ puts "Kytoon Toolkit Version: #{Kytoon::Version::VERSION}"
77
+ puts ""
78
+ puts "The following tasks are available:"
79
+
80
+ puts %x{cd #{KYTOON_PROJECT} && rake -T}
81
+ puts "----"
82
+ puts "Example commands:"
83
+ puts ""
84
+ puts "\t- Create a new server group."
85
+ puts ""
86
+ puts "\t\t$ rake group:create"
87
+
88
+ puts ""
89
+ puts "\t- List your currently running server groups."
90
+ puts ""
91
+ puts "\t\t$ rake group:list"
92
+
93
+ puts ""
94
+ puts "\t- List all remote groups using a common Cloud Servers VPC account."
95
+ puts ""
96
+ puts "\t\t$ rake group:list"
97
+
98
+ puts ""
99
+ puts "\t- SSH into the current (most recently created) server group."
100
+ puts ""
101
+ puts "\t\t$ rake ssh"
102
+
103
+ puts ""
104
+ puts "\t- SSH into a server group with an ID of 3."
105
+ puts ""
106
+ puts "\t\t$ rake ssh GROUP_ID=3"
107
+
108
+ puts ""
109
+ puts "\t- Delete the server group with an ID of 3."
110
+ puts ""
111
+ puts "\t\t$ rake group:delete GROUP_ID=3"
112
+
113
+ end
114
+
115
+ task :default => 'usage'
@@ -0,0 +1,111 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'test_helper'
3
+
4
+ module Kytoon
5
+ module Providers
6
+ module CloudServersVPC
7
+
8
+ class ClientTest < Test::Unit::TestCase
9
+
10
+ include Kytoon::Providers::CloudServersVPC
11
+
12
+ def setup
13
+ @tmp_dir=TmpDir.new_tmp_dir
14
+ Client.data_dir=@tmp_dir
15
+ end
16
+
17
+ def teardown
18
+ FileUtils.rm_rf(@tmp_dir)
19
+ end
20
+
21
+ def test_new
22
+ client=Client.new(:name => "test", :description => "zz", :status => "Pending")
23
+ assert_equal "test", client.name
24
+ assert_equal "zz", client.description
25
+ assert_equal 0, client.vpn_network_interfaces.size
26
+ end
27
+
28
+ def test_from_xml
29
+ client=Client.from_xml(CLIENT_XML)
30
+ assert_equal "local", client.name
31
+ assert_equal "Toolkit Client: local", client.description
32
+ assert_equal 5, client.id
33
+ assert_equal 11, client.server_group_id
34
+ vni=client.vpn_network_interfaces[0]
35
+ assert_not_nil vni.client_key
36
+ assert_not_nil vni.client_cert
37
+ assert_not_nil vni.ca_cert
38
+ end
39
+
40
+ def test_client_to_and_from_xml
41
+ client=Client.from_xml(CLIENT_XML)
42
+ xml=client.to_xml
43
+ assert_not_nil xml
44
+ client=Client.from_xml(xml)
45
+ assert_equal "local", client.name
46
+ assert_equal "Toolkit Client: local", client.description
47
+ assert_equal 5, client.id
48
+ assert_equal 11, client.server_group_id
49
+ vni=client.vpn_network_interfaces[0]
50
+ assert_not_nil vni.client_key
51
+ assert_not_nil vni.client_cert
52
+ assert_not_nil vni.ca_cert
53
+ end
54
+
55
+ def test_get
56
+
57
+ tmp_dir=TmpDir.new_tmp_dir
58
+ File.open("#{tmp_dir}/5.xml", 'w') do |f|
59
+ f.write(CLIENT_XML)
60
+ end
61
+ Client.data_dir=tmp_dir
62
+
63
+ Connection.stubs(:get).returns(CLIENT_XML)
64
+
65
+ # should raise exception if no ID is set and doing a remote lookup
66
+ assert_raises(RuntimeError) do
67
+ Client.get
68
+ end
69
+
70
+ client=Client.get(:id => "1234")
71
+ assert_not_nil client
72
+ assert_equal "Toolkit Client: local", client.description
73
+
74
+ client=Client.get(:id => "5", :source => "cache")
75
+ assert_not_nil client
76
+ assert_equal "Toolkit Client: local", client.description
77
+
78
+ #nonexistent group from cache
79
+ ENV['GROUP_ID']="1234"
80
+ assert_raises(RuntimeError) do
81
+ Client.get(:source => "cache")
82
+ end
83
+
84
+ #invalid get source
85
+ assert_raises(RuntimeError) do
86
+ Client.get(:id => "5", :source => "asdf")
87
+ end
88
+
89
+ end
90
+
91
+ def test_delete
92
+
93
+ client=Client.from_xml(CLIENT_XML)
94
+ client.delete
95
+ assert_equal false, File.exists?(File.join(Client.data_dir, "#{client.id}.xml"))
96
+
97
+ end
98
+
99
+ def test_create
100
+
101
+ Connection.stubs(:post).returns(CLIENT_XML)
102
+ client=Client.create(ServerGroup.from_xml(SERVER_GROUP_XML), "local")
103
+ assert_equal "local", client.name
104
+
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'kytoon'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,253 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'test_helper'
3
+
4
+ require 'fileutils'
5
+ require 'tempfile'
6
+
7
+ module Kytoon
8
+ module Providers
9
+ module CloudServersVPC
10
+
11
+ class ServerGroupTest < Test::Unit::TestCase
12
+
13
+ def setup
14
+ @tmp_dir=TmpDir.new_tmp_dir
15
+ ServerGroup.data_dir=@tmp_dir
16
+ end
17
+
18
+ def teardown
19
+ FileUtils.rm_rf(@tmp_dir)
20
+ end
21
+
22
+ TEST_JSON_CONFIG = %{{
23
+ "name": "test",
24
+ "domain_name": "vpc",
25
+ "description": "test description",
26
+ "servers": {
27
+ "login": {
28
+ "image_id": "51",
29
+ "flavor_id": "2",
30
+ "openvpn_server": "true"
31
+ },
32
+ "client1": {
33
+ "image_id": "69",
34
+ "flavor_id": "3"
35
+ }
36
+ }
37
+ }}
38
+
39
+ def test_server_new
40
+ sg=ServerGroup.new(:name => "test", :domain_name => "vpc", :description => "zz")
41
+ assert_equal "test", sg.name
42
+ assert_equal "zz", sg.description
43
+ assert_equal "vpc", sg.domain_name
44
+ assert_equal "172.19.0.0", sg.vpn_network
45
+ assert_equal "255.255.128.0", sg.vpn_subnet
46
+ end
47
+
48
+ def test_gateway_ip
49
+ sg=ServerGroup.from_xml(SERVER_GROUP_XML)
50
+ assert_equal "184.106.205.120", sg.gateway_ip
51
+ assert_equal 1759, sg.id
52
+ assert_equal "test description", sg.description
53
+ assert_equal "dan.prince", sg.owner_name
54
+ assert_equal "172.19.0.0", sg.vpn_network
55
+ assert_equal "255.255.128.0", sg.vpn_subnet
56
+ assert_equal 2, sg.servers.size
57
+ end
58
+
59
+ #def test_vpn_gateway_name
60
+ #sg=ServerGroup.from_xml(SERVER_GROUP_XML)
61
+ #assert_equal "login1", sg.vpn_gateway_name
62
+ #end
63
+
64
+ def test_server_group_from_json_config
65
+ sg=ServerGroup.from_json(TEST_JSON_CONFIG)
66
+ assert_equal "vpc", sg.domain_name
67
+ assert_equal "test", sg.name
68
+ assert_equal "test description", sg.description
69
+ assert_equal 2, sg.servers.size
70
+ assert_equal 1, sg.ssh_public_keys.size
71
+
72
+ # validate the login server
73
+ login_server=sg.server("login")
74
+ assert_equal "51", login_server.image_id
75
+ assert_equal "2", login_server.flavor_id
76
+ assert_equal true, login_server.openvpn_server?
77
+
78
+ # validate the client1 server
79
+ client1_server=sg.server("client1")
80
+ assert_equal "69", client1_server.image_id
81
+ assert_equal "3", client1_server.flavor_id
82
+ assert_equal false, client1_server.openvpn_server?
83
+
84
+ end
85
+
86
+ def test_server_group_from_xml
87
+ sg=ServerGroup.from_xml(SERVER_GROUP_XML)
88
+ assert_equal "mydomain.net", sg.domain_name
89
+ assert_equal "test", sg.name
90
+ assert_equal "test description", sg.description
91
+ assert_equal 2, sg.servers.size
92
+ assert_equal 1759, sg.id
93
+ end
94
+
95
+ def test_server_group_to_xml
96
+ sg=ServerGroup.from_xml(SERVER_GROUP_XML)
97
+ assert_equal "mydomain.net", sg.domain_name
98
+ assert_equal "test", sg.name
99
+ assert_equal "test description", sg.description
100
+ assert_equal 2, sg.servers.size
101
+ assert_equal 1759, sg.id
102
+ xml=sg.to_xml
103
+ end
104
+
105
+ def test_print_server_group
106
+
107
+ sg=ServerGroup.from_xml(SERVER_GROUP_XML)
108
+ tmp = Tempfile.open('kytoon')
109
+ begin
110
+ $stdout = tmp
111
+ sg.pretty_print
112
+ tmp.flush
113
+ output=IO.read(tmp.path)
114
+ $stdout = STDOUT
115
+ assert output =~ /login1/
116
+ assert output =~ /test1/
117
+ assert output =~ /184.106.205.120/
118
+ ensure
119
+ $stdout = STDOUT
120
+ end
121
+
122
+ end
123
+
124
+ def test_server_names
125
+
126
+ sg=ServerGroup.from_xml(SERVER_GROUP_XML)
127
+ names=sg.server_names
128
+
129
+ assert_equal 2, names.size
130
+ assert names.include?("login1")
131
+ assert names.include?("test1")
132
+
133
+ end
134
+
135
+ def test_get
136
+
137
+ tmp_dir=TmpDir.new_tmp_dir
138
+ File.open("#{tmp_dir}/1759.xml", 'w') do |f|
139
+ f.write(SERVER_GROUP_XML)
140
+ end
141
+ ServerGroup.data_dir=tmp_dir
142
+
143
+ Connection.stubs(:get).returns(SERVER_GROUP_XML)
144
+
145
+ sg=ServerGroup.get(:source => "cache")
146
+ assert_not_nil sg
147
+ assert_equal "test", sg.name
148
+
149
+ sg=ServerGroup.get(:id => "1759", :source => "cache")
150
+ assert_not_nil sg
151
+ assert_equal "test", sg.name
152
+
153
+ #nonexistent group from cache
154
+ assert_raises(RuntimeError) do
155
+ ServerGroup.get(:id => "1234", :source => "cache")
156
+ end
157
+
158
+ #invalid get source
159
+ assert_raises(RuntimeError) do
160
+ ServerGroup.get(:id => "1759", :source => "asdf")
161
+ end
162
+
163
+ end
164
+
165
+ def test_index_from_cache
166
+
167
+ tmp_dir=TmpDir.new_tmp_dir
168
+ File.open("#{tmp_dir}/1759.xml", 'w') do |f|
169
+ f.write(SERVER_GROUP_XML)
170
+ end
171
+ ServerGroup.data_dir=tmp_dir
172
+
173
+ server_groups = ServerGroup.index
174
+
175
+ assert_equal 1, server_groups.size
176
+ assert_equal 1759, server_groups[0].id
177
+
178
+ end
179
+
180
+ def test_index_from_remote
181
+
182
+ tmp_dir=TmpDir.new_tmp_dir
183
+ File.open("#{tmp_dir}/1759.xml", 'w') do |f|
184
+ f.write(SERVER_GROUP_XML)
185
+ end
186
+ ServerGroup.data_dir=tmp_dir
187
+
188
+ Connection.stubs(:get).returns(SERVER_GROUP_XML)
189
+ server_groups = ServerGroup.index(:source => "remote")
190
+
191
+ assert_equal 1, server_groups.size
192
+ assert_equal 1759, server_groups[0].id
193
+
194
+ end
195
+
196
+ def test_create
197
+
198
+ sg=ServerGroup.from_json(TEST_JSON_CONFIG)
199
+
200
+ tmp_dir=TmpDir.new_tmp_dir
201
+ File.open("#{tmp_dir}/1759.xml", 'w') do |f|
202
+ f.write(SERVER_GROUP_XML)
203
+ end
204
+ ServerGroup.data_dir=tmp_dir
205
+
206
+ Connection.stubs(:post).returns(SERVER_GROUP_XML)
207
+ Connection.stubs(:get).returns(SERVER_GROUP_XML)
208
+ sg = ServerGroup.create(sg)
209
+ assert_not_nil sg
210
+ assert_equal "mydomain.net", sg.domain_name
211
+ assert_equal "test", sg.name
212
+ assert_equal "test description", sg.description
213
+ assert_equal 2, sg.servers.size
214
+ assert_equal 1759, sg.id
215
+
216
+ end
217
+
218
+ def test_most_recent
219
+
220
+ File.open("#{ServerGroup.data_dir}/5.xml", 'w') do |f|
221
+ f.write(SERVER_GROUP_XML)
222
+ end
223
+
224
+ sg=ServerGroup.most_recent
225
+
226
+ assert_equal "mydomain.net", sg.domain_name
227
+ assert_equal 1759, sg.id
228
+ assert_equal 2, sg.servers.size
229
+
230
+ end
231
+
232
+ def test_cache_to_disk
233
+
234
+ sg=ServerGroup.from_xml(SERVER_GROUP_XML)
235
+ assert sg.cache_to_disk
236
+ assert File.exists?(File.join(ServerGroup.data_dir, "#{sg.id}.xml"))
237
+
238
+ end
239
+
240
+ def test_delete
241
+
242
+ sg=ServerGroup.from_xml(SERVER_GROUP_XML)
243
+ Connection.stubs(:delete).returns("")
244
+ sg.delete
245
+ assert_equal false, File.exists?(File.join(ServerGroup.data_dir, "#{sg.id}.xml"))
246
+
247
+ end
248
+
249
+ end
250
+
251
+ end
252
+ end
253
+ end