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,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rdoc", "~> 3.12"
10
+ gem "bundler", "~> 1.1.4"
11
+ gem "builder", "~> 3.0.0"
12
+ gem "jeweler", "~> 1.8.3"
13
+ gem "uuidtools", "~> 2.1.2"
14
+ gem "mocha", "~> 0.12.1"
15
+ end
@@ -0,0 +1,29 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ builder (3.0.0)
5
+ git (1.2.5)
6
+ jeweler (1.8.4)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rdoc
11
+ json (1.7.3)
12
+ metaclass (0.0.1)
13
+ mocha (0.12.1)
14
+ metaclass (~> 0.0.1)
15
+ rake (0.9.2.2)
16
+ rdoc (3.12)
17
+ json (~> 1.4)
18
+ uuidtools (2.1.2)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ builder (~> 3.0.0)
25
+ bundler (~> 1.1.4)
26
+ jeweler (~> 1.8.3)
27
+ mocha (~> 0.12.1)
28
+ rdoc (~> 3.12)
29
+ uuidtools (~> 2.1.2)
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Red Hat Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,81 @@
1
+ = Kytoon
2
+
3
+ Create & configure ephemeral virtual private clouds
4
+
5
+ == Description
6
+
7
+ A set of Rake tasks that provide a framework to help automate the creation and configuration of “identical” VPC server groups. Kytoon provides the ability to create projects that can be used by team members and continuous integration systems to spin up groups of servers for development and/or testing. Configuration information is stored in JSON and YAML formats which can be easily parsed, edited, and version controlled.
8
+
9
+ Inspired by and based on the Chef VPC Toolkit.
10
+
11
+ == Supports
12
+
13
+ - Cloud Servers VPC (Rackspace and OpenStack)
14
+ - XenServer
15
+
16
+ == Installation
17
+
18
+ 1) Gem install kytoon.
19
+
20
+ 2) Add Kytoon tasks to your projects Rakefile:
21
+
22
+ KYTOON_PROJECT = "#{File.dirname(__FILE__)}" unless defined?(KYTOON_PROJECT)
23
+ require 'rubygems'
24
+ require 'kytoon'
25
+ include Kytoon
26
+
27
+ 3) Create a .kytoon.conf file in your HOME directory that contains the following:
28
+
29
+ # Set one of the following group_types
30
+ group_type: cloud_server_vpc
31
+ #group_type: xenserver
32
+
33
+ # Cloud Servers VPC credentials
34
+ cloud_servers_vpc_url: https://your.vpc.url/
35
+ cloud_servers_vpc_username: <username>
36
+ cloud_servers_vpc_password: <password>
37
+
38
+ == Examples
39
+
40
+ Example commands:
41
+
42
+ - Create a new server group.
43
+
44
+ $ rake group:create
45
+
46
+ - List your currently running server groups.
47
+
48
+ $ rake group:list
49
+
50
+ - SSH into the current (most recently created) server group
51
+
52
+ $ rake ssh
53
+
54
+ - SSH into a server group with an ID of 3
55
+
56
+ $ rake ssh GROUP_ID=3
57
+
58
+ - Delete the server group with an ID of 3
59
+
60
+ $ rake group:delete GROUP_ID=3
61
+
62
+
63
+ == Bash Automation Script
64
+
65
+ The following is an example bash script to spin up a group and run commands via SSH.
66
+
67
+ #!/bin/bash
68
+ trap "rake group:delete" INT TERM EXIT # cleanup the group on exit
69
+
70
+ # create a server group
71
+ rake group:create
72
+
73
+ # Run some scripts on the login server
74
+ rake ssh bash <<-EOF_BASH
75
+ echo 'It works!'
76
+ EOF_BASH
77
+
78
+
79
+ == Copyright
80
+
81
+ Copyright (c) 2012 Red Hat Inc. See LICENSE.txt for further details.
@@ -0,0 +1,35 @@
1
+ require 'rake/testtask'
2
+
3
+ KYTOON_PROJECT = "#{File.dirname(__FILE__)}" unless defined?(KYTOON_PROJECT)
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__),'lib')
6
+ require 'kytoon'
7
+ include Kytoon
8
+
9
+ Dir[File.join(File.dirname(__FILE__), 'rake', '*.rake')].each do |rakefile|
10
+ import(rakefile)
11
+ end
12
+
13
+ Rake::TestTask.new(:test) do |t|
14
+ t.pattern = 'test/*_test.rb'
15
+ t.verbose = true
16
+ end
17
+ Rake::Task['test'].comment = "Unit"
18
+
19
+ begin
20
+ require 'jeweler'
21
+ Jeweler::Tasks.new do |gemspec|
22
+ gemspec.name = "kytoon"
23
+ gemspec.summary = "Create & configure ephemeral virtual private clouds."
24
+ gemspec.description = "A set of Rake tasks that provide a framework to help automate the creation and configuration of VPC server groups."
25
+ gemspec.email = "dprince@redhat.com"
26
+ gemspec.homepage = "http://github.com/dprince/kytoon"
27
+ gemspec.authors = ["Dan Prince"]
28
+ gemspec.add_dependency 'rake'
29
+ gemspec.add_dependency 'builder'
30
+ gemspec.add_dependency 'json'
31
+ gemspec.add_dependency 'uuidtools'
32
+ end
33
+ rescue LoadError
34
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
35
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "Fedora",
3
+ "domain_name": "vpc",
4
+ "description": "Fedora",
5
+ "vpn_device": "tap",
6
+ "vpn_proto": "udp",
7
+ "servers": {
8
+ "login": {
9
+ "image_id": "24344414",
10
+ "flavor_id": "3",
11
+ "openvpn_server": "true"
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "Fedora",
3
+ "netmask": "255.255.255.0",
4
+ "gateway": "192.168.0.1",
5
+ "broadcast": "192.168.0.127",
6
+ "dns_nameserver": "8.8.8.8",
7
+ "network_type": "static",
8
+ "public_ip_bridge": "xenbr0",
9
+ "bridge": "xenbr1",
10
+ "servers": [
11
+ {
12
+ "hostname": "login",
13
+ "image_path": "/images/fedora-agent2.xva",
14
+ "ip_address": "192.168.0.2",
15
+ "mac": "e2:6d:71:67:7e:66"
16
+ },
17
+ {
18
+ "hostname": "nova1",
19
+ "image_path": "/images/fedora-agent2.xva",
20
+ "ip_address": "192.168.0.3",
21
+ "mac": "e2:ad:a1:a7:ae:67"
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,8 @@
1
+ require 'kytoon/util'
2
+ require 'kytoon/ssh_util'
3
+ require 'kytoon/version'
4
+ require 'kytoon/xml_util'
5
+ require 'kytoon/server_group'
6
+ require 'kytoon/vpn/vpn_connection'
7
+ require 'kytoon/vpn/vpn_network_manager'
8
+ require 'kytoon/vpn/vpn_openvpn'
@@ -0,0 +1,6 @@
1
+ require 'kytoon/providers/cloud_servers_vpc/connection'
2
+ require 'kytoon/providers/cloud_servers_vpc/client'
3
+ require 'kytoon/providers/cloud_servers_vpc/server'
4
+ require 'kytoon/providers/cloud_servers_vpc/server_group'
5
+ require 'kytoon/providers/cloud_servers_vpc/ssh_public_key'
6
+ require 'kytoon/providers/cloud_servers_vpc/vpn_network_interface'
@@ -0,0 +1,197 @@
1
+ module Kytoon
2
+
3
+ module Providers
4
+
5
+ module CloudServersVPC
6
+
7
+ class Client
8
+
9
+ @@data_dir=File.join(KYTOON_PROJECT, "tmp", "clients")
10
+
11
+ def self.data_dir
12
+ @@data_dir
13
+ end
14
+
15
+ def self.data_dir=(dir)
16
+ @@data_dir=dir
17
+ end
18
+
19
+ attr_accessor :id
20
+ attr_accessor :name
21
+ attr_accessor :description
22
+ attr_accessor :status
23
+ attr_accessor :server_group_id
24
+ attr_accessor :cache_file
25
+
26
+ def initialize(options={})
27
+ @id=options[:id].to_i
28
+ @name=options[:name]
29
+ @description=options[:description]
30
+ if options[:status]
31
+ @status=options[:status]
32
+ else
33
+ @status = "Pending"
34
+ end
35
+ @status=options[:status] or @status = "Pending"
36
+ @server_group_id=options[:server_group_id]
37
+ if options[:cache_file] then
38
+ @cache_file=options[:cache_file]
39
+ else
40
+ @cache_file=options[:server_group_id]
41
+ end
42
+ @vpn_network_interfaces=[]
43
+ end
44
+
45
+ def vpn_network_interfaces
46
+ @vpn_network_interfaces
47
+ end
48
+
49
+ def cache_to_disk
50
+ FileUtils.mkdir_p(@@data_dir)
51
+ File.open(File.join(@@data_dir, "#{@cache_file}.xml"), 'w') do |f|
52
+ f.chmod(0600)
53
+ f.write(self.to_xml)
54
+ end
55
+ end
56
+
57
+ def delete
58
+ client_xml_file=File.join(@@data_dir, "#{@cache_file}.xml")
59
+ if File.exists?(client_xml_file) then
60
+ File.delete(client_xml_file)
61
+ end
62
+ end
63
+
64
+ def self.from_xml(xml)
65
+ client=nil
66
+ dom = REXML::Document.new(xml)
67
+ REXML::XPath.each(dom, "/client") do |cxml|
68
+
69
+ client=Client.new(
70
+ :id => XMLUtil.element_text(cxml,"id").to_i,
71
+ :name => XMLUtil.element_text(cxml, "name"),
72
+ :description => XMLUtil.element_text(cxml,"description"),
73
+ :status => XMLUtil.element_text(cxml,"status"),
74
+ :server_group_id => XMLUtil.element_text(cxml, "server-group-id").to_i
75
+ )
76
+ REXML::XPath.each(dom, "//vpn-network-interface") do |vni|
77
+ vni = VpnNetworkInterface.new(
78
+ :id => XMLUtil.element_text(vni, "id"),
79
+ :vpn_ip_addr => XMLUtil.element_text(vni, "vpn-ip-addr"),
80
+ :ptp_ip_addr => XMLUtil.element_text(vni, "ptp-ip-addr"),
81
+ :client_key => XMLUtil.element_text(vni, "client-key"),
82
+ :client_cert => XMLUtil.element_text(vni, "client-cert"),
83
+ :ca_cert => XMLUtil.element_text(vni, "ca-cert")
84
+ )
85
+ client.vpn_network_interfaces << vni
86
+ end
87
+ end
88
+ client
89
+ end
90
+
91
+ def to_xml
92
+
93
+ xml = Builder::XmlMarkup.new
94
+ xml.tag! "client" do |sg|
95
+ sg.id(@id)
96
+ sg.name(@name)
97
+ sg.description(@description)
98
+ sg.status(@status)
99
+ sg.tag! "server-group-id", @server_group_id
100
+ sg.tag! "vpn-network-interfaces", {"type" => "array"} do |interfaces|
101
+ @vpn_network_interfaces.each do |vni|
102
+ interfaces.tag! "vpn-network-interface" do |xml_vni|
103
+ xml_vni.id(vni.id)
104
+ xml_vni.tag! "vpn-ip-addr", vni.vpn_ip_addr
105
+ xml_vni.tag! "ptp-ip-addr", vni.ptp_ip_addr
106
+ xml_vni.tag! "client-key", vni.client_key
107
+ xml_vni.tag! "client-cert", vni.client_cert
108
+ xml_vni.tag! "ca-cert", vni.ca_cert
109
+ end
110
+ end
111
+ end
112
+
113
+ end
114
+ xml.target!
115
+
116
+ end
117
+
118
+
119
+ # Poll the server group until it is online.
120
+ # :timeout - max number of seconds to wait before raising an exception.
121
+ # Defaults to 1500
122
+ def poll_until_online(options={})
123
+
124
+ timeout=options[:timeout] or timeout = ENV['VPN_CLIENT_TIMEOUT']
125
+ if timeout.nil? or timeout.empty? then
126
+ timeout=300 # defaults to 5 minutes
127
+ end
128
+
129
+ online = false
130
+ count=0
131
+ until online or (count*5) >= timeout.to_i do
132
+ count+=1
133
+ begin
134
+ client=Client.get(:id => @id, :source => "remote")
135
+
136
+ if client.status == "Online" then
137
+ online = true
138
+ else
139
+ yield client if block_given?
140
+ sleep 5
141
+ end
142
+ rescue EOFError
143
+ end
144
+ end
145
+ if (count*20) >= timeout.to_i then
146
+ raise "Timeout waiting for client to come online."
147
+ end
148
+
149
+ end
150
+
151
+ def self.create(server_group, client_name, cache_to_disk=true)
152
+
153
+ xml = Builder::XmlMarkup.new
154
+ xml.client do |client|
155
+ client.name(client_name)
156
+ client.description("Toolkit Client: #{client_name}")
157
+ client.tag! "server-group-id", server_group.id
158
+ end
159
+
160
+ xml=Connection.post("/clients.xml", xml.target!)
161
+ client=Client.from_xml(xml)
162
+ client.cache_to_disk if cache_to_disk
163
+ client
164
+
165
+ end
166
+
167
+ # Get a client. The following options are available:
168
+ #
169
+ # :id - The ID of the client to get.
170
+ # :source - valid options are 'remote' and 'cache'
171
+ #
172
+ def self.get(options = {})
173
+
174
+ source = options[:source] or source = "remote"
175
+
176
+ if source == "remote" then
177
+ id=options[:id] or raise "Please specify a Client ID."
178
+ xml=Connection.get("/clients/#{id}.xml")
179
+ Client.from_xml(xml)
180
+ elsif source == "cache" then
181
+ id=options[:id] or id = ENV['GROUP_ID']
182
+ client_xml_file=File.join(@@data_dir, "#{id}.xml")
183
+ raise "No client files exist." if not File.exists?(client_xml_file)
184
+ Client.from_xml(IO.read(client_xml_file))
185
+ else
186
+ raise "Invalid get :source specified."
187
+ end
188
+
189
+ end
190
+
191
+ end
192
+
193
+ end
194
+
195
+ end
196
+
197
+ end