kitchen-cloudstack 0.20.2 → 0.20.3
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.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/kitchen-cloudstack.gemspec +2 -2
- data/lib/kitchen/driver/cloudstack.rb +75 -70
- data/lib/kitchen/driver/cloudstack_version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee4d465613811dd5d90ac237c6dedd0c6244af0b
|
4
|
+
data.tar.gz: 2e8787096938b70798dc267c0efa47e06d4701bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc30e3effe7036cd5a5cd47c3842535cff1691d643694e76d981c531af0c6a1f9745a830ffd5822bbc1afc1b4ac5e99d0e61d8eb6f3fea0555e55dcb9a0aba2a
|
7
|
+
data.tar.gz: 01535d54ad3e924f5f192cb3d70c5fa704560948c2347f7fa1920d9e25e4df903da16f6555bcaed417f1497bf81577d327e904840e01269d3ebd889de279e6e0
|
data/README.md
CHANGED
@@ -32,9 +32,10 @@ Then to specify different OS templates,
|
|
32
32
|
cloudstack_template_id: [INSTANCE TEMPLATE ID]
|
33
33
|
cloudstack_serviceoffering_id: [INSTANCE SERVICE OFFERING ID]
|
34
34
|
cloudstack_zone_id: [INSTANCE ZONE ID]
|
35
|
+
OPTIONAL
|
35
36
|
cloudstack_network_id: [NETWORK ID FOR ISOLATED OR VPC NETWORKS]
|
36
37
|
cloudstack_security_group_id: [SECURITY GROUP ID FOR SHARED NETWORKS]
|
37
|
-
|
38
|
+
cloudstack_diskoffering_id: [INSTANCE DISK OFFERING ID]
|
38
39
|
cloudstack_ssh_keypair_name: [SSH KEY NAME]
|
39
40
|
cloudstack_sync_time: [NUMBER OF SECONDS TO WAIT FOR CLOUD-SET-GUEST-PASSWORD/SSHKEY]
|
40
41
|
To use the CloudStack public key provider, you need to have the .PEM file located in the same directory as
|
data/kitchen-cloudstack.gemspec
CHANGED
@@ -24,8 +24,8 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
25
25
|
spec.add_development_dependency 'rake', '~> 0'
|
26
26
|
|
27
|
-
spec.add_development_dependency 'cane', '~>
|
28
|
-
spec.add_development_dependency 'tailor', '~>
|
27
|
+
spec.add_development_dependency 'cane', '~> 2'
|
28
|
+
spec.add_development_dependency 'tailor', '~> 1'
|
29
29
|
spec.add_development_dependency 'countloc', '~> 0'
|
30
30
|
spec.add_development_dependency 'pry', '~> 0'
|
31
31
|
end
|
@@ -21,12 +21,9 @@ require 'kitchen'
|
|
21
21
|
require 'fog'
|
22
22
|
require 'socket'
|
23
23
|
require 'openssl'
|
24
|
-
# require 'pry'
|
25
24
|
|
26
25
|
module Kitchen
|
27
|
-
|
28
26
|
module Driver
|
29
|
-
|
30
27
|
# Cloudstack driver for Kitchen.
|
31
28
|
#
|
32
29
|
# @author Jeff Moody <fifthecho@gmail.com>
|
@@ -35,42 +32,47 @@ module Kitchen
|
|
35
32
|
default_config :username, 'root'
|
36
33
|
default_config :port, '22'
|
37
34
|
default_config :password, nil
|
38
|
-
|
35
|
+
|
39
36
|
def compute
|
40
37
|
cloudstack_uri = URI.parse(config[:cloudstack_api_url])
|
41
38
|
connection = Fog::Compute.new(
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
39
|
+
:provider => :cloudstack,
|
40
|
+
:cloudstack_api_key => config[:cloudstack_api_key],
|
41
|
+
:cloudstack_secret_access_key => config[:cloudstack_secret_key],
|
42
|
+
:cloudstack_host => cloudstack_uri.host,
|
43
|
+
:cloudstack_port => cloudstack_uri.port,
|
44
|
+
:cloudstack_path => cloudstack_uri.path,
|
45
|
+
:cloudstack_scheme => cloudstack_uri.scheme
|
49
46
|
)
|
50
47
|
end
|
51
48
|
|
52
49
|
def create_server
|
53
50
|
options = {}
|
51
|
+
|
54
52
|
config[:server_name] ||= generate_name(instance.name)
|
55
53
|
options['displayname'] = config[:server_name]
|
56
|
-
|
54
|
+
|
55
|
+
if config[:cloudstack_network_id]
|
57
56
|
options['networkids'] = config[:cloudstack_network_id]
|
58
57
|
end
|
59
58
|
|
60
|
-
if
|
59
|
+
if config[:cloudstack_security_group_id]
|
61
60
|
options['securitygroupids'] = config[:cloudstack_security_group_id]
|
62
61
|
end
|
63
62
|
|
64
|
-
if
|
63
|
+
if config[:cloudstack_ssh_keypair_name]
|
65
64
|
options['keypair'] = config[:cloudstack_ssh_keypair_name]
|
66
65
|
end
|
67
66
|
|
67
|
+
if config[:cloudstack_diskoffering_id]
|
68
|
+
options['diskofferingid'] = config[:cloudstack_diskoffering_id]
|
69
|
+
end
|
70
|
+
|
68
71
|
options[:templateid] = config[:cloudstack_template_id]
|
69
72
|
options[:serviceofferingid] = config[:cloudstack_serviceoffering_id]
|
70
73
|
options[:zoneid] = config[:cloudstack_zone_id]
|
71
74
|
|
72
75
|
debug(options)
|
73
|
-
# binding.pry
|
74
76
|
compute.deploy_virtual_machine(options)
|
75
77
|
end
|
76
78
|
|
@@ -78,22 +80,24 @@ module Kitchen
|
|
78
80
|
if not config[:name]
|
79
81
|
# Generate what should be a unique server name
|
80
82
|
config[:name] = "#{instance.name}-#{Etc.getlogin}-" +
|
81
|
-
|
83
|
+
"#{Socket.gethostname}-#{Array.new(8){rand(36).to_s(36)}.join}"
|
82
84
|
end
|
83
85
|
if config[:disable_ssl_validation]
|
84
86
|
require 'excon'
|
85
87
|
Excon.defaults[:ssl_verify_peer] = false
|
86
88
|
end
|
87
89
|
|
88
|
-
|
89
90
|
server = create_server
|
90
91
|
debug(server)
|
91
92
|
|
92
93
|
state[:server_id] = server['deployvirtualmachineresponse'].fetch('id')
|
93
|
-
start_jobid = {
|
94
|
+
start_jobid = {
|
95
|
+
'jobid' => server['deployvirtualmachineresponse'].fetch('jobid')
|
96
|
+
}
|
94
97
|
info("CloudStack instance <#{state[:server_id]}> created.")
|
95
98
|
debug("Job ID #{start_jobid}")
|
96
|
-
# Cloning the original job id hash because running the
|
99
|
+
# Cloning the original job id hash because running the
|
100
|
+
# query_async_job_result updates the hash to include
|
97
101
|
# more than just the job id (which I could work around, but I'm lazy).
|
98
102
|
jobid = start_jobid.clone
|
99
103
|
|
@@ -113,8 +117,12 @@ module Kitchen
|
|
113
117
|
|
114
118
|
# jobstatus of 2 is an error response
|
115
119
|
if server_start['queryasyncjobresultresponse'].fetch('jobstatus').to_i == 2
|
116
|
-
errortext = server_start['queryasyncjobresultresponse']
|
120
|
+
errortext = server_start['queryasyncjobresultresponse']
|
121
|
+
.fetch('jobresult')
|
122
|
+
.fetch('errortext')
|
123
|
+
|
117
124
|
error("ERROR! Job failed with #{errortext}")
|
125
|
+
|
118
126
|
raise ActionFailed, "Could not create server #{errortext}"
|
119
127
|
end
|
120
128
|
|
@@ -124,9 +132,10 @@ module Kitchen
|
|
124
132
|
debug(server_info)
|
125
133
|
print "(server ready)"
|
126
134
|
|
127
|
-
|
128
135
|
keypair = nil
|
129
|
-
if
|
136
|
+
if config[:keypair_search_directory] and File.exist?(
|
137
|
+
"#{config[:keypair_search_directory]}/#{config[:cloudstack_ssh_keypair_name]}.pem"
|
138
|
+
)
|
130
139
|
keypair = "#{config[:keypair_search_directory]}/#{config[:cloudstack_ssh_keypair_name]}.pem"
|
131
140
|
debug("Keypair being used is #{keypair}")
|
132
141
|
elsif File.exist?("./#{config[:cloudstack_ssh_keypair_name]}.pem")
|
@@ -142,11 +151,9 @@ module Kitchen
|
|
142
151
|
info("Keypair specified but not found. Using password if enabled.")
|
143
152
|
end
|
144
153
|
|
145
|
-
# binding.pry
|
146
|
-
# debug("Keypair is #{keypair}")
|
147
154
|
state[:hostname] = config[:cloudstack_vm_public_ip] || server_info.fetch('nic').first.fetch('ipaddress')
|
148
155
|
|
149
|
-
if
|
156
|
+
if keypair
|
150
157
|
debug("Using keypair: #{keypair}")
|
151
158
|
info("SSH for #{state[:hostname]} with keypair #{config[:cloudstack_ssh_keypair_name]}.")
|
152
159
|
ssh_key = File.read(keypair)
|
@@ -159,7 +166,7 @@ module Kitchen
|
|
159
166
|
|
160
167
|
ssh = Fog::SSH.new(state[:hostname], config[:username], {:keys => keypair})
|
161
168
|
debug("Connecting to : #{state[:hostname]} as #{config[:username]} using keypair #{keypair}.")
|
162
|
-
elsif
|
169
|
+
elsif server_info.fetch('passwordenabled')
|
163
170
|
password = server_info.fetch('password')
|
164
171
|
config[:password] = password
|
165
172
|
# Print out IP and password so you can record it if you want.
|
@@ -170,7 +177,7 @@ module Kitchen
|
|
170
177
|
|
171
178
|
ssh = Fog::SSH.new(state[:hostname], config[:username], {:password => password})
|
172
179
|
debug("Connecting to : #{state[:hostname]} as #{config[:username]} using password #{password}.")
|
173
|
-
elsif
|
180
|
+
elsif config[:password]
|
174
181
|
info("Connecting with user #{config[:username]} with password #{config[:password]}")
|
175
182
|
|
176
183
|
wait_for_sshd(state[:hostname], config[:username], {:password => config[:password]})
|
@@ -180,7 +187,6 @@ module Kitchen
|
|
180
187
|
else
|
181
188
|
info("No keypair specified (or file not found) nor is this a password enabled template. You will have to manually copy your SSH public key to #{state[:hostname]} to use this Kitchen.")
|
182
189
|
end
|
183
|
-
# binding.pry
|
184
190
|
|
185
191
|
validate_ssh_connectivity(ssh)
|
186
192
|
|
@@ -189,10 +195,10 @@ module Kitchen
|
|
189
195
|
end
|
190
196
|
|
191
197
|
def destroy(state)
|
192
|
-
return
|
198
|
+
return unless state[:server_id]
|
193
199
|
debug("Destroying #{state[:server_id]}")
|
194
200
|
server = compute.servers.get(state[:server_id])
|
195
|
-
if
|
201
|
+
if server
|
196
202
|
compute.destroy_virtual_machine({'id' => state[:server_id]})
|
197
203
|
end
|
198
204
|
info("CloudStack instance <#{state[:server_id]}> destroyed.")
|
@@ -201,41 +207,41 @@ module Kitchen
|
|
201
207
|
end
|
202
208
|
|
203
209
|
def validate_ssh_connectivity(ssh)
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
210
|
+
rescue Errno::ETIMEDOUT
|
211
|
+
debug("SSH connection timed out. Retrying.")
|
212
|
+
sleep 2
|
213
|
+
false
|
214
|
+
rescue Errno::EPERM
|
215
|
+
debug("SSH connection returned error. Retrying.")
|
216
|
+
false
|
217
|
+
rescue Errno::ECONNREFUSED
|
218
|
+
debug("SSH connection returned connection refused. Retrying.")
|
219
|
+
sleep 2
|
220
|
+
false
|
221
|
+
rescue Errno::EHOSTUNREACH
|
222
|
+
debug("SSH connection returned host unreachable. Retrying.")
|
223
|
+
sleep 2
|
224
|
+
false
|
225
|
+
rescue Errno::ENETUNREACH
|
226
|
+
debug("SSH connection returned network unreachable. Retrying.")
|
227
|
+
sleep 30
|
228
|
+
false
|
229
|
+
rescue Net::SSH::Disconnect
|
230
|
+
debug("SSH connection has been disconnected. Retrying.")
|
231
|
+
sleep 15
|
232
|
+
false
|
233
|
+
rescue Net::SSH::AuthenticationFailed
|
234
|
+
debug("SSH authentication has failed. Password or Keys may not be in place yet. Retrying.")
|
235
|
+
sleep 15
|
236
|
+
false
|
237
|
+
ensure
|
238
|
+
sync_time = 0
|
239
|
+
if (config[:cloudstack_sync_time])
|
240
|
+
sync_time = config[:cloudstack_sync_time]
|
241
|
+
end
|
242
|
+
sleep(sync_time)
|
243
|
+
debug("Connecting to host and running ls")
|
244
|
+
ssh.run('ls')
|
239
245
|
end
|
240
246
|
|
241
247
|
def deploy_private_key(ssh)
|
@@ -250,12 +256,12 @@ module Kitchen
|
|
250
256
|
|
251
257
|
if user_public_key
|
252
258
|
ssh.run([
|
253
|
-
|
254
|
-
|
255
|
-
|
259
|
+
%{mkdir .ssh},
|
260
|
+
%{echo "#{user_public_key}" >> ~/.ssh/authorized_keys}
|
261
|
+
])
|
256
262
|
end
|
257
263
|
end
|
258
|
-
|
264
|
+
|
259
265
|
def generate_name(base)
|
260
266
|
# Generate what should be a unique server name
|
261
267
|
sep = '-'
|
@@ -276,7 +282,6 @@ module Kitchen
|
|
276
282
|
end
|
277
283
|
pieces.join sep
|
278
284
|
end
|
279
|
-
|
280
285
|
end
|
281
286
|
end
|
282
287
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kitchen-cloudstack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.20.
|
4
|
+
version: 0.20.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Moody
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: test-kitchen
|
@@ -84,28 +84,28 @@ dependencies:
|
|
84
84
|
requirements:
|
85
85
|
- - "~>"
|
86
86
|
- !ruby/object:Gem::Version
|
87
|
-
version: '
|
87
|
+
version: '2'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
90
|
version_requirements: !ruby/object:Gem::Requirement
|
91
91
|
requirements:
|
92
92
|
- - "~>"
|
93
93
|
- !ruby/object:Gem::Version
|
94
|
-
version: '
|
94
|
+
version: '2'
|
95
95
|
- !ruby/object:Gem::Dependency
|
96
96
|
name: tailor
|
97
97
|
requirement: !ruby/object:Gem::Requirement
|
98
98
|
requirements:
|
99
99
|
- - "~>"
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version: '
|
101
|
+
version: '1'
|
102
102
|
type: :development
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
105
105
|
requirements:
|
106
106
|
- - "~>"
|
107
107
|
- !ruby/object:Gem::Version
|
108
|
-
version: '
|
108
|
+
version: '1'
|
109
109
|
- !ruby/object:Gem::Dependency
|
110
110
|
name: countloc
|
111
111
|
requirement: !ruby/object:Gem::Requirement
|