rouster 0.5 → 0.7
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/.gitignore +4 -1
- data/.reek +63 -0
- data/.travis.yml +11 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +102 -0
- data/README.md +233 -7
- data/Rakefile +52 -34
- data/Vagrantfile +26 -8
- data/examples/aws.rb +85 -0
- data/examples/openstack.rb +61 -0
- data/examples/passthrough.rb +71 -0
- data/lib/rouster.rb +380 -262
- data/lib/rouster/deltas.rb +470 -138
- data/lib/rouster/puppet.rb +155 -26
- data/lib/rouster/testing.rb +205 -46
- data/lib/rouster/tests.rb +40 -11
- data/lib/rouster/vagrant.rb +311 -0
- data/path_helper.rb +3 -4
- data/plugins/aws.rb +347 -0
- data/plugins/openstack.rb +136 -0
- data/test/basic.rb +4 -1
- data/test/functional/deltas/test_get_crontab.rb +64 -2
- data/test/functional/deltas/test_get_groups.rb +74 -2
- data/test/functional/deltas/test_get_os.rb +68 -0
- data/test/functional/deltas/test_get_packages.rb +73 -6
- data/test/functional/deltas/test_get_ports.rb +26 -1
- data/test/functional/deltas/test_get_services.rb +43 -5
- data/test/functional/deltas/test_get_users.rb +35 -2
- data/test/functional/puppet/test_facter.rb +41 -1
- data/test/functional/test_caching.rb +2 -2
- data/test/functional/test_inspect.rb +1 -1
- data/test/functional/test_is_file.rb +17 -1
- data/test/functional/test_is_in_file.rb +40 -0
- data/test/functional/test_new.rb +233 -22
- data/test/functional/test_passthroughs.rb +94 -0
- data/test/functional/test_put.rb +2 -2
- data/test/functional/test_validate_file.rb +104 -3
- data/test/puppet/test_apply.rb +8 -6
- data/test/unit/puppet/resources/puppet_run_with_failed_exec +59 -0
- data/test/unit/puppet/resources/puppet_run_with_successful_exec +61 -0
- data/test/unit/puppet/test_get_puppet_star.rb +27 -4
- data/test/unit/puppet/test_puppet_parsing.rb +44 -0
- data/test/unit/test_new.rb +88 -0
- data/test/unit/test_parse_ls_string.rb +67 -0
- data/test/unit/testing/resources/osx-launchd +285 -0
- data/test/unit/testing/resources/rhel-systemd +46 -0
- data/test/unit/testing/resources/rhel-systemv +41 -0
- data/test/unit/testing/resources/rhel-upstart +20 -0
- data/test/unit/testing/test_get_services.rb +178 -0
- data/test/unit/testing/test_validate_cron.rb +78 -0
- data/test/unit/testing/test_validate_package.rb +36 -10
- data/test/unit/testing/test_validate_port.rb +5 -0
- metadata +42 -21
- data/test/puppet/test_roles.rb +0 -186
data/plugins/aws.rb
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
## plugins/aws.rb - provide helper functions for Rouster objects running on AWS/EC2
|
|
3
|
+
|
|
4
|
+
require sprintf('%s/../%s', File.dirname(File.expand_path(__FILE__)), 'path_helper')
|
|
5
|
+
|
|
6
|
+
require 'fog'
|
|
7
|
+
require 'uri'
|
|
8
|
+
|
|
9
|
+
class Rouster
|
|
10
|
+
|
|
11
|
+
attr_reader :ec2, :elb # expose AWS workers
|
|
12
|
+
attr_reader :instance_data # the result of the runInstances request
|
|
13
|
+
|
|
14
|
+
def aws_get_url(url)
|
|
15
|
+
# convenience method to run curls from inside the VM
|
|
16
|
+
self.run(sprintf('curl -s %s', url))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# TODO should this be 'aws_ip'?
|
|
20
|
+
def aws_get_ip (method = :internal, type = :public)
|
|
21
|
+
# allowed methods: :internal (check meta-data inside VM), :aws (ask API)
|
|
22
|
+
# allowed types: :public, :private
|
|
23
|
+
self.aws_describe_instance
|
|
24
|
+
|
|
25
|
+
if method.equal?(:internal)
|
|
26
|
+
key = type.equal?(:public) ? 'public-ipv4' : 'local-ipv4'
|
|
27
|
+
murl = sprintf('http://169.254.169.254/latest/meta-data/%s', key)
|
|
28
|
+
result = self.aws_get_url(murl)
|
|
29
|
+
else
|
|
30
|
+
key = type.equal?(:public) ? 'ipAddress' : 'privateIpAddress'
|
|
31
|
+
result = @instance_data[key]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
result
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def aws_get_userdata
|
|
38
|
+
murl = 'http://169.254.169.254/latest/user-data/'
|
|
39
|
+
result = self.aws_get_url(murl)
|
|
40
|
+
|
|
41
|
+
if result.match(/\S=\S/)
|
|
42
|
+
# TODO should we really be doing this?
|
|
43
|
+
userdata = Hash.new()
|
|
44
|
+
result.split("\n").each do |line|
|
|
45
|
+
if line.match(/^(.*?)=(.*)/)
|
|
46
|
+
userdata[$1] = $2
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
else
|
|
50
|
+
userdata = result
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
userdata
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# return a hash containing meta-data items
|
|
57
|
+
def aws_get_metadata
|
|
58
|
+
murl = 'http://169.254.169.254/latest/meta-data/'
|
|
59
|
+
result = self.aws_get_url(murl)
|
|
60
|
+
metadata = Hash.new()
|
|
61
|
+
|
|
62
|
+
# TODO this isn't entirely right.. if the element ends in '/', it's actually another level of hash..
|
|
63
|
+
result.split("\n").each do |element|
|
|
64
|
+
metadata[element] = self.aws_get_url(sprintf('%s%s', murl, element))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
metadata
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def aws_get_hostname (method = :internal, type = :public)
|
|
71
|
+
# allowed methods: :internal (check meta-data inside VM), :aws (ask API)
|
|
72
|
+
# allowed types: :public, :private
|
|
73
|
+
self.aws_describe_instance
|
|
74
|
+
|
|
75
|
+
result = nil
|
|
76
|
+
|
|
77
|
+
if method.equal?(:internal)
|
|
78
|
+
key = type.equal?(:public) ? 'public-hostname' : 'local-hostname'
|
|
79
|
+
murl = sprintf('http://169.254.169.254/latest/meta-data/%s', key)
|
|
80
|
+
result = self.aws_get_url(murl)
|
|
81
|
+
else
|
|
82
|
+
key = type.equal?(:public) ? 'dnsName' : 'privateDnsName'
|
|
83
|
+
result = @instance_data[key]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
result
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def aws_get_instance ()
|
|
90
|
+
if ! @instance_data.nil? and @instance_data.has_key?('instanceId')
|
|
91
|
+
return @instance_data['instanceId'] # we already know the id
|
|
92
|
+
elsif @passthrough.has_key?(:instance)
|
|
93
|
+
return @passthrough[:instance] # we know the id we want
|
|
94
|
+
else
|
|
95
|
+
@logger.debug(sprintf('unable to determine ami-id from instance_data[%s] or passthrough specification[%s]', @instance_data, @passthrough))
|
|
96
|
+
return nil # we don't have an id yet, likely a up() call
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def aws_get_ami ()
|
|
101
|
+
if ! @instance_data.nil? and @instance_data.has_key?('ami')
|
|
102
|
+
return @instance_data['ami']
|
|
103
|
+
else
|
|
104
|
+
return @passthrough[:ami]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def aws_up
|
|
109
|
+
# wait for machine to transition to running state and become sshable (TODO maybe make the second half optional)
|
|
110
|
+
self.aws_connect
|
|
111
|
+
|
|
112
|
+
status = self.status()
|
|
113
|
+
|
|
114
|
+
if status.eql?('running')
|
|
115
|
+
self.connect_ssh_tunnel
|
|
116
|
+
self.passthrough[:instance] = self.aws_get_instance
|
|
117
|
+
return self.aws_get_instance
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# TODO provide more context
|
|
121
|
+
@logger.info(sprintf('calling RunInstances ami[%s], size[%s], keypair[%s]',
|
|
122
|
+
self.passthrough[:ami],
|
|
123
|
+
self.passthrough[:size],
|
|
124
|
+
self.passthrough[:keypair]
|
|
125
|
+
))
|
|
126
|
+
|
|
127
|
+
server = @ec2.run_instances(
|
|
128
|
+
self.passthrough[:ami],
|
|
129
|
+
self.passthrough[:min_count],
|
|
130
|
+
self.passthrough[:max_count],
|
|
131
|
+
{
|
|
132
|
+
'InstanceType' => self.passthrough[:size],
|
|
133
|
+
'KeyName' => self.passthrough[:keypair],
|
|
134
|
+
'SecurityGroup' => self.passthrough[:security_groups],
|
|
135
|
+
'UserData' => self.passthrough[:userdata],
|
|
136
|
+
},
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
@instance_data = server.data[:body]['instancesSet'][0]
|
|
140
|
+
|
|
141
|
+
# wait until the machine starts
|
|
142
|
+
ceiling = 9
|
|
143
|
+
sleep_time = 20
|
|
144
|
+
status = nil
|
|
145
|
+
0.upto(ceiling) do |try|
|
|
146
|
+
status = self.aws_status
|
|
147
|
+
|
|
148
|
+
@logger.debug(sprintf('describeInstances[%s]: [%s] [#%s]', self.aws_get_instance, status, try))
|
|
149
|
+
|
|
150
|
+
if status.eql?('running') or status.eql?('16')
|
|
151
|
+
@logger.info(sprintf('[%s] transitioned to state[%s]', self.aws_get_instance, self.aws_status))
|
|
152
|
+
break
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
sleep sleep_time
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
raise sprintf('instance[%s] did not transition to running state, stopped trying at[%s]', self.aws_get_instance, status) unless status.eql?('running') or status.eql?('16')
|
|
159
|
+
|
|
160
|
+
# TODO don't be this hacky
|
|
161
|
+
self.aws_describe_instance # the server.data response doesn't include public hostname/ip
|
|
162
|
+
if @passthrough[:type].eql?(:aws)
|
|
163
|
+
@passthrough[:host] = @instance_data['dnsName']
|
|
164
|
+
else
|
|
165
|
+
@passthrough[:host] = self.find_ssh_elb(true)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
self.connect_ssh_tunnel
|
|
169
|
+
|
|
170
|
+
self.passthrough[:instance] = self.aws_get_instance
|
|
171
|
+
self.passthrough[:instance]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def aws_destroy
|
|
175
|
+
self.aws_connect
|
|
176
|
+
|
|
177
|
+
server = @ec2.terminate_instances(self.aws_get_instance)
|
|
178
|
+
|
|
179
|
+
if self.passthrough[:created_elb] && self.passthrough[:elb_cleanup]
|
|
180
|
+
@logger.info(sprintf('deleting ELB[%s]', self.passthrough[:created_elb]))
|
|
181
|
+
@elb.delete_load_balancer(self.passthrough[:created_elb])
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
self.aws_status
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def aws_describe_instance(instance = aws_get_instance)
|
|
188
|
+
|
|
189
|
+
if @cache_timeout
|
|
190
|
+
if @cache.has_key?(:aws_describe_instance)
|
|
191
|
+
if (Time.now.to_i - @cache[:aws_describe_instance][:time]) < @cache_timeout
|
|
192
|
+
@logger.debug(sprintf('using cached aws_describe_instance?[%s] from [%s]', @cache[:aws_describe_instance][:instance], @cache[:aws_describe_instance][:time]))
|
|
193
|
+
return @cache[:aws_describe_instance][:instance]
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
return nil if instance.nil?
|
|
199
|
+
|
|
200
|
+
self.aws_connect
|
|
201
|
+
server = @ec2.describe_instances('instance-id' => [ instance ])
|
|
202
|
+
response = server.data[:body]['reservationSet'][0]['instancesSet'][0]
|
|
203
|
+
|
|
204
|
+
if instance.eql?(self.aws_get_instance)
|
|
205
|
+
@instance_data = response
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
if @cache_timeout
|
|
209
|
+
@cache[:aws_describe_instance] = Hash.new unless @cache[:aws_describe_instance].class.eql?(Hash)
|
|
210
|
+
@cache[:aws_describe_instance][:time] = Time.now.to_i
|
|
211
|
+
@cache[:aws_describe_instance][:instance] = response
|
|
212
|
+
@logger.debug(sprintf('caching is_available_via_ssh?[%s] at [%s]', @cache[:aws_describe_instance][:instance], @cache[:aws_decribe_instance][:time]))
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
response
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def aws_status
|
|
219
|
+
self.aws_describe_instance
|
|
220
|
+
return 'not-created' if @instance_data.nil?
|
|
221
|
+
@instance_data['instanceState']['name'].nil? ? @instance_data['instanceState']['code'] : @instance_data['instanceState']['name']
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def aws_connect_to_elb (id, elbname, listeners = [{ 'InstancePort' => 22, 'LoadBalancerPort' => 22, 'Protocol' => 'TCP' }])
|
|
225
|
+
self.elb_connect
|
|
226
|
+
|
|
227
|
+
# allow either hash or array of hash specification for listeners
|
|
228
|
+
listeners = [ listeners ] unless listeners.is_a?(Array)
|
|
229
|
+
required_params = [ 'InstancePort', 'LoadBalancerPort', 'Protocol' ] # figure out plan re: InstanceProtocol/LoadbalancerProtocol vs. Protocol
|
|
230
|
+
|
|
231
|
+
listeners.each do |l|
|
|
232
|
+
required_params.each do |r|
|
|
233
|
+
raise sprintf('listener[%s] does not include required parameter[%s]', l, r) unless l[r]
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
@logger.debug(sprintf('confirming ELB name uniqueness[%s]', elbname))
|
|
239
|
+
response = @elb.describe_load_balancers()
|
|
240
|
+
response.body['DescribeLoadBalancersResult']['LoadBalancerDescriptions'].each do |elb|
|
|
241
|
+
if elb['LoadBalancerName'].eql?(elbname)
|
|
242
|
+
# terminate
|
|
243
|
+
@logger.debug(sprintf('terminating ELB[%s]', elbname))
|
|
244
|
+
@elb.delete_load_balancer(elbname)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# create the ELB/VIP
|
|
249
|
+
@logger.debug(sprintf('creating a load balancer[%s] with listeners[%s]', elbname, listeners))
|
|
250
|
+
response = @elb.create_load_balancer(
|
|
251
|
+
[], # availability zones not needed on raiden
|
|
252
|
+
elbname,
|
|
253
|
+
listeners
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
dnsname = response.body['CreateLoadBalancerResult']['DNSName']
|
|
257
|
+
|
|
258
|
+
# string it up to the id passed
|
|
259
|
+
response = @elb.register_instances_with_load_balancer(id, elbname)
|
|
260
|
+
|
|
261
|
+
# i hate this so much.
|
|
262
|
+
@logger.debug(sprintf('sleeping[%s] to allow DNS propagation', self.passthrough[:dns_propagation_sleep]))
|
|
263
|
+
sleep self.passthrough[:dns_propagation_sleep]
|
|
264
|
+
|
|
265
|
+
self.passthrough[:created_elb] = elbname
|
|
266
|
+
|
|
267
|
+
return dnsname
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# TODO this will throw at the first error - should we catch?
|
|
271
|
+
# run some commands, return an array of the output
|
|
272
|
+
def aws_bootstrap (commands)
|
|
273
|
+
self.aws_connect
|
|
274
|
+
commands = (commands.is_a?(Array)) ? commands : [ commands ]
|
|
275
|
+
output = Array.new
|
|
276
|
+
|
|
277
|
+
commands.each do |command|
|
|
278
|
+
output << self.run(command)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
return output
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def aws_connect
|
|
285
|
+
return @ec2 unless @ec2.nil?
|
|
286
|
+
|
|
287
|
+
config = {
|
|
288
|
+
:provider => 'AWS',
|
|
289
|
+
:region => self.passthrough[:region],
|
|
290
|
+
:aws_access_key_id => self.passthrough[:key_id],
|
|
291
|
+
:aws_secret_access_key => self.passthrough[:secret_key],
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
config[:endpoint] = self.passthrough[:ec2_endpoint] unless self.passthrough[:ec2_endpoint].nil?
|
|
295
|
+
@ec2 = Fog::Compute.new(config)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def elb_connect
|
|
299
|
+
return @elb unless @elb.nil?
|
|
300
|
+
|
|
301
|
+
if self.passthrough[:elb_endpoint]
|
|
302
|
+
endpoint = URI.parse(self.passthrough[:elb_endpoint])
|
|
303
|
+
elsif self.passthrough[:ec2_endpoint]
|
|
304
|
+
endpoint = URI.parse(self.passthrough[:ec2_endpoint])
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
config = {
|
|
308
|
+
:region => self.passthrough[:region],
|
|
309
|
+
:aws_access_key_id => self.passthrough[:key_id],
|
|
310
|
+
:aws_secret_access_key => self.passthrough[:secret_key],
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
unless endpoint.nil?
|
|
314
|
+
# if not specifying an endpoint, don't add to the config
|
|
315
|
+
config[:host] = endpoint.host
|
|
316
|
+
config[:path] = endpoint.path
|
|
317
|
+
config[:port] = endpoint.port
|
|
318
|
+
config[:scheme] = endpoint.scheme
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
@elb = Fog::AWS::ELB.new(config)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def find_ssh_elb (create_if_not_found = false, instance = aws_get_instance)
|
|
325
|
+
# given an instance, see if there is already an ELB that it is connected to - and potentially create one
|
|
326
|
+
self.elb_connect
|
|
327
|
+
result = nil
|
|
328
|
+
|
|
329
|
+
response = @elb.describe_load_balancers
|
|
330
|
+
elbs = response.body['DescribeLoadBalancersResult']['LoadBalancerDescriptions']
|
|
331
|
+
|
|
332
|
+
elbs.each do |elb|
|
|
333
|
+
if elb['Instances'].member?(instance)
|
|
334
|
+
result = elb['DNSName']
|
|
335
|
+
break
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
if create_if_not_found and result.nil?
|
|
340
|
+
result = self.aws_connect_to_elb(instance, sprintf('%s-ssh', self.name))
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
result
|
|
344
|
+
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
## plugins/openstack.rb - provide helper functions for Rouster objects running on OpenStack/Compute
|
|
3
|
+
|
|
4
|
+
require sprintf('%s/../%s', File.dirname(File.expand_path(__FILE__)), 'path_helper')
|
|
5
|
+
|
|
6
|
+
require 'fog'
|
|
7
|
+
require 'uri'
|
|
8
|
+
|
|
9
|
+
class Rouster
|
|
10
|
+
|
|
11
|
+
attr_reader :nova # expose OpenStack workers
|
|
12
|
+
attr_reader :instance_data # the result of the runInstances request
|
|
13
|
+
|
|
14
|
+
# return a hash containing meta-data items
|
|
15
|
+
def ostack_get_instance_id ()
|
|
16
|
+
# The instance id is kept in @passthrough[:instance] or
|
|
17
|
+
# can be obtained from @instance_data which has all instance
|
|
18
|
+
# details.
|
|
19
|
+
if ! @instance_data.nil? and ! @instance_data.id.nil?
|
|
20
|
+
return @instance_data.id # we already know the id
|
|
21
|
+
elsif @passthrough.has_key?(:instance)
|
|
22
|
+
return @passthrough[:instance] # we know the id we want
|
|
23
|
+
else
|
|
24
|
+
@logger.debug(sprintf('unable to determine id from instance_data[%s] or passthrough specification[%s]', @instance_data, @passthrough))
|
|
25
|
+
return nil # we don't have an id yet, likely a up() call
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def ostack_up
|
|
30
|
+
# wait for machine to transition to running state and become sshable (TODO maybe make the second half optional)
|
|
31
|
+
self.ostack_connect
|
|
32
|
+
# This will check if instance_id has been provided. If so, it will check on status of the instance.
|
|
33
|
+
status = self.status()
|
|
34
|
+
if status.eql?('running')
|
|
35
|
+
self.passthrough[:instance] = self.ostack_get_instance_id
|
|
36
|
+
@logger.debug(sprintf('Connecting to running instance [%s] while calling ostack_up()', self.passthrough[:instance]))
|
|
37
|
+
self.connect_ssh_tunnel
|
|
38
|
+
else
|
|
39
|
+
server = @nova.servers.create(:name => @name, :flavor_ref => @passthrough[:flavor_ref],
|
|
40
|
+
:image_ref => @passthrough[:image_ref], :key_name => @passthrough[:keypair], :user_data => @passthrough[:user_data])
|
|
41
|
+
server.wait_for { ready? }
|
|
42
|
+
@instance_data = server
|
|
43
|
+
server.addresses.each_key do |address_key|
|
|
44
|
+
if defined?(server.addresses[address_key])
|
|
45
|
+
self.passthrough[:host] = server.addresses[address_key].first['addr']
|
|
46
|
+
break
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
self.passthrough[:instance] = self.ostack_get_instance_id
|
|
50
|
+
@logger.debug(sprintf('Connecting to running instance [%s] while calling ostack_up()', self.passthrough[:instance]))
|
|
51
|
+
self.connect_ssh_tunnel
|
|
52
|
+
end
|
|
53
|
+
self.passthrough[:instance]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def ostack_get_ip()
|
|
57
|
+
self.passthrough[:host]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def ostack_destroy
|
|
61
|
+
server = self.ostack_describe_instance
|
|
62
|
+
raise sprintf("instance[%s] not found by destroy()", self.ostack_get_instance_id) if server.nil?
|
|
63
|
+
server.destroy
|
|
64
|
+
@instance_data = nil
|
|
65
|
+
self.passthrough.delete(:instance)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def ostack_describe_instance(instance_id = ostack_get_instance_id)
|
|
69
|
+
|
|
70
|
+
if @cache_timeout
|
|
71
|
+
if @cache.has_key?(:ostack_describe_instance)
|
|
72
|
+
if (Time.now.to_i - @cache[:ostack_describe_instance][:time]) < @cache_timeout
|
|
73
|
+
@logger.debug(sprintf('using cached ostack_describe_instance?[%s] from [%s]', @cache[:ostack_describe_instance][:instance], @cache[:ostack_describe_instance][:time]))
|
|
74
|
+
return @cache[:ostack_describe_instance][:instance]
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
# We don't have a instance.
|
|
79
|
+
return nil if instance_id.nil?
|
|
80
|
+
self.ostack_connect
|
|
81
|
+
response = @nova.servers.get(instance_id)
|
|
82
|
+
return nil if response.nil?
|
|
83
|
+
@instance_data = response
|
|
84
|
+
|
|
85
|
+
if @cache_timeout
|
|
86
|
+
@cache[:ostack_describe_instance] = Hash.new unless @cache[:ostack_describe_instance].class.eql?(Hash)
|
|
87
|
+
@cache[:ostack_describe_instance][:time] = Time.now.to_i
|
|
88
|
+
@cache[:ostack_describe_instance][:instance] = response
|
|
89
|
+
@logger.debug(sprintf('caching is_available_via_ssh?[%s] at [%s]', @cache[:ostack_describe_instance][:instance], @cache[:ostack_describe_instance][:time]))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
@instance_data
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def ostack_status
|
|
96
|
+
self.ostack_describe_instance
|
|
97
|
+
return 'not-created' if @instance_data.nil?
|
|
98
|
+
if @instance_data.state.eql?('ACTIVE')
|
|
99
|
+
# Make this consistent with AWS response.
|
|
100
|
+
return 'running'
|
|
101
|
+
else
|
|
102
|
+
return @instance_data.state
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# TODO this will throw at the first error - should we catch?
|
|
108
|
+
# run some commands, return an array of the output
|
|
109
|
+
def ostack_bootstrap (commands)
|
|
110
|
+
self.ostack_connect
|
|
111
|
+
commands = (commands.is_a?(Array)) ? commands : [ commands ]
|
|
112
|
+
output = Array.new
|
|
113
|
+
|
|
114
|
+
commands.each do |command|
|
|
115
|
+
output << self.run(command)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
return output
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def ostack_connect
|
|
122
|
+
# Instantiates an Object which can communicate with OS Compute.
|
|
123
|
+
# No instance specific information is set at this time.
|
|
124
|
+
return @nova unless @nova.nil?
|
|
125
|
+
|
|
126
|
+
config = {
|
|
127
|
+
:provider => 'openstack', # OpenStack Fog provider
|
|
128
|
+
:openstack_auth_url => self.passthrough[:openstack_auth_url], # OpenStack Keystone endpoint
|
|
129
|
+
:openstack_username => self.passthrough[:openstack_username], # Your OpenStack Username
|
|
130
|
+
:openstack_tenant => self.passthrough[:openstack_tenant], # Your tenant id
|
|
131
|
+
:openstack_api_key => self.passthrough[:openstack_api_key], # Your OpenStack Password
|
|
132
|
+
:connection_options => self.passthrough[:connection_options] # Optional
|
|
133
|
+
}
|
|
134
|
+
@nova = Fog::Compute.new(config)
|
|
135
|
+
end
|
|
136
|
+
end
|