chef-vpc-toolkit 2.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.
- data/.gitignore +2 -0
- data/COPYING +26 -0
- data/README.rdoc +75 -0
- data/Rakefile +34 -0
- data/VERSION +1 -0
- data/bin/chef-vpc-toolkit +80 -0
- data/config/chef_installer.yml +20 -0
- data/config/databags.json.example +24 -0
- data/config/nodes.json +10 -0
- data/config/server_group.json +16 -0
- data/contrib/doc/ChefVPCToolkit.odt +0 -0
- data/contrib/doc/ChefVPCToolkit.pdf +0 -0
- data/contrib/etc/chef_vpc_toolkit.conf +10 -0
- data/contrib/rake/Rakefile +23 -0
- data/cookbook-repos/local/README +5 -0
- data/cookbook-repos/local/Rakefile +66 -0
- data/cookbook-repos/local/certificates/README +1 -0
- data/cookbook-repos/local/config/client.rb.example +21 -0
- data/cookbook-repos/local/config/knife.rb.example +10 -0
- data/cookbook-repos/local/config/rake.rb +60 -0
- data/cookbook-repos/local/config/server.rb.example +42 -0
- data/cookbook-repos/local/config/solo.rb.example +13 -0
- data/cookbook-repos/local/cookbooks/README +4 -0
- data/cookbook-repos/local/cookbooks/motd/README.rdoc +15 -0
- data/cookbook-repos/local/cookbooks/motd/attributes/motd.rb +1 -0
- data/cookbook-repos/local/cookbooks/motd/metadata.rb +6 -0
- data/cookbook-repos/local/cookbooks/motd/recipes/default.rb +13 -0
- data/cookbook-repos/local/cookbooks/motd/templates/default/motd.erb +1 -0
- data/cookbook-repos/local/roles/README +4 -0
- data/lib/chef-vpc-toolkit.rb +6 -0
- data/lib/chef-vpc-toolkit/chef-0.9.bash +232 -0
- data/lib/chef-vpc-toolkit/chef_bootstrap/centos.bash +47 -0
- data/lib/chef-vpc-toolkit/chef_bootstrap/fedora.bash +41 -0
- data/lib/chef-vpc-toolkit/chef_bootstrap/rhel.bash +38 -0
- data/lib/chef-vpc-toolkit/chef_bootstrap/ubuntu.bash +32 -0
- data/lib/chef-vpc-toolkit/chef_installer.rb +276 -0
- data/lib/chef-vpc-toolkit/cloud_files.bash +67 -0
- data/lib/chef-vpc-toolkit/cloud_servers_vpc.rb +285 -0
- data/lib/chef-vpc-toolkit/http_util.rb +117 -0
- data/lib/chef-vpc-toolkit/ssh_util.rb +22 -0
- data/lib/chef-vpc-toolkit/util.rb +56 -0
- data/lib/chef-vpc-toolkit/version.rb +8 -0
- data/rake/chef_vpc_toolkit.rake +284 -0
- data/test/cloud_servers_vpc_test.rb +174 -0
- data/test/test_helper.rb +25 -0
- metadata +153 -0
@@ -0,0 +1,285 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'builder'
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'rexml/xpath'
|
5
|
+
|
6
|
+
module ChefVPCToolkit
|
7
|
+
|
8
|
+
module CloudServersVPC
|
9
|
+
|
10
|
+
SERVER_GROUP_CONFIG_FILE = CHEF_VPC_PROJECT + File::SEPARATOR + "config" + File::SEPARATOR + "server_group.json"
|
11
|
+
|
12
|
+
def self.load_public_key
|
13
|
+
|
14
|
+
ssh_dir=ENV['HOME']+File::SEPARATOR+".ssh"+File::SEPARATOR
|
15
|
+
if File.exists?(ssh_dir+"id_rsa.pub")
|
16
|
+
pubkey=IO.read(ssh_dir+"id_rsa.pub")
|
17
|
+
elsif File.exists?(ssh_dir+"id_dsa.pub")
|
18
|
+
pubkey=IO.read(ssh_dir+"id_dsa.pub")
|
19
|
+
else
|
20
|
+
raise "Failed to load SSH key. Please create a SSH public key pair in your HOME directory."
|
21
|
+
end
|
22
|
+
|
23
|
+
pubkey.chomp
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
# generate a Server Group XML from server_group.json
|
28
|
+
def self.server_group_xml(config_file=SERVER_GROUP_CONFIG_FILE, owner=ENV['USER'])
|
29
|
+
|
30
|
+
json_hash=JSON.parse(IO.read(config_file))
|
31
|
+
|
32
|
+
xml = Builder::XmlMarkup.new
|
33
|
+
xml.tag! "server-group" do |sg|
|
34
|
+
sg.name(json_hash["name"])
|
35
|
+
sg.description(json_hash["description"])
|
36
|
+
sg.tag! "owner-name", owner
|
37
|
+
sg.tag! "domain-name", json_hash["domain_name"]
|
38
|
+
if json_hash["vpn_network"] then
|
39
|
+
sg.tag! "vpn-network", json_hash["vpn_network"]
|
40
|
+
else
|
41
|
+
sg.tag! "vpn-network", "172.19.0.0"
|
42
|
+
end
|
43
|
+
if json_hash["vpn_subnet"] then
|
44
|
+
sg.tag! "vpn-subnet", json_hash["vpn_subnet"]
|
45
|
+
else
|
46
|
+
sg.tag! "vpn-subnet", "255.255.128.0"
|
47
|
+
end
|
48
|
+
sg.servers("type" => "array") do |servers|
|
49
|
+
json_hash["servers"].each_pair do |server_name, server_config|
|
50
|
+
servers.server do |server|
|
51
|
+
server.name(server_name)
|
52
|
+
if server_config["description"] then
|
53
|
+
server.description(server_config["description"])
|
54
|
+
else
|
55
|
+
server.description(server_name)
|
56
|
+
end
|
57
|
+
server.tag! "flavor-id", server_config["flavor_id"]
|
58
|
+
server.tag! "image-id", server_config["image_id"]
|
59
|
+
if server_config["openvpn_server"]
|
60
|
+
server.tag! "openvpn-server", "true", { "type" => "boolean"}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
sg.tag! "ssh-public-keys", { "type" => "array"} do |ssh_keys|
|
66
|
+
ssh_keys.tag! "ssh-public-key" do |ssh_public_key|
|
67
|
+
ssh_public_key.description "#{ENV['USER']}'s public key"
|
68
|
+
ssh_public_key.tag! "public-key", self.load_public_key
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
xml.target!
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.server_group_hash(xml)
|
77
|
+
|
78
|
+
hash={}
|
79
|
+
dom = REXML::Document.new(xml)
|
80
|
+
REXML::XPath.each(dom, "/server-group") do |sg|
|
81
|
+
|
82
|
+
hash["name"]=sg.elements["name"].text
|
83
|
+
hash["description"]=sg.elements["description"].text
|
84
|
+
hash["id"]=sg.elements["id"].text
|
85
|
+
hash["domain-name"]=sg.elements["domain-name"].text
|
86
|
+
|
87
|
+
hash["servers"]={}
|
88
|
+
REXML::XPath.each(dom, "//server") do |server|
|
89
|
+
server_name=server.elements["name"].text
|
90
|
+
server_attributes={
|
91
|
+
"id" => server.elements["id"].text,
|
92
|
+
"cloud-server-id-number" => server.elements["cloud-server-id-number"].text,
|
93
|
+
"status" => server.elements["status"].text,
|
94
|
+
"external-ip-addr" => server.elements["external-ip-addr"].text,
|
95
|
+
"internal-ip-addr" => server.elements["internal-ip-addr"].text,
|
96
|
+
"error-message" => server.elements["error-message"].text,
|
97
|
+
"image-id" => server.elements["image-id"].text,
|
98
|
+
"retry-count" => server.elements["retry-count"].text,
|
99
|
+
"openvpn-server" => server.elements["openvpn-server"].text
|
100
|
+
}
|
101
|
+
if server.elements["openvpn-server"].text and server.elements["openvpn-server"].text == "true" and server.elements["external-ip-addr"].text then
|
102
|
+
hash["vpn-gateway"]=server.elements["external-ip-addr"].text
|
103
|
+
end
|
104
|
+
hash["servers"].store(server_name, server_attributes)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
hash
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.server_group_xml_for_id(configs, dir, id=nil)
|
113
|
+
|
114
|
+
if id then
|
115
|
+
xml=HttpUtil.get(
|
116
|
+
configs["cloud_servers_vpc_url"]+"/server_groups/#{id}.xml",
|
117
|
+
configs["cloud_servers_vpc_username"],
|
118
|
+
configs["cloud_servers_vpc_password"]
|
119
|
+
)
|
120
|
+
else
|
121
|
+
recent_hash=CloudServersVPC.most_recent_server_group_hash(dir)
|
122
|
+
raise "No server group files exist." if recent_hash.nil?
|
123
|
+
xml=HttpUtil.get(
|
124
|
+
configs["cloud_servers_vpc_url"]+"/server_groups/#{recent_hash['id']}.xml",
|
125
|
+
configs["cloud_servers_vpc_username"],
|
126
|
+
configs["cloud_servers_vpc_password"]
|
127
|
+
)
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.most_recent_server_group_hash(dir_pattern)
|
134
|
+
server_groups=[]
|
135
|
+
Dir[dir_pattern].each do |file|
|
136
|
+
server_groups << CloudServersVPC.server_group_hash(IO.read(file))
|
137
|
+
end
|
138
|
+
if server_groups.size > 0 then
|
139
|
+
server_groups.sort { |a,b| b["id"].to_i <=> a["id"].to_i }[0]
|
140
|
+
else
|
141
|
+
nil
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.print_server_group(hash)
|
146
|
+
|
147
|
+
puts "Cloud Group ID: #{hash["id"]}"
|
148
|
+
puts "name: #{hash["name"]}"
|
149
|
+
puts "description: #{hash["description"]}"
|
150
|
+
puts "domain name: #{hash["domain-name"]}"
|
151
|
+
puts "VPN gateway IP: #{hash["vpn-gateway"]}"
|
152
|
+
puts "Servers:"
|
153
|
+
hash["servers"].each_pair do |name, attrs|
|
154
|
+
puts "\tname: #{name} (id: #{attrs['cloud-server-id-number']})"
|
155
|
+
puts "\tstatus: #{attrs['status']}"
|
156
|
+
if attrs["openvpn-server"] and attrs["openvpn-server"] == "true" then
|
157
|
+
puts "\tOpenVPN server: #{attrs['openvpn-server']}"
|
158
|
+
end
|
159
|
+
if attrs["error-message"] then
|
160
|
+
puts "\tlast error message: #{attrs['error-message']}"
|
161
|
+
end
|
162
|
+
puts "\t--"
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.server_names(hash)
|
168
|
+
|
169
|
+
names=[]
|
170
|
+
|
171
|
+
hash["servers"].each_pair do |name, hash|
|
172
|
+
if block_given? then
|
173
|
+
yield name
|
174
|
+
else
|
175
|
+
names << name
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
names
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
# default timeout of 20 minutes
|
184
|
+
def self.poll_until_online(group_id, timeout=1200)
|
185
|
+
|
186
|
+
configs=Util.load_configs
|
187
|
+
|
188
|
+
online = false
|
189
|
+
count=0
|
190
|
+
until online or (count*20) >= timeout.to_i do
|
191
|
+
count+=1
|
192
|
+
begin
|
193
|
+
xml=HttpUtil.get(
|
194
|
+
configs["cloud_servers_vpc_url"]+"/server_groups/#{group_id}.xml",
|
195
|
+
configs["cloud_servers_vpc_username"],
|
196
|
+
configs["cloud_servers_vpc_password"]
|
197
|
+
)
|
198
|
+
|
199
|
+
hash=CloudServersVPC.server_group_hash(xml)
|
200
|
+
|
201
|
+
online=true
|
202
|
+
hash["servers"].each_pair do |name, attrs|
|
203
|
+
if ["Pending", "Rebuilding"].include?(attrs["status"]) then
|
204
|
+
online=false
|
205
|
+
end
|
206
|
+
if attrs["status"] == "Failed" then
|
207
|
+
raise "Failed to create server group with the following message: #{attrs['error-message']}"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
if not online
|
211
|
+
yield hash if block_given?
|
212
|
+
sleep 20
|
213
|
+
end
|
214
|
+
rescue EOFError
|
215
|
+
end
|
216
|
+
end
|
217
|
+
if (count*20) >= timeout.to_i then
|
218
|
+
raise "Timeout waiting for server groups to come online."
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.os_types(server_group_hash)
|
224
|
+
|
225
|
+
os_types={}
|
226
|
+
server_group_hash["servers"].each_pair do |name, attrs|
|
227
|
+
os_type = case attrs["image-id"].to_i
|
228
|
+
when 51 # Centos 5.5
|
229
|
+
"centos"
|
230
|
+
when 187811 # Centos 5.4
|
231
|
+
"centos"
|
232
|
+
when 53 # Fedora 13
|
233
|
+
"fedora"
|
234
|
+
when 17 # Fedora 12
|
235
|
+
"fedora"
|
236
|
+
when 14 # RHEL 5.4
|
237
|
+
"rhel"
|
238
|
+
when 62 # RHEL 5.5
|
239
|
+
"rhel"
|
240
|
+
when 49 # Ubuntu 10.04
|
241
|
+
"ubuntu"
|
242
|
+
when 14362 # Ubuntu 9.10
|
243
|
+
"ubuntu"
|
244
|
+
when 8 # Ubuntu 9.04
|
245
|
+
"ubuntu"
|
246
|
+
else
|
247
|
+
"unknown"
|
248
|
+
end
|
249
|
+
if block_given? then
|
250
|
+
yield name, os_type
|
251
|
+
else
|
252
|
+
os_types.store(name, os_type)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
os_types
|
256
|
+
|
257
|
+
end
|
258
|
+
|
259
|
+
def self.rebuild(server_group_hash, server_name)
|
260
|
+
|
261
|
+
configs=Util.load_configs
|
262
|
+
|
263
|
+
server_id=nil
|
264
|
+
image_id=nil
|
265
|
+
server_group_hash["servers"].each_pair do |name, attrs|
|
266
|
+
if name == server_name then
|
267
|
+
raise "Error: Rebuilding the OpenVPN server is not supported at this time." if attrs["openvpn-server"] == "true"
|
268
|
+
server_id=attrs["id"]
|
269
|
+
image_id=attrs["image-id"]
|
270
|
+
end
|
271
|
+
end
|
272
|
+
raise "Unable to find server name: #{server_name}" if server_id.nil?
|
273
|
+
|
274
|
+
HttpUtil.post(
|
275
|
+
configs["cloud_servers_vpc_url"]+"/servers/#{server_id}/rebuild",
|
276
|
+
{},
|
277
|
+
configs["cloud_servers_vpc_username"],
|
278
|
+
configs["cloud_servers_vpc_password"]
|
279
|
+
)
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
4
|
+
|
5
|
+
module ChefVPCToolkit
|
6
|
+
|
7
|
+
module HttpUtil
|
8
|
+
|
9
|
+
MULTI_PART_BOUNDARY="jtZ!pZ1973um"
|
10
|
+
|
11
|
+
def self.file_upload(url_string, file_data={}, post_data={}, auth_user=nil, auth_password=nil)
|
12
|
+
url=URI.parse(url_string)
|
13
|
+
http = Net::HTTP.new(url.host,url.port)
|
14
|
+
req = Net::HTTP::Post.new(url.path)
|
15
|
+
|
16
|
+
post_arr=[]
|
17
|
+
post_data.each_pair do |key, value|
|
18
|
+
post_arr << "--#{MULTI_PART_BOUNDARY}\r\n"
|
19
|
+
post_arr << "Content-Disposition: form-data; name=\"#{key}\"\r\n"
|
20
|
+
post_arr << "\r\n"
|
21
|
+
post_arr << value
|
22
|
+
post_arr << "\r\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
file_data.each_pair do |name, file|
|
26
|
+
post_arr << "--#{MULTI_PART_BOUNDARY}\r\n"
|
27
|
+
post_arr << "Content-Disposition: form-data; name=\"#{name}\"; filename=\"#{File.basename(file)}\"\r\n"
|
28
|
+
post_arr << "Content-Type: text/plain\r\n"
|
29
|
+
post_arr << "\r\n"
|
30
|
+
post_arr << File.read(file)
|
31
|
+
post_arr << "\r\n--#{MULTI_PART_BOUNDARY}--\r\n"
|
32
|
+
end
|
33
|
+
post_arr << "--#{MULTI_PART_BOUNDARY}--\r\n\r\n"
|
34
|
+
|
35
|
+
req.body=post_arr.join
|
36
|
+
|
37
|
+
if url_string =~ /^https/
|
38
|
+
http.use_ssl = true
|
39
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
40
|
+
end
|
41
|
+
req.basic_auth auth_user, auth_password if auth_user and auth_password
|
42
|
+
req["Content-Type"] = "multipart/form-data, boundary=#{MULTI_PART_BOUNDARY}"
|
43
|
+
|
44
|
+
response = http.request(req)
|
45
|
+
case response
|
46
|
+
when Net::HTTPSuccess
|
47
|
+
return response.body
|
48
|
+
else
|
49
|
+
puts response.body
|
50
|
+
response.error!
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.post(url_string, post_data, auth_user=nil, auth_password=nil)
|
55
|
+
url=URI.parse(url_string)
|
56
|
+
http = Net::HTTP.new(url.host,url.port)
|
57
|
+
req = Net::HTTP::Post.new(url.path)
|
58
|
+
if post_data.kind_of?(String) then
|
59
|
+
req.body=post_data
|
60
|
+
elsif post_data.kind_of?(Hash) then
|
61
|
+
req.form_data=post_data
|
62
|
+
else
|
63
|
+
raise "Invalid post data type."
|
64
|
+
end
|
65
|
+
if url_string =~ /^https/
|
66
|
+
http.use_ssl = true
|
67
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
68
|
+
end
|
69
|
+
req.basic_auth auth_user, auth_password if auth_user and auth_password
|
70
|
+
response = http.request(req)
|
71
|
+
case response
|
72
|
+
when Net::HTTPSuccess
|
73
|
+
return response.body
|
74
|
+
else
|
75
|
+
response.error!
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.get(url_string, auth_user=nil, auth_password=nil)
|
80
|
+
url=URI.parse(url_string)
|
81
|
+
http = Net::HTTP.new(url.host,url.port)
|
82
|
+
req = Net::HTTP::Get.new(url.path)
|
83
|
+
if url_string =~ /^https/
|
84
|
+
http.use_ssl = true
|
85
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
86
|
+
end
|
87
|
+
req.basic_auth auth_user, auth_password if auth_user and auth_password
|
88
|
+
response = http.request(req)
|
89
|
+
case response
|
90
|
+
when Net::HTTPSuccess
|
91
|
+
return response.body
|
92
|
+
else
|
93
|
+
response.error!
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.delete(url_string, auth_user=nil, auth_password=nil)
|
98
|
+
url=URI.parse(url_string)
|
99
|
+
http = Net::HTTP.new(url.host,url.port)
|
100
|
+
req = Net::HTTP::Delete.new(url.path)
|
101
|
+
if url_string =~ /^https/
|
102
|
+
http.use_ssl = true
|
103
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
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
|
+
response.error!
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ChefVPCToolkit
|
2
|
+
|
3
|
+
module SshUtil
|
4
|
+
|
5
|
+
def self.remove_known_hosts_ip(ip, known_hosts_file=File.join(ENV['HOME'], ".ssh", "known_hosts"))
|
6
|
+
|
7
|
+
return if ip.nil? or ip.empty?
|
8
|
+
|
9
|
+
existing=IO.read(known_hosts_file)
|
10
|
+
File.open(known_hosts_file, 'w') do |file|
|
11
|
+
existing.each_line do |line|
|
12
|
+
if not line =~ Regexp.new("^#{ip}.*$") then
|
13
|
+
file.write(line)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module ChefVPCToolkit
|
4
|
+
|
5
|
+
module Util
|
6
|
+
|
7
|
+
def self.load_configs
|
8
|
+
|
9
|
+
config_file=ENV['CLOUD_TEST_CONFIG_FILE']
|
10
|
+
if config_file.nil? then
|
11
|
+
|
12
|
+
config_file=ENV['HOME']+File::SEPARATOR+".cloud_toolkit.conf"
|
13
|
+
if not File.exists?(config_file) then
|
14
|
+
config_file="/etc/cloud_toolkit.conf"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
if File.exists?(config_file) then
|
20
|
+
configs=YAML.load_file(config_file)
|
21
|
+
raise_if_nil_or_empty(configs, "cloud_servers_vpc_url")
|
22
|
+
raise_if_nil_or_empty(configs, "cloud_servers_vpc_username")
|
23
|
+
raise_if_nil_or_empty(configs, "cloud_servers_vpc_password")
|
24
|
+
return configs
|
25
|
+
else
|
26
|
+
raise "Failed to load cloud toolkit config file. Please configure /etc/cloud_toolkit.conf or create a .cloud_toolkit.conf config file in your HOME directory."
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.raise_if_nil_or_empty(options, key)
|
32
|
+
if options[key].nil? || options[key].empty? then
|
33
|
+
raise "Please specify a valid #{key.to_s} parameter."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.hash_for_group(configs=Util.load_configs)
|
38
|
+
|
39
|
+
id=ENV['GROUP_ID']
|
40
|
+
configs=Util.load_configs
|
41
|
+
hash=nil
|
42
|
+
if id.nil? then
|
43
|
+
hash=CloudServersVPC.most_recent_server_group_hash(File.join(TMP_SG, '*.xml'))
|
44
|
+
else
|
45
|
+
file=File.join(TMP_SG, "#{id}.xml")
|
46
|
+
hash = CloudServersVPC.server_group_hash(IO.read(file))
|
47
|
+
end
|
48
|
+
raise "Create a cloud before running this command." if hash.nil?
|
49
|
+
|
50
|
+
hash
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|