kytoon 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/README.md +214 -0
- data/Rakefile +13 -11
- data/VERSION +1 -1
- data/bin/kytoon +7 -0
- data/config/server_group_openstack.json +12 -0
- data/lib/kytoon.rb +2 -0
- data/lib/kytoon/exception.rb +8 -0
- data/lib/kytoon/providers/libvirt/server_group.rb +8 -8
- data/lib/kytoon/providers/openstack.rb +5 -0
- data/lib/kytoon/providers/openstack/server_group.rb +317 -0
- data/lib/kytoon/providers/xenserver/server_group.rb +4 -4
- data/lib/kytoon/server_group.rb +33 -17
- data/lib/kytoon/thor_tasks.rb +92 -0
- data/lib/kytoon/util.rb +15 -10
- data/rake/kytoon.rake +37 -53
- metadata +61 -7
- data/README.rdoc +0 -102
- data/test/vpn_network_manager_test.rb +0 -41
data/CHANGELOG
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
* Wed Sep 19 2012 Dan Prince <dprince@redhat.com> - 1.2.0
|
2
|
+
- Add provider for OpenStack.
|
3
|
+
- Add bin/kytoon with commands corresponding to rake tasks.
|
4
|
+
- Move rake tasks under 'kytoon' namespace (previously used 'group').
|
5
|
+
- Updates to raise KytoonException instead of just raise.
|
6
|
+
- Rename gateway_ip task to just ip.
|
7
|
+
|
1
8
|
* Thu Aug 23 2012 Dan Prince <dprince@redhat.com> - 1.1.1
|
2
9
|
- Add 'libvirt_use_sudo' option to config file.
|
3
10
|
- Move config parameter checks into specific providers.
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
# Kytoon
|
2
|
+
|
3
|
+
Create small virtual server groups
|
4
|
+
|
5
|
+
## Description
|
6
|
+
|
7
|
+
Provides both a CLI (bin/kytoon) and set of rake tasks to help automate the creation and configuration of virtual server groups. Kytoon provides the ability to create projects that can be used by team members and continuous integration systems to create similar (if not identical) 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
|
+
## Features
|
12
|
+
|
13
|
+
* Multiple providers (see below)
|
14
|
+
* A CLI with matching set of rake tasks.
|
15
|
+
* Create server groups with multiple nodes.
|
16
|
+
* Automatically injects your ssh into each server group.
|
17
|
+
* Configures hostnames (host files) on nodes within each group. Once you ssh
|
18
|
+
into the group use preconfigured hostnames to access all the nodes.
|
19
|
+
|
20
|
+
## Provider support
|
21
|
+
|
22
|
+
- Libvirt: manage instances on local machine w/ libvirt, virt-clone, and libguestfs
|
23
|
+
- XenServer: manage instances on a remote XenServer box (via ssh)
|
24
|
+
- OpenStack: create instances in the cloud.
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
Gem install kytoon.
|
29
|
+
|
30
|
+
gem install kytoon
|
31
|
+
|
32
|
+
Create a .kytoon.conf file in your $HOME directory.
|
33
|
+
|
34
|
+
# The default group type.
|
35
|
+
# Set to one of: openstack, libvirt, xenserver
|
36
|
+
group_type: openstack
|
37
|
+
|
38
|
+
# Openstack Settings
|
39
|
+
openstack_url: <auth_url>
|
40
|
+
openstack_username: <username>
|
41
|
+
openstack_password: <password>
|
42
|
+
openstack_network_name: public # Optional: defaults to public
|
43
|
+
openstack_keypair_name: < keyname > # Optional: file injection via personalities is the default
|
44
|
+
openstack_ip_type: 4 # IP type (4 or 6): defaults to 4
|
45
|
+
openstack_build_timeout: 480 # Server build timeout. Defaults to: 480
|
46
|
+
openstack_ping_timeout: 60 # Server build timeout. Defaults to: 60
|
47
|
+
|
48
|
+
# Libvirt settings
|
49
|
+
# Whether commands to create local group should use sudo
|
50
|
+
libvirt_use_sudo: False
|
51
|
+
|
52
|
+
## Defining server groups
|
53
|
+
|
54
|
+
Server group config files are used to define how Kytoon configures
|
55
|
+
each server group. These files typically live inside of project are
|
56
|
+
provider specific. The config files control things like memory, hostname,
|
57
|
+
flavor, etc. Each group should define identify one instance as the 'gateway' host which marks it as the primary access point for SSH access into the group.
|
58
|
+
|
59
|
+
By default Kytoon looks for config/server_group.json in the current directory.
|
60
|
+
You can override this with Rake using GROUP_CONFIG or bin/kytoon using --group-config.
|
61
|
+
|
62
|
+
Below are example server_group.json config files for each provider.
|
63
|
+
|
64
|
+
For Openstack:
|
65
|
+
|
66
|
+
```bash
|
67
|
+
|
68
|
+
cat > config/server_group.json <<-"EOF_CAT"
|
69
|
+
{
|
70
|
+
"name": "Fedora",
|
71
|
+
"servers": [
|
72
|
+
{
|
73
|
+
"hostname": "nova1",
|
74
|
+
"image_ref": "1234",
|
75
|
+
"flavor_ref": "5678",
|
76
|
+
"gateway": "true"
|
77
|
+
},
|
78
|
+
{
|
79
|
+
"hostname": "compute1",
|
80
|
+
"image_ref": "1234",
|
81
|
+
"flavor_ref": "5678"
|
82
|
+
}
|
83
|
+
]
|
84
|
+
}
|
85
|
+
EOF_CAT
|
86
|
+
```
|
87
|
+
|
88
|
+
For Libvirt (uses libvirt DHCP server for instance IP configuration):
|
89
|
+
|
90
|
+
```bash
|
91
|
+
|
92
|
+
cat > config/server_group.json <<-"EOF_CAT"
|
93
|
+
{
|
94
|
+
"name": "Fedora",
|
95
|
+
"servers": [
|
96
|
+
{
|
97
|
+
"hostname": "nova1",
|
98
|
+
"memory": "1",
|
99
|
+
"gateway": "true",
|
100
|
+
"original_xml": "/home/dprince/f17.xml",
|
101
|
+
"create_cow": "true"
|
102
|
+
}
|
103
|
+
]
|
104
|
+
}
|
105
|
+
EOF_CAT
|
106
|
+
```
|
107
|
+
|
108
|
+
For XenServer (uses Openstack Guest agent for instance configuration):
|
109
|
+
```bash
|
110
|
+
|
111
|
+
cat > config/server_group.json <<-"EOF_CAT"
|
112
|
+
{
|
113
|
+
"name": "Fedora",
|
114
|
+
"netmask": "255.255.255.0",
|
115
|
+
"gateway": "192.168.0.1",
|
116
|
+
"broadcast": "192.168.0.127",
|
117
|
+
"dns_nameserver": "8.8.8.8",
|
118
|
+
"network_type": "static",
|
119
|
+
"public_ip_bridge": "xenbr0",
|
120
|
+
"bridge": "xenbr1",
|
121
|
+
"servers": [
|
122
|
+
{
|
123
|
+
"hostname": "login",
|
124
|
+
"image_path": "/images/fedora-agent2.xva",
|
125
|
+
"ip_address": "192.168.0.2",
|
126
|
+
"mac": "e2:6d:71:67:7e:66"
|
127
|
+
},
|
128
|
+
{
|
129
|
+
"hostname": "nova1",
|
130
|
+
"image_path": "/images/fedora-agent2.xva",
|
131
|
+
"ip_address": "192.168.0.3",
|
132
|
+
"mac": "e2:ad:a1:a7:ae:67"
|
133
|
+
}
|
134
|
+
]
|
135
|
+
}
|
136
|
+
EOF_CAT
|
137
|
+
```
|
138
|
+
|
139
|
+
## Command line
|
140
|
+
|
141
|
+
The following options are supported on the command line:
|
142
|
+
|
143
|
+
kytoon create # Create a new server group.
|
144
|
+
kytoon delete # Delete a server group.
|
145
|
+
kytoon help [TASK] # Describe available tasks or one specific task
|
146
|
+
kytoon ip # Print the IP address of the gateway server
|
147
|
+
kytoon list # List existing server groups.
|
148
|
+
kytoon show # Print information for a server group.
|
149
|
+
kytoon ssh # SSH into a group.
|
150
|
+
|
151
|
+
## Rakefile configuration
|
152
|
+
|
153
|
+
To include Kytoon rake tasks in your own project add the following to your
|
154
|
+
Rakefile:
|
155
|
+
|
156
|
+
KYTOON_PROJECT = "#{File.dirname(__FILE__)}" unless defined?(KYTOON_PROJECT)
|
157
|
+
require 'rubygems'
|
158
|
+
require 'kytoon'
|
159
|
+
include Kytoon
|
160
|
+
Dir[File.join("#{Kytoon::Version::KYTOON_ROOT}/rake", '*.rake')].each do |rakefile|
|
161
|
+
import(rakefile)
|
162
|
+
end
|
163
|
+
|
164
|
+
## Rake Tasks
|
165
|
+
|
166
|
+
Example commands:
|
167
|
+
|
168
|
+
* Create a new server group.
|
169
|
+
|
170
|
+
$ rake kytoon:create
|
171
|
+
|
172
|
+
* List your currently running server groups.
|
173
|
+
|
174
|
+
$ rake kytoon:list
|
175
|
+
|
176
|
+
* SSH into the current (most recently created) server group
|
177
|
+
|
178
|
+
$ rake kytoon:ssh
|
179
|
+
|
180
|
+
* SSH into a server group with an ID of 3
|
181
|
+
|
182
|
+
$ rake kytoon:ssh GROUP_ID=3
|
183
|
+
|
184
|
+
* Delete the server group with an ID of 3
|
185
|
+
|
186
|
+
$ rake kytoon:delete GROUP_ID=3
|
187
|
+
|
188
|
+
|
189
|
+
## Bash Automation Script
|
190
|
+
|
191
|
+
The following is an example bash script to spin up a group and run commands via SSH.
|
192
|
+
|
193
|
+
```bash
|
194
|
+
#!/bin/bash
|
195
|
+
# override the group type specified in .kytoon.conf
|
196
|
+
export GROUP_TYPE=libvirt
|
197
|
+
|
198
|
+
trap "rake kytoon:delete" INT TERM EXIT # cleanup the group on exit
|
199
|
+
|
200
|
+
# create a server group (uses config/server_group.json)
|
201
|
+
rake kytoon:create
|
202
|
+
|
203
|
+
# create a server group with alternate json file
|
204
|
+
rake kytoon:create GROUP_CONFIG=config/my_group.json
|
205
|
+
|
206
|
+
# Run some scripts on the login server
|
207
|
+
rake kytoon:ssh bash <<-EOF_BASH
|
208
|
+
echo 'It works!'
|
209
|
+
EOF_BASH
|
210
|
+
```
|
211
|
+
|
212
|
+
## Copyright
|
213
|
+
|
214
|
+
Copyright (c) 2012 Red Hat Inc. See LICENSE.txt for further details.
|
data/Rakefile
CHANGED
@@ -18,17 +18,19 @@ Rake::Task['test'].comment = "Unit"
|
|
18
18
|
|
19
19
|
begin
|
20
20
|
require 'jeweler'
|
21
|
-
Jeweler::Tasks.new do |
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
21
|
+
Jeweler::Tasks.new do |gem|
|
22
|
+
gem.name = "kytoon"
|
23
|
+
gem.summary = "Create & configure ephemeral virtual private clouds."
|
24
|
+
gem.description = "A set of Rake tasks that provide a framework to help automate the creation and configuration of VPC server groups."
|
25
|
+
gem.email = "dprince@redhat.com"
|
26
|
+
gem.homepage = "http://github.com/dprince/kytoon"
|
27
|
+
gem.authors = ["Dan Prince"]
|
28
|
+
gem.add_dependency 'rake'
|
29
|
+
gem.add_dependency 'builder'
|
30
|
+
gem.add_dependency 'json'
|
31
|
+
gem.add_dependency 'openstack-compute'
|
32
|
+
gem.add_dependency 'thor'
|
33
|
+
gem.add_dependency 'uuidtools'
|
32
34
|
end
|
33
35
|
rescue LoadError
|
34
36
|
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
data/bin/kytoon
ADDED
data/lib/kytoon.rb
CHANGED
@@ -170,7 +170,7 @@ fi
|
|
170
170
|
}, server['ip_address']) do |ok, out|
|
171
171
|
if not ok
|
172
172
|
puts out
|
173
|
-
raise "Failed to copy host file to instance #{server['hostname']}."
|
173
|
+
raise KytoonException, "Failed to copy host file to instance #{server['hostname']}."
|
174
174
|
end
|
175
175
|
end
|
176
176
|
end
|
@@ -182,12 +182,12 @@ fi
|
|
182
182
|
id = options[:id]
|
183
183
|
if id.nil? then
|
184
184
|
group=ServerGroup.most_recent
|
185
|
-
raise "No server group files exist." if group.nil?
|
185
|
+
raise NoServerGroupExists, "No server group files exist." if group.nil?
|
186
186
|
id=group.id
|
187
187
|
end
|
188
188
|
|
189
189
|
out_file=File.join(@@data_dir, "#{id}.json")
|
190
|
-
raise "No server group files exist." if not File.exists?(out_file)
|
190
|
+
raise NoServerGroupExists, "No server group files exist." if not File.exists?(out_file)
|
191
191
|
ServerGroup.from_json(IO.read(out_file))
|
192
192
|
end
|
193
193
|
|
@@ -223,7 +223,7 @@ fi
|
|
223
223
|
REXML::XPath.each(dom, "//disk[1]/source") do |source_xml|
|
224
224
|
return source_xml.attributes['file']
|
225
225
|
end
|
226
|
-
raise "Unable to find disk path for instance."
|
226
|
+
raise KytoonException, "Unable to find disk path for instance."
|
227
227
|
end
|
228
228
|
|
229
229
|
def self.create_instance(group_id, inst_name, memory_gigs, original, original_xml, disk_path, create_cow, ssh_public_key, sudo)
|
@@ -288,7 +288,7 @@ fi
|
|
288
288
|
retval=$?
|
289
289
|
if not retval.success?
|
290
290
|
puts out
|
291
|
-
raise "Failed to create instance #{inst_name}."
|
291
|
+
raise KytoonException, "Failed to create instance #{inst_name}."
|
292
292
|
end
|
293
293
|
|
294
294
|
# lookup server IP here...
|
@@ -298,7 +298,7 @@ fi
|
|
298
298
|
REXML::XPath.each(dom, "//interface/mac") do |interface_xml|
|
299
299
|
mac_addr = interface_xml.attributes['address']
|
300
300
|
end
|
301
|
-
raise "Failed to lookup mac address for #{inst_name}" if mac_addr.nil?
|
301
|
+
raise KytoonException, "Failed to lookup mac address for #{inst_name}" if mac_addr.nil?
|
302
302
|
|
303
303
|
instance_ip = %x{grep -i #{mac_addr} /var/lib/libvirt/dnsmasq/default.leases | cut -d " " -f 3}.chomp
|
304
304
|
count = 0
|
@@ -307,7 +307,7 @@ fi
|
|
307
307
|
sleep 1
|
308
308
|
count += 1
|
309
309
|
if count >= 60 then
|
310
|
-
raise "Failed to lookup ip address for #{inst_name}"
|
310
|
+
raise KytoonException, "Failed to lookup ip address for #{inst_name}"
|
311
311
|
end
|
312
312
|
end
|
313
313
|
return instance_ip
|
@@ -335,7 +335,7 @@ fi
|
|
335
335
|
retval=$?
|
336
336
|
if not retval.success?
|
337
337
|
puts out
|
338
|
-
raise "Failed to cleanup instances."
|
338
|
+
raise KytoonException, "Failed to cleanup instances."
|
339
339
|
end
|
340
340
|
end
|
341
341
|
|
@@ -0,0 +1,317 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'kytoon/util'
|
3
|
+
require 'openstack/compute'
|
4
|
+
|
5
|
+
module Kytoon
|
6
|
+
|
7
|
+
module Providers
|
8
|
+
|
9
|
+
module Openstack
|
10
|
+
|
11
|
+
# Openstack server group provider.
|
12
|
+
class ServerGroup
|
13
|
+
|
14
|
+
@@connection=nil
|
15
|
+
@@data_dir=File.join(KYTOON_PROJECT, "tmp", "openstack")
|
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
|
+
|
30
|
+
def initialize(options={})
|
31
|
+
@id = options[:id] || Time.now.to_i
|
32
|
+
@name = options[:name]
|
33
|
+
@servers=[]
|
34
|
+
end
|
35
|
+
|
36
|
+
def server(name)
|
37
|
+
@servers.select {|s| s['hostname'] == name}[0] if @servers.size > 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def servers
|
41
|
+
@servers
|
42
|
+
end
|
43
|
+
|
44
|
+
def gateway_ip
|
45
|
+
@servers.select {|s| s['gateway'] == 'true' }[0]['ip_address'] if @servers.size > 0
|
46
|
+
end
|
47
|
+
|
48
|
+
# generate a Server Group XML from server_group.json
|
49
|
+
def self.from_json(json)
|
50
|
+
|
51
|
+
json_hash=JSON.parse(json)
|
52
|
+
|
53
|
+
sg=ServerGroup.new(
|
54
|
+
:id => json_hash["id"],
|
55
|
+
:name => json_hash["name"]
|
56
|
+
)
|
57
|
+
json_hash["servers"].each do |server_hash|
|
58
|
+
|
59
|
+
sg.servers << {
|
60
|
+
'id' => server_hash['id'],
|
61
|
+
'hostname' => server_hash['hostname'],
|
62
|
+
'image_ref' => server_hash['image_ref'],
|
63
|
+
'flavor_ref' => server_hash['flavor_ref'],
|
64
|
+
'keypair_name' => server_hash['keypair_name'],
|
65
|
+
'gateway' => server_hash['gateway'] || "false",
|
66
|
+
'ip_address' => server_hash['ip_address']
|
67
|
+
}
|
68
|
+
end
|
69
|
+
return sg
|
70
|
+
end
|
71
|
+
|
72
|
+
def pretty_print
|
73
|
+
|
74
|
+
puts "Group ID: #{@id}"
|
75
|
+
puts "name: #{@name}"
|
76
|
+
puts "gateway IP: #{self.gateway_ip}"
|
77
|
+
puts "Servers:"
|
78
|
+
servers.each do |server|
|
79
|
+
puts "\tname: #{server['hostname']}"
|
80
|
+
puts "\t--"
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
def server_names
|
86
|
+
|
87
|
+
names=[]
|
88
|
+
|
89
|
+
servers.each do |server|
|
90
|
+
if block_given? then
|
91
|
+
yield server['hostname']
|
92
|
+
else
|
93
|
+
names << server['hostname']
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
names
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def cache_to_disk
|
102
|
+
|
103
|
+
sg_hash = {
|
104
|
+
'id' => @id,
|
105
|
+
'name' => @name,
|
106
|
+
'servers' => []
|
107
|
+
}
|
108
|
+
@servers.each do |server|
|
109
|
+
sg_hash['servers'] << {'id' => server['id'], 'hostname' => server['hostname'], 'image_ref' => server['image_ref'], 'gateway' => server['gateway'], 'flavor_ref' => server['flavor_ref'], 'ip_address' => server['ip_address']}
|
110
|
+
end
|
111
|
+
|
112
|
+
FileUtils.mkdir_p(@@data_dir)
|
113
|
+
File.open(File.join(@@data_dir, "#{@id}.json"), 'w') do |f|
|
114
|
+
f.chmod(0600)
|
115
|
+
f.write(sg_hash.to_json)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def delete
|
120
|
+
servers.each do |server|
|
121
|
+
ServerGroup.destroy_instance(server['id'])
|
122
|
+
end
|
123
|
+
out_file=File.join(@@data_dir, "#{@id}.json")
|
124
|
+
File.delete(out_file) if File.exists?(out_file)
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def self.create(sg)
|
129
|
+
|
130
|
+
hosts_file_data = "127.0.0.1\tlocalhost localhost.localdomain\n"
|
131
|
+
|
132
|
+
build_timeout = (Util.load_configs['openstack_build_timeout'] || 60).to_i
|
133
|
+
|
134
|
+
sg.servers.each do |server|
|
135
|
+
server_id = create_instance(sg.id, server['hostname'], server['image_ref'], server['flavor_ref'], server['keypair_name']).id
|
136
|
+
|
137
|
+
server['id'] = server_id
|
138
|
+
sg.cache_to_disk
|
139
|
+
end
|
140
|
+
|
141
|
+
begin
|
142
|
+
Timeout::timeout(build_timeout) do
|
143
|
+
ips = get_server_ips
|
144
|
+
sg.servers.each do |server|
|
145
|
+
server_ip = ips[server['id']]
|
146
|
+
server['ip_address'] = server_ip
|
147
|
+
sg.cache_to_disk
|
148
|
+
hosts_file_data += "#{server_ip}\t#{server['hostname']}\n"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
rescue Timeout::Error => te
|
152
|
+
raise KytoonException, "Timeout building server group."
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
puts "Copying hosts files..."
|
157
|
+
#now that we have IP info copy hosts files into the servers
|
158
|
+
sg.servers.each do |server|
|
159
|
+
ping_test(server['ip_address'])
|
160
|
+
Kytoon::Util.remote_exec(%{
|
161
|
+
cat > /etc/hosts <<-EOF_CAT
|
162
|
+
#{hosts_file_data}
|
163
|
+
EOF_CAT
|
164
|
+
hostname "#{server['hostname']}"
|
165
|
+
if [ -f /etc/sysconfig/network ]; then
|
166
|
+
sed -e "s|^HOSTNAME.*|HOSTNAME=#{server['hostname']}|" -i /etc/sysconfig/network
|
167
|
+
fi
|
168
|
+
}, server['ip_address']) do |ok, out|
|
169
|
+
if not ok
|
170
|
+
puts out
|
171
|
+
raise KytoonException, "Failed to copy host file to instance #{server['hostname']}."
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
sg
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.get(options={})
|
181
|
+
id = options[:id]
|
182
|
+
if id.nil? then
|
183
|
+
group=ServerGroup.most_recent
|
184
|
+
raise NoServerGroupExists, "No server group files exist." if group.nil?
|
185
|
+
id=group.id
|
186
|
+
end
|
187
|
+
|
188
|
+
out_file=File.join(@@data_dir, "#{id}.json")
|
189
|
+
raise NoServerGroupExists, "No server group files exist." if not File.exists?(out_file)
|
190
|
+
ServerGroup.from_json(IO.read(out_file))
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.index(options={})
|
194
|
+
|
195
|
+
server_groups=[]
|
196
|
+
Dir[File.join(ServerGroup.data_dir, '*.json')].each do |file|
|
197
|
+
server_groups << ServerGroup.from_json(IO.read(file))
|
198
|
+
end
|
199
|
+
server_groups
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
def self.most_recent
|
204
|
+
server_groups=[]
|
205
|
+
Dir[File.join(@@data_dir, "*.json")].each do |file|
|
206
|
+
server_groups << ServerGroup.from_json(IO.read(file))
|
207
|
+
end
|
208
|
+
if server_groups.size > 0 then
|
209
|
+
server_groups.sort { |a,b| b.id <=> a.id }[0]
|
210
|
+
else
|
211
|
+
nil
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.init_connection
|
216
|
+
configs = Util.load_configs
|
217
|
+
if @@connection.nil? then
|
218
|
+
@@connection = OpenStack::Compute::Connection.new(
|
219
|
+
:username => configs['openstack_username'].to_s,
|
220
|
+
:api_key => configs['openstack_password'].to_s,
|
221
|
+
:auth_url => configs['openstack_url'],
|
222
|
+
:retry_auth => false)
|
223
|
+
else
|
224
|
+
@@connection
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.create_instance(group_id, hostname, image_ref, flavor_ref, keypair_name)
|
229
|
+
|
230
|
+
ssh_public_key = Util.public_key_path
|
231
|
+
configs = Util.load_configs
|
232
|
+
|
233
|
+
conn = self.init_connection
|
234
|
+
|
235
|
+
options = {
|
236
|
+
:name => "#{group_id}_#{hostname}",
|
237
|
+
:imageRef => image_ref,
|
238
|
+
:flavorRef => flavor_ref,
|
239
|
+
:personality => {ssh_public_key => "/root/.ssh/authorized_keys"},
|
240
|
+
:is_debug => true}
|
241
|
+
|
242
|
+
keypair_name = configs['openstack_keypair_name'] if keypair_name.nil?
|
243
|
+
if not keypair_name.nil? and not keypair_name.empty? then
|
244
|
+
options.store(:key_name, keypair_name)
|
245
|
+
end
|
246
|
+
|
247
|
+
conn.create_server(options)
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.default_ip_type()
|
252
|
+
ip_type = Util.load_configs['openstack_ip_type'] || 4
|
253
|
+
ip_type.to_i
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.get_server_ips()
|
257
|
+
|
258
|
+
ips = {}
|
259
|
+
configs = Util.load_configs
|
260
|
+
|
261
|
+
network_name = configs['openstack_network_name'] || 'public'
|
262
|
+
|
263
|
+
conn = self.init_connection
|
264
|
+
all_active = false
|
265
|
+
until all_active do
|
266
|
+
all_active = true
|
267
|
+
conn.servers.each do |server|
|
268
|
+
server = conn.server(server[:id])
|
269
|
+
if server.status == 'ACTIVE' and ips[server.id].nil? then
|
270
|
+
addresses = server.addresses[network_name.to_sym].select {|a| a.version == self.default_ip_type}
|
271
|
+
ips[server.id] = addresses[0].address
|
272
|
+
else
|
273
|
+
all_active = false
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
ips
|
279
|
+
|
280
|
+
end
|
281
|
+
|
282
|
+
def self.ping_test(ip_addr)
|
283
|
+
|
284
|
+
ping_timeout = (Util.load_configs['openstack_ping_timeout'] || 60).to_i
|
285
|
+
|
286
|
+
begin
|
287
|
+
ping = self.default_ip_type == 6 ? 'ping6' : 'ping'
|
288
|
+
ping_command = "#{ping} -c 1 #{ip_addr} > /dev/null 2>&1"
|
289
|
+
Timeout::timeout(ping_timeout) do
|
290
|
+
while(1) do
|
291
|
+
return true if system(ping_command)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
rescue Timeout::Error => te
|
295
|
+
raise KytoonException, "Timeout pinging server: #{ping_command}"
|
296
|
+
end
|
297
|
+
|
298
|
+
return false
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
def self.destroy_instance(uuid)
|
303
|
+
begin
|
304
|
+
conn = self.init_connection
|
305
|
+
conn.server(uuid).delete!
|
306
|
+
rescue Exception => e
|
307
|
+
puts "Error deleting server: #{e.message}"
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
end
|
312
|
+
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|