corl 0.4.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/.document +5 -0
- data/.gitmodules +4 -0
- data/Gemfile +24 -0
- data/Gemfile.lock +123 -0
- data/LICENSE.txt +674 -0
- data/README.rdoc +27 -0
- data/Rakefile +78 -0
- data/VERSION +1 -0
- data/bin/corl +55 -0
- data/corl.gemspec +228 -0
- data/lib/corl/action/add.rb +69 -0
- data/lib/corl/action/bootstrap.rb +83 -0
- data/lib/corl/action/clone.rb +40 -0
- data/lib/corl/action/create.rb +55 -0
- data/lib/corl/action/exec.rb +41 -0
- data/lib/corl/action/extract.rb +49 -0
- data/lib/corl/action/image.rb +30 -0
- data/lib/corl/action/images.rb +55 -0
- data/lib/corl/action/lookup.rb +35 -0
- data/lib/corl/action/machines.rb +51 -0
- data/lib/corl/action/provision.rb +37 -0
- data/lib/corl/action/remove.rb +51 -0
- data/lib/corl/action/save.rb +53 -0
- data/lib/corl/action/seed.rb +115 -0
- data/lib/corl/action/spawn.rb +75 -0
- data/lib/corl/action/start.rb +37 -0
- data/lib/corl/action/stop.rb +30 -0
- data/lib/corl/action/update.rb +37 -0
- data/lib/corl/command/shell.rb +164 -0
- data/lib/corl/configuration/file.rb +386 -0
- data/lib/corl/event/puppet.rb +90 -0
- data/lib/corl/event/regex.rb +52 -0
- data/lib/corl/extension/puppetloader.rb +24 -0
- data/lib/corl/machine/fog.rb +310 -0
- data/lib/corl/machine/physical.rb +161 -0
- data/lib/corl/network/default.rb +26 -0
- data/lib/corl/node/aws.rb +90 -0
- data/lib/corl/node/fog.rb +198 -0
- data/lib/corl/node/google.rb +115 -0
- data/lib/corl/node/local.rb +26 -0
- data/lib/corl/node/rackspace.rb +89 -0
- data/lib/corl/project/git.rb +465 -0
- data/lib/corl/project/github.rb +108 -0
- data/lib/corl/provisioner/puppetnode/resource.rb +245 -0
- data/lib/corl/provisioner/puppetnode/resource_group.rb +205 -0
- data/lib/corl/provisioner/puppetnode.rb +407 -0
- data/lib/corl/template/environment.rb +73 -0
- data/lib/corl/template/json.rb +16 -0
- data/lib/corl/template/wrapper.rb +16 -0
- data/lib/corl/template/yaml.rb +16 -0
- data/lib/corl/translator/json.rb +27 -0
- data/lib/corl/translator/yaml.rb +27 -0
- data/lib/corl.rb +173 -0
- data/lib/corl_core/codes.rb +107 -0
- data/lib/corl_core/config/collection.rb +57 -0
- data/lib/corl_core/config/options.rb +70 -0
- data/lib/corl_core/config.rb +337 -0
- data/lib/corl_core/core.rb +59 -0
- data/lib/corl_core/corl.rb +254 -0
- data/lib/corl_core/errors.rb +84 -0
- data/lib/corl_core/facade.rb +126 -0
- data/lib/corl_core/gems.rb +72 -0
- data/lib/corl_core/manager.rb +425 -0
- data/lib/corl_core/mixin/action/commit.rb +58 -0
- data/lib/corl_core/mixin/action/keypair.rb +105 -0
- data/lib/corl_core/mixin/action/node.rb +129 -0
- data/lib/corl_core/mixin/action/project.rb +53 -0
- data/lib/corl_core/mixin/action/push.rb +52 -0
- data/lib/corl_core/mixin/config/collection.rb +53 -0
- data/lib/corl_core/mixin/config/ops.rb +53 -0
- data/lib/corl_core/mixin/config/options.rb +39 -0
- data/lib/corl_core/mixin/lookup.rb +196 -0
- data/lib/corl_core/mixin/macro/object_interface.rb +361 -0
- data/lib/corl_core/mixin/macro/plugin_interface.rb +380 -0
- data/lib/corl_core/mixin/settings.rb +46 -0
- data/lib/corl_core/mixin/sub_config.rb +148 -0
- data/lib/corl_core/mod/hash.rb +29 -0
- data/lib/corl_core/mod/hiera_backend.rb +63 -0
- data/lib/corl_core/plugin/action.rb +381 -0
- data/lib/corl_core/plugin/base.rb +374 -0
- data/lib/corl_core/plugin/command.rb +98 -0
- data/lib/corl_core/plugin/configuration.rb +177 -0
- data/lib/corl_core/plugin/event.rb +53 -0
- data/lib/corl_core/plugin/extension.rb +12 -0
- data/lib/corl_core/plugin/machine.rb +266 -0
- data/lib/corl_core/plugin/network.rb +359 -0
- data/lib/corl_core/plugin/node.rb +904 -0
- data/lib/corl_core/plugin/project.rb +927 -0
- data/lib/corl_core/plugin/provisioner.rb +51 -0
- data/lib/corl_core/plugin/template.rb +80 -0
- data/lib/corl_core/plugin/translator.rb +38 -0
- data/lib/corl_core/util/cli.rb +352 -0
- data/lib/corl_core/util/data.rb +404 -0
- data/lib/corl_core/util/disk.rb +114 -0
- data/lib/corl_core/util/git.rb +47 -0
- data/lib/corl_core/util/interface.rb +319 -0
- data/lib/corl_core/util/liquid.rb +17 -0
- data/lib/corl_core/util/package.rb +93 -0
- data/lib/corl_core/util/shell.rb +239 -0
- data/lib/corl_core/util/ssh.rb +286 -0
- data/lib/facter/corl_config_ready.rb +13 -0
- data/lib/facter/corl_exists.rb +15 -0
- data/lib/facter/corl_network.rb +17 -0
- data/lib/hiera/corl_logger.rb +18 -0
- data/lib/puppet/indirector/corl.rb +27 -0
- data/lib/puppet/indirector/data_binding/corl.rb +6 -0
- data/lib/puppet/parser/functions/config_initialized.rb +26 -0
- data/lib/puppet/parser/functions/corl_include.rb +44 -0
- data/lib/puppet/parser/functions/corl_resources.rb +58 -0
- data/lib/puppet/parser/functions/deep_merge.rb +21 -0
- data/lib/puppet/parser/functions/ensure.rb +29 -0
- data/lib/puppet/parser/functions/file_exists.rb +19 -0
- data/lib/puppet/parser/functions/global_array.rb +35 -0
- data/lib/puppet/parser/functions/global_hash.rb +35 -0
- data/lib/puppet/parser/functions/global_options.rb +23 -0
- data/lib/puppet/parser/functions/global_param.rb +43 -0
- data/lib/puppet/parser/functions/interpolate.rb +26 -0
- data/lib/puppet/parser/functions/is_false.rb +21 -0
- data/lib/puppet/parser/functions/is_true.rb +21 -0
- data/lib/puppet/parser/functions/module_array.rb +38 -0
- data/lib/puppet/parser/functions/module_hash.rb +38 -0
- data/lib/puppet/parser/functions/module_options.rb +23 -0
- data/lib/puppet/parser/functions/module_param.rb +48 -0
- data/lib/puppet/parser/functions/name.rb +21 -0
- data/lib/puppet/parser/functions/render.rb +33 -0
- data/lib/puppet/parser/functions/value.rb +21 -0
- data/locales/en.yml +232 -0
- data/spec/corl_core/interface_spec.rb +489 -0
- data/spec/corl_mock_input.rb +29 -0
- data/spec/corl_test_kernel.rb +22 -0
- data/spec/spec_helper.rb +15 -0
- metadata +406 -0
@@ -0,0 +1,904 @@
|
|
1
|
+
|
2
|
+
module CORL
|
3
|
+
module Plugin
|
4
|
+
class Node < Base
|
5
|
+
|
6
|
+
include Celluloid
|
7
|
+
|
8
|
+
#-----------------------------------------------------------------------------
|
9
|
+
# Node plugin interface
|
10
|
+
|
11
|
+
def normalize
|
12
|
+
super
|
13
|
+
|
14
|
+
ui.resource = hostname
|
15
|
+
ui.logger = hostname
|
16
|
+
|
17
|
+
@cli_interface = Util::Liquid.new do |method, args, &code|
|
18
|
+
result = exec({ :commands => [ [ method, args ].flatten.join(' ') ] }) do |op, data|
|
19
|
+
code.call(op, data) if code
|
20
|
+
end
|
21
|
+
result = result.first
|
22
|
+
|
23
|
+
alert(result.errors) unless result.errors.empty?
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
@action_interface = Util::Liquid.new do |method, args, &code|
|
28
|
+
action(method, *args) do |op, data|
|
29
|
+
code.call(op, data) if code
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#---
|
35
|
+
|
36
|
+
def method_missing(method, *args, &code)
|
37
|
+
action(method, *args) do |op, data|
|
38
|
+
code.call(op, data) if code
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#---
|
43
|
+
|
44
|
+
def localize
|
45
|
+
@local_context = true
|
46
|
+
myself.local_machine = create_machine(:local_machine, :physical)
|
47
|
+
end
|
48
|
+
|
49
|
+
#-----------------------------------------------------------------------------
|
50
|
+
# Checks
|
51
|
+
|
52
|
+
def local?
|
53
|
+
@local_context ? true : false
|
54
|
+
end
|
55
|
+
|
56
|
+
#---
|
57
|
+
|
58
|
+
def usable_image?(image)
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
#-----------------------------------------------------------------------------
|
63
|
+
# Property accessors / modifiers
|
64
|
+
|
65
|
+
def network
|
66
|
+
return plugin_parent
|
67
|
+
end
|
68
|
+
|
69
|
+
def network=network
|
70
|
+
myself.plugin_parent = network
|
71
|
+
end
|
72
|
+
|
73
|
+
#---
|
74
|
+
|
75
|
+
def setting(property, default = nil, format = false)
|
76
|
+
return network.node_setting(plugin_provider, plugin_name, property, default, format)
|
77
|
+
end
|
78
|
+
|
79
|
+
def search(property, default = nil, format = false)
|
80
|
+
return network.search_node(plugin_provider, plugin_name, property, default, format)
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_setting(property, value = nil)
|
84
|
+
network.set_node_setting(plugin_provider, plugin_name, property, value)
|
85
|
+
end
|
86
|
+
|
87
|
+
def delete_setting(property)
|
88
|
+
network.delete_node_setting(plugin_provider, plugin_name, property)
|
89
|
+
end
|
90
|
+
|
91
|
+
#---
|
92
|
+
|
93
|
+
def [](name, default = nil, format = false)
|
94
|
+
search(name, default, format)
|
95
|
+
end
|
96
|
+
|
97
|
+
#---
|
98
|
+
|
99
|
+
def []=(name, value)
|
100
|
+
set_setting(name, value)
|
101
|
+
end
|
102
|
+
|
103
|
+
#-----------------------------------------------------------------------------
|
104
|
+
|
105
|
+
def groups
|
106
|
+
array(self[:groups])
|
107
|
+
end
|
108
|
+
|
109
|
+
#-----------------------------------------------------------------------------
|
110
|
+
|
111
|
+
def machine
|
112
|
+
@machine
|
113
|
+
end
|
114
|
+
|
115
|
+
def machine=machine
|
116
|
+
@machine = machine
|
117
|
+
end
|
118
|
+
|
119
|
+
#---
|
120
|
+
|
121
|
+
def local_machine
|
122
|
+
@local_machine
|
123
|
+
end
|
124
|
+
|
125
|
+
def local_machine=local_machine
|
126
|
+
@local_machine = local_machine
|
127
|
+
end
|
128
|
+
|
129
|
+
#---
|
130
|
+
|
131
|
+
def id(reset = false)
|
132
|
+
myself[:id] = machine.plugin_name if reset || myself[:id].nil?
|
133
|
+
myself[:id]
|
134
|
+
end
|
135
|
+
|
136
|
+
#---
|
137
|
+
|
138
|
+
def public_ip(reset = false)
|
139
|
+
myself[:public_ip] = machine.public_ip if reset || myself[:public_ip].nil?
|
140
|
+
myself[:public_ip]
|
141
|
+
end
|
142
|
+
|
143
|
+
def private_ip(reset = false)
|
144
|
+
myself[:private_ip] = machine.private_ip if reset || myself[:private_ip].nil?
|
145
|
+
myself[:private_ip]
|
146
|
+
end
|
147
|
+
|
148
|
+
#---
|
149
|
+
|
150
|
+
def hostname(reset = false)
|
151
|
+
myself[:hostname] = machine.hostname if reset || myself[:hostname].nil?
|
152
|
+
myself[:hostname]
|
153
|
+
end
|
154
|
+
|
155
|
+
#---
|
156
|
+
|
157
|
+
def state(reset = false)
|
158
|
+
myself[:state] = machine.state if reset || myself[:state].nil?
|
159
|
+
myself[:state]
|
160
|
+
end
|
161
|
+
|
162
|
+
#---
|
163
|
+
|
164
|
+
def user=user
|
165
|
+
myself[:user] = user
|
166
|
+
end
|
167
|
+
|
168
|
+
def user
|
169
|
+
myself[:user]
|
170
|
+
end
|
171
|
+
|
172
|
+
#---
|
173
|
+
|
174
|
+
def ssh_port=ssh_port
|
175
|
+
myself[:ssh_port] = ssh_port
|
176
|
+
end
|
177
|
+
|
178
|
+
def ssh_port
|
179
|
+
myself[:ssh_port] = 22 if myself[:ssh_port].nil?
|
180
|
+
myself[:ssh_port]
|
181
|
+
end
|
182
|
+
|
183
|
+
#---
|
184
|
+
|
185
|
+
def home(env_var = 'HOME', reset = false)
|
186
|
+
if reset || myself[:user_home].nil?
|
187
|
+
myself[:user_home] = cli_capture(:echo, '$' + env_var.to_s.gsub('$', '')) if machine
|
188
|
+
end
|
189
|
+
myself[:user_home]
|
190
|
+
end
|
191
|
+
|
192
|
+
#---
|
193
|
+
|
194
|
+
def private_key=private_key
|
195
|
+
myself[:private_key] = private_key
|
196
|
+
end
|
197
|
+
|
198
|
+
def private_key
|
199
|
+
config_key = myself[:private_key]
|
200
|
+
return File.expand_path(config_key) if config_key
|
201
|
+
nil
|
202
|
+
end
|
203
|
+
|
204
|
+
#---
|
205
|
+
|
206
|
+
def public_key=public_key
|
207
|
+
myself[:public_key] = public_key
|
208
|
+
end
|
209
|
+
|
210
|
+
def public_key
|
211
|
+
config_key = myself[:private_key]
|
212
|
+
return File.expand_path(config_key) if config_key
|
213
|
+
end
|
214
|
+
|
215
|
+
#---
|
216
|
+
|
217
|
+
def machine_types # Must be set at machine level (queried)
|
218
|
+
machine.machine_types if machine
|
219
|
+
end
|
220
|
+
|
221
|
+
def machine_type=machine_type
|
222
|
+
myself[:machine_type] = machine_type
|
223
|
+
end
|
224
|
+
|
225
|
+
def machine_type(reset = false)
|
226
|
+
myself[:machine_type] = machine.machine_type if reset || myself[:machine_type].nil?
|
227
|
+
machine_type = myself[:machine_type]
|
228
|
+
|
229
|
+
if machine_type.nil? && machine
|
230
|
+
if types = machine_types
|
231
|
+
unless types.empty?
|
232
|
+
machine_type = machine_type_id(types.first)
|
233
|
+
myself.machine_type = machine_type
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
machine_type
|
239
|
+
end
|
240
|
+
|
241
|
+
#---
|
242
|
+
|
243
|
+
def images(search_terms = [], options = {})
|
244
|
+
config = Config.ensure(options)
|
245
|
+
images = []
|
246
|
+
|
247
|
+
if machine
|
248
|
+
loaded_images = machine.images
|
249
|
+
|
250
|
+
if loaded_images
|
251
|
+
require_all = config.get(:require_all, false)
|
252
|
+
|
253
|
+
loaded_images.each do |image|
|
254
|
+
if usable_image?(image)
|
255
|
+
include_image = ( search_terms.empty? ? true : require_all )
|
256
|
+
image_text = image_search_text(image)
|
257
|
+
|
258
|
+
search_terms.each do |term|
|
259
|
+
if config.get(:match_case, false)
|
260
|
+
success = image_text.match(/#{term}/)
|
261
|
+
else
|
262
|
+
success = image_text.match(/#{term}/i)
|
263
|
+
end
|
264
|
+
|
265
|
+
if require_all
|
266
|
+
include_image = false unless success
|
267
|
+
else
|
268
|
+
include_image = true if success
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
images << image if include_image
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
images
|
278
|
+
end
|
279
|
+
|
280
|
+
#---
|
281
|
+
|
282
|
+
def image=image
|
283
|
+
myself[:image] = image
|
284
|
+
end
|
285
|
+
|
286
|
+
def image(reset = false)
|
287
|
+
myself[:image] = machine.image if reset || myself[:image].nil?
|
288
|
+
myself[:image]
|
289
|
+
end
|
290
|
+
|
291
|
+
#-----------------------------------------------------------------------------
|
292
|
+
# Settings groups
|
293
|
+
|
294
|
+
def machine_config
|
295
|
+
name = myself[:id]
|
296
|
+
name = myself[:hostname] if name.nil?
|
297
|
+
config = Config.new({ :name => name })
|
298
|
+
|
299
|
+
yield(config) if block_given?
|
300
|
+
config
|
301
|
+
end
|
302
|
+
|
303
|
+
#-----------------------------------------------------------------------------
|
304
|
+
# Machine operations
|
305
|
+
|
306
|
+
def create_machine(name, provider, options = {})
|
307
|
+
CORL.plugin_load(:machine, provider, extended_config(name, options).import({ :meta => { :parent => myself }}))
|
308
|
+
end
|
309
|
+
|
310
|
+
#---
|
311
|
+
|
312
|
+
def create(options = {})
|
313
|
+
success = true
|
314
|
+
|
315
|
+
if machine
|
316
|
+
config = Config.ensure(options)
|
317
|
+
|
318
|
+
if extension_check(:create, { :config => config })
|
319
|
+
logger.info("Creating node: #{plugin_name}")
|
320
|
+
|
321
|
+
yield(:config, config) if block_given?
|
322
|
+
success = machine.create(config.export)
|
323
|
+
|
324
|
+
if success && block_given?
|
325
|
+
process_success = yield(:process, config)
|
326
|
+
success = process_success if process_success == false
|
327
|
+
end
|
328
|
+
|
329
|
+
if success
|
330
|
+
extension(:create_success, { :config => config })
|
331
|
+
end
|
332
|
+
end
|
333
|
+
else
|
334
|
+
logger.warn("Node #{plugin_name} does not have an attached machine so cannot be created")
|
335
|
+
end
|
336
|
+
success
|
337
|
+
end
|
338
|
+
|
339
|
+
#---
|
340
|
+
|
341
|
+
def download(remote_path, local_path, options = {})
|
342
|
+
success = false
|
343
|
+
|
344
|
+
if machine && machine.running?
|
345
|
+
config = Config.ensure(options)
|
346
|
+
hook_config = Config.new({ :local_path => local_path, :remote_path => remote_path, :config => config })
|
347
|
+
|
348
|
+
if extension_check(:download, hook_config)
|
349
|
+
logger.info("Downloading from #{plugin_name}")
|
350
|
+
|
351
|
+
render("Starting download of #{remote_path} to #{local_path}")
|
352
|
+
yield(:config, hook_config) if block_given?
|
353
|
+
|
354
|
+
active_machine = local? ? local_machine : machine
|
355
|
+
|
356
|
+
success = active_machine.download(remote_path, local_path, config.export) do |name, received, total|
|
357
|
+
render("#{name}: Sent #{received} of #{total}")
|
358
|
+
yield(:progress, { :name => name, :received => received, :total => total })
|
359
|
+
end
|
360
|
+
|
361
|
+
if success && block_given?
|
362
|
+
render("Successfully finished download of #{remote_path} to #{local_path}")
|
363
|
+
process_success = yield(:process, hook_config)
|
364
|
+
success = process_success if process_success == false
|
365
|
+
end
|
366
|
+
|
367
|
+
if success
|
368
|
+
extension(:download_success, hook_config)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
else
|
372
|
+
logger.warn("Node #{plugin_name} does not have an attached machine or is not running so cannot download")
|
373
|
+
end
|
374
|
+
success
|
375
|
+
end
|
376
|
+
|
377
|
+
#---
|
378
|
+
|
379
|
+
def upload(local_path, remote_path, options = {})
|
380
|
+
success = false
|
381
|
+
|
382
|
+
if machine && machine.running?
|
383
|
+
config = Config.ensure(options)
|
384
|
+
hook_config = Config.new({ :local_path => local_path, :remote_path => remote_path, :config => config })
|
385
|
+
|
386
|
+
if extension_check(:upload, hook_config)
|
387
|
+
logger.info("Uploading to #{plugin_name}")
|
388
|
+
|
389
|
+
render("Starting upload of #{local_path} to #{remote_path}")
|
390
|
+
yield(:config, hook_config) if block_given?
|
391
|
+
|
392
|
+
active_machine = local? ? local_machine : machine
|
393
|
+
|
394
|
+
success = active_machine.upload(local_path, remote_path, config.export) do |name, sent, total|
|
395
|
+
render("#{name}: Sent #{sent} of #{total}")
|
396
|
+
yield(:progress, { :name => name, :sent => sent, :total => total })
|
397
|
+
end
|
398
|
+
|
399
|
+
if success && block_given?
|
400
|
+
render("Successfully finished upload of #{local_path} to #{remote_path}")
|
401
|
+
process_success = yield(:process, hook_config)
|
402
|
+
success = process_success if process_success == false
|
403
|
+
end
|
404
|
+
|
405
|
+
if success
|
406
|
+
extension(:upload_success, hook_config)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
else
|
410
|
+
logger.warn("Node #{plugin_name} does not have an attached machine or is not running so cannot upload")
|
411
|
+
end
|
412
|
+
success
|
413
|
+
end
|
414
|
+
|
415
|
+
#---
|
416
|
+
|
417
|
+
def send_files(local_path, remote_path, files = nil, permission = '0644', &code)
|
418
|
+
local_path = File.expand_path(local_path)
|
419
|
+
return false unless File.directory?(local_path)
|
420
|
+
|
421
|
+
success = true
|
422
|
+
|
423
|
+
send_file = lambda do |local_file, remote_file|
|
424
|
+
send_success = upload(local_file, remote_file) do |op, options|
|
425
|
+
code.call(op, options) if code
|
426
|
+
end
|
427
|
+
send_success = cli_check(:chmod, permission, remote_file) if send_success
|
428
|
+
send_success
|
429
|
+
end
|
430
|
+
|
431
|
+
if files && files.is_a?(Array)
|
432
|
+
files.flatten.each do |rel_file_name|
|
433
|
+
local_file = "#{local_path}/#{rel_file_name}"
|
434
|
+
remote_file = "#{remote_path}/#{rel_file_name}"
|
435
|
+
|
436
|
+
if File.exists?(local_file)
|
437
|
+
send_success = send_file.call(local_file, remote_file)
|
438
|
+
success = false unless send_success
|
439
|
+
end
|
440
|
+
end
|
441
|
+
else
|
442
|
+
send_success = send_file.call(local_path, remote_path)
|
443
|
+
success = false unless send_success
|
444
|
+
end
|
445
|
+
success
|
446
|
+
end
|
447
|
+
|
448
|
+
#---
|
449
|
+
|
450
|
+
def exec(options = {})
|
451
|
+
results = nil
|
452
|
+
|
453
|
+
if machine && machine.running?
|
454
|
+
config = Config.ensure(options)
|
455
|
+
|
456
|
+
if extension_check(:exec, { :config => config })
|
457
|
+
logger.info("Executing node: #{plugin_name}")
|
458
|
+
|
459
|
+
yield(:config, config) if block_given?
|
460
|
+
|
461
|
+
active_machine = local? ? local_machine : machine
|
462
|
+
|
463
|
+
if commands = config.get(:commands, nil)
|
464
|
+
logger.info("Starting command execution: #{commands.join('; ')}")
|
465
|
+
results = active_machine.exec(commands, config.export) do |type, command, data|
|
466
|
+
if type == :error
|
467
|
+
alert(data)
|
468
|
+
else
|
469
|
+
render(data)
|
470
|
+
end
|
471
|
+
yield(:progress, { :type => type, :command => command, :data => data })
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
success = true
|
476
|
+
results.each do |result|
|
477
|
+
success = false if result.status != CORL.code.success
|
478
|
+
end
|
479
|
+
if success
|
480
|
+
render("Successfully finished execution")
|
481
|
+
yield(:process, config) if block_given?
|
482
|
+
extension(:exec_success, { :config => config, :results => results })
|
483
|
+
end
|
484
|
+
end
|
485
|
+
else
|
486
|
+
logger.warn("Node #{plugin_name} does not have an attached machine or is not running so cannot execute commands")
|
487
|
+
end
|
488
|
+
results
|
489
|
+
end
|
490
|
+
|
491
|
+
#---
|
492
|
+
|
493
|
+
def cli
|
494
|
+
@cli_interface
|
495
|
+
end
|
496
|
+
|
497
|
+
#---
|
498
|
+
|
499
|
+
def command(command, options = {})
|
500
|
+
unless command.is_a?(CORL::Plugin::Command)
|
501
|
+
command = CORL.command(Config.new({ :command => command }).import(options), :shell)
|
502
|
+
end
|
503
|
+
results = exec({ :commands => [ command.to_s ] }) do |op, data|
|
504
|
+
yield(op, data) if block_given?
|
505
|
+
end
|
506
|
+
results.first
|
507
|
+
end
|
508
|
+
|
509
|
+
#---
|
510
|
+
|
511
|
+
def action(provider, options = {})
|
512
|
+
config = Config.ensure(options).defaults({ :net_provider => network.plugin_provider })
|
513
|
+
encoded_config = Util::CLI.encode(config.export)
|
514
|
+
|
515
|
+
logger.info("Executing remote action #{provider} with encoded arguments: #{config.export.inspect}")
|
516
|
+
|
517
|
+
action_config = extended_config(:action, {
|
518
|
+
:command => provider,
|
519
|
+
:data => { :encoded => encoded_config }
|
520
|
+
})
|
521
|
+
command(:corl, { :subcommand => action_config }) do |op, data|
|
522
|
+
yield(op, data) if block_given?
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
#---
|
527
|
+
|
528
|
+
def run
|
529
|
+
@action_interface
|
530
|
+
end
|
531
|
+
|
532
|
+
#---
|
533
|
+
|
534
|
+
def bootstrap(local_path, options = {})
|
535
|
+
config = Config.ensure(options)
|
536
|
+
myself.status = code.unknown_status
|
537
|
+
|
538
|
+
bootstrap_name = 'bootstrap'
|
539
|
+
bootstrap_path = config.get(:bootstrap_path, File.join(Gems.core.full_gem_path, bootstrap_name))
|
540
|
+
bootstrap_glob = config.get(:bootstrap_glob, '**/*.sh')
|
541
|
+
bootstrap_init = config.get(:bootstrap_init, 'bootstrap.sh')
|
542
|
+
|
543
|
+
user_home = config[:home]
|
544
|
+
auth_files = config.get_array(:auth_files)
|
545
|
+
|
546
|
+
codes :local_path_not_found,
|
547
|
+
:home_path_lookup_failure,
|
548
|
+
:auth_upload_failure,
|
549
|
+
:bootstrap_upload_failure,
|
550
|
+
:bootstrap_exec_failure
|
551
|
+
|
552
|
+
if File.directory?(local_path)
|
553
|
+
if user_home || user_home = home(config.get(:home_env_var, 'HOME'), config.get(:force, false))
|
554
|
+
myself.status = code.success
|
555
|
+
|
556
|
+
# Transmit authorisation / credential files
|
557
|
+
package_files = [ '.fog', '.netrc', '.google-privatekey.p12' ]
|
558
|
+
auth_files.each do |file|
|
559
|
+
package_files = file.gsub(local_path + '/', '')
|
560
|
+
end
|
561
|
+
send_success = send_files(local_path, user_home, package_files, '0600') do |op, data|
|
562
|
+
yield("send_#{op}".to_sym, data) if block_given?
|
563
|
+
data
|
564
|
+
end
|
565
|
+
unless send_success
|
566
|
+
myself.status = code.auth_upload_failure
|
567
|
+
end
|
568
|
+
|
569
|
+
# Send bootstrap package
|
570
|
+
if status == code.success
|
571
|
+
remote_bootstrap_path = File.join(user_home, bootstrap_name)
|
572
|
+
|
573
|
+
cli.rm('-Rf', remote_bootstrap_path)
|
574
|
+
send_success = send_files(bootstrap_path, remote_bootstrap_path, nil, '0700') do |op, data|
|
575
|
+
yield("send_#{op}".to_sym, data) if block_given?
|
576
|
+
data
|
577
|
+
end
|
578
|
+
unless send_success
|
579
|
+
myself.status = code.bootstrap_upload_failure
|
580
|
+
end
|
581
|
+
|
582
|
+
# Execute bootstrap process
|
583
|
+
if status == code.success
|
584
|
+
remote_script = File.join(remote_bootstrap_path, bootstrap_init)
|
585
|
+
result = command(remote_script) do |op, data|
|
586
|
+
yield("exec_#{op}".to_sym, data) if block_given?
|
587
|
+
data
|
588
|
+
end
|
589
|
+
|
590
|
+
if result.status != code.success
|
591
|
+
myself.status = code.bootstrap_exec_failure
|
592
|
+
end
|
593
|
+
end
|
594
|
+
end
|
595
|
+
else
|
596
|
+
myself.status = code.home_path_lookup_failure
|
597
|
+
end
|
598
|
+
else
|
599
|
+
myself.status = code.local_path_not_found
|
600
|
+
end
|
601
|
+
status == code.success
|
602
|
+
end
|
603
|
+
|
604
|
+
#---
|
605
|
+
|
606
|
+
def save(options = {})
|
607
|
+
config = Config.ensure(options)
|
608
|
+
|
609
|
+
# Record machine parameters
|
610
|
+
id(true)
|
611
|
+
public_ip(true)
|
612
|
+
private_ip(true)
|
613
|
+
hostname(true)
|
614
|
+
state(true)
|
615
|
+
machine_type(true)
|
616
|
+
image(true)
|
617
|
+
|
618
|
+
# Provider or external configuration preparation
|
619
|
+
yield(config) if block_given?
|
620
|
+
|
621
|
+
network.save(config.import({
|
622
|
+
:commit => true,
|
623
|
+
:allow_empty => true,
|
624
|
+
:message => config.get(:message, "Saving #{plugin_provider} node #{plugin_name}"),
|
625
|
+
:remote => config.get(:remote, :edit)
|
626
|
+
}))
|
627
|
+
end
|
628
|
+
|
629
|
+
#---
|
630
|
+
|
631
|
+
def start(options = {})
|
632
|
+
success = true
|
633
|
+
|
634
|
+
if machine
|
635
|
+
config = Config.ensure(options)
|
636
|
+
|
637
|
+
if extension_check(:start, { :config => config })
|
638
|
+
logger.info("Starting node: #{plugin_name}")
|
639
|
+
|
640
|
+
yield(:config, config) if block_given?
|
641
|
+
success = machine.start(config.export)
|
642
|
+
|
643
|
+
if success && block_given?
|
644
|
+
process_success = yield(:process, config)
|
645
|
+
success = process_success if process_success == false
|
646
|
+
end
|
647
|
+
|
648
|
+
if success
|
649
|
+
extension(:start_success, { :config => config })
|
650
|
+
end
|
651
|
+
end
|
652
|
+
else
|
653
|
+
logger.warn("Node #{plugin_name} does not have an attached machine so cannot be started")
|
654
|
+
end
|
655
|
+
success
|
656
|
+
end
|
657
|
+
|
658
|
+
#---
|
659
|
+
|
660
|
+
def reload(options = {})
|
661
|
+
success = true
|
662
|
+
|
663
|
+
if machine && machine.created?
|
664
|
+
config = Config.ensure(options)
|
665
|
+
|
666
|
+
if extension_check(:reload, { :config => config })
|
667
|
+
logger.info("Reloading node: #{plugin_name}")
|
668
|
+
|
669
|
+
yield(:config, config) if block_given?
|
670
|
+
success = machine.reload(config.export)
|
671
|
+
|
672
|
+
if success && block_given?
|
673
|
+
process_success = yield(:process, config)
|
674
|
+
success = process_success if process_success == false
|
675
|
+
end
|
676
|
+
|
677
|
+
if success
|
678
|
+
extension(:reload_success, { :config => config })
|
679
|
+
end
|
680
|
+
end
|
681
|
+
else
|
682
|
+
logger.warn("Node #{plugin_name} does not have an attached machine or is not created so cannot be reloaded")
|
683
|
+
end
|
684
|
+
success
|
685
|
+
end
|
686
|
+
|
687
|
+
#---
|
688
|
+
|
689
|
+
def create_image(options = {})
|
690
|
+
success = true
|
691
|
+
|
692
|
+
if machine && machine.running?
|
693
|
+
config = Config.ensure(options)
|
694
|
+
|
695
|
+
if extension_check(:create_image, { :config => config })
|
696
|
+
logger.info("Executing node: #{plugin_name}")
|
697
|
+
|
698
|
+
yield(:config, config) if block_given?
|
699
|
+
success = machine.create_image(config.export)
|
700
|
+
|
701
|
+
if success && block_given?
|
702
|
+
process_success = yield(:process, config)
|
703
|
+
success = process_success if process_success == false
|
704
|
+
end
|
705
|
+
|
706
|
+
if success
|
707
|
+
extension(:create_image_success, { :config => config })
|
708
|
+
end
|
709
|
+
end
|
710
|
+
else
|
711
|
+
logger.warn("Node #{plugin_name} does not have an attached machine or is not running so cannot create an image")
|
712
|
+
end
|
713
|
+
success
|
714
|
+
end
|
715
|
+
|
716
|
+
#---
|
717
|
+
|
718
|
+
def stop(options = {})
|
719
|
+
success = true
|
720
|
+
|
721
|
+
if machine && machine.running?
|
722
|
+
config = Config.ensure(options)
|
723
|
+
|
724
|
+
if extension_check(:stop, { :config => config })
|
725
|
+
logger.info("Stopping node: #{plugin_name}")
|
726
|
+
|
727
|
+
yield(:config, config) if block_given?
|
728
|
+
success = machine.stop(config.export)
|
729
|
+
|
730
|
+
if success && block_given?
|
731
|
+
process_success = yield(:process, config)
|
732
|
+
success = process_success if process_success == false
|
733
|
+
end
|
734
|
+
|
735
|
+
if success
|
736
|
+
extension(:stop_success, { :config => config })
|
737
|
+
end
|
738
|
+
end
|
739
|
+
else
|
740
|
+
logger.warn("Node #{plugin_name} does not have an attached machine or is not running so cannot be stopped")
|
741
|
+
end
|
742
|
+
success
|
743
|
+
end
|
744
|
+
|
745
|
+
#---
|
746
|
+
|
747
|
+
def destroy(options = {})
|
748
|
+
success = true
|
749
|
+
|
750
|
+
if machine && machine.created?
|
751
|
+
config = Config.ensure(options)
|
752
|
+
|
753
|
+
run = false
|
754
|
+
|
755
|
+
if config[:force]
|
756
|
+
run = true
|
757
|
+
else
|
758
|
+
choice = nil
|
759
|
+
begin
|
760
|
+
choice = ui.ask("Are you sure you want to permanently destroy (Y|N): #{plugin_name}?")
|
761
|
+
run = choice.upcase == "Y"
|
762
|
+
|
763
|
+
rescue Errors::UIExpectsTTY
|
764
|
+
run = false
|
765
|
+
end
|
766
|
+
end
|
767
|
+
|
768
|
+
if run
|
769
|
+
if extension_check(:destroy, { :config => config })
|
770
|
+
logger.info("Destroying node: #{plugin_name}")
|
771
|
+
|
772
|
+
yield(:config, config) if block_given?
|
773
|
+
success = machine.destroy(config.export)
|
774
|
+
|
775
|
+
if success && block_given?
|
776
|
+
process_success = yield(:process, config)
|
777
|
+
success = process_success if process_success == false
|
778
|
+
end
|
779
|
+
|
780
|
+
if success
|
781
|
+
extension(:destroy_success, { :config => config })
|
782
|
+
end
|
783
|
+
end
|
784
|
+
else
|
785
|
+
logger.warn("Node #{plugin_name} does not have an attached machine or is not created so cannot be destroyed")
|
786
|
+
end
|
787
|
+
else
|
788
|
+
logger.info("Node #{plugin_name} not destroyed due to user cancellation")
|
789
|
+
end
|
790
|
+
success
|
791
|
+
end
|
792
|
+
|
793
|
+
#-----------------------------------------------------------------------------
|
794
|
+
# Utilities
|
795
|
+
|
796
|
+
def self.build_info(type, data)
|
797
|
+
data = data.split(/\s*,\s*/) if data.is_a?(String)
|
798
|
+
super(type, data)
|
799
|
+
end
|
800
|
+
|
801
|
+
#---
|
802
|
+
|
803
|
+
def self.translate(data)
|
804
|
+
options = super(data)
|
805
|
+
|
806
|
+
case data
|
807
|
+
when String
|
808
|
+
options = { :name => data }
|
809
|
+
when Hash
|
810
|
+
options = data
|
811
|
+
end
|
812
|
+
|
813
|
+
if options.has_key?(:name)
|
814
|
+
if matches = translate_reference(options[:name])
|
815
|
+
options[:provider] = matches[:provider]
|
816
|
+
options[:name] = matches[:name]
|
817
|
+
|
818
|
+
logger.debug("Translating node options: #{options.inspect}")
|
819
|
+
end
|
820
|
+
end
|
821
|
+
options
|
822
|
+
end
|
823
|
+
|
824
|
+
#---
|
825
|
+
|
826
|
+
def self.translate_reference(reference)
|
827
|
+
# ex: rackspace:::web1.staging.example.com
|
828
|
+
if reference && reference.match(/^\s*([a-zA-Z0-9_-]+):::([^\s]+)\s*$/)
|
829
|
+
provider = $1
|
830
|
+
name = $2
|
831
|
+
|
832
|
+
logger.debug("Translating node reference: #{provider} #{name}")
|
833
|
+
|
834
|
+
info = {
|
835
|
+
:provider => provider,
|
836
|
+
:name => name
|
837
|
+
}
|
838
|
+
|
839
|
+
logger.debug("Project reference info: #{info.inspect}")
|
840
|
+
return info
|
841
|
+
end
|
842
|
+
nil
|
843
|
+
end
|
844
|
+
|
845
|
+
#---
|
846
|
+
|
847
|
+
def translate_reference(reference)
|
848
|
+
myself.class.translate_reference(reference)
|
849
|
+
end
|
850
|
+
|
851
|
+
#-----------------------------------------------------------------------------
|
852
|
+
# CLI utilities
|
853
|
+
|
854
|
+
def cli_capture(cli_command, *args)
|
855
|
+
result = cli.send(cli_command, args)
|
856
|
+
|
857
|
+
if result.status == code.success && ! result.output.empty?
|
858
|
+
result.output
|
859
|
+
else
|
860
|
+
nil
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
#---
|
865
|
+
|
866
|
+
def cli_check(cli_command, *args)
|
867
|
+
result = cli.send(cli_command, args)
|
868
|
+
result.status == code.success ? true : false
|
869
|
+
end
|
870
|
+
|
871
|
+
#-----------------------------------------------------------------------------
|
872
|
+
# Machine type utilities
|
873
|
+
|
874
|
+
def machine_type_id(machine_type)
|
875
|
+
machine_type.id
|
876
|
+
end
|
877
|
+
|
878
|
+
#---
|
879
|
+
|
880
|
+
def render_machine_type(machine_type)
|
881
|
+
''
|
882
|
+
end
|
883
|
+
|
884
|
+
#-----------------------------------------------------------------------------
|
885
|
+
# Image utilities
|
886
|
+
|
887
|
+
def image_id(image)
|
888
|
+
image.id
|
889
|
+
end
|
890
|
+
|
891
|
+
#---
|
892
|
+
|
893
|
+
def render_image(image)
|
894
|
+
''
|
895
|
+
end
|
896
|
+
|
897
|
+
#---
|
898
|
+
|
899
|
+
def image_search_text(image)
|
900
|
+
image.to_s
|
901
|
+
end
|
902
|
+
end
|
903
|
+
end
|
904
|
+
end
|