corl 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. data/.document +5 -0
  2. data/.gitmodules +4 -0
  3. data/Gemfile +24 -0
  4. data/Gemfile.lock +123 -0
  5. data/LICENSE.txt +674 -0
  6. data/README.rdoc +27 -0
  7. data/Rakefile +78 -0
  8. data/VERSION +1 -0
  9. data/bin/corl +55 -0
  10. data/corl.gemspec +228 -0
  11. data/lib/corl/action/add.rb +69 -0
  12. data/lib/corl/action/bootstrap.rb +83 -0
  13. data/lib/corl/action/clone.rb +40 -0
  14. data/lib/corl/action/create.rb +55 -0
  15. data/lib/corl/action/exec.rb +41 -0
  16. data/lib/corl/action/extract.rb +49 -0
  17. data/lib/corl/action/image.rb +30 -0
  18. data/lib/corl/action/images.rb +55 -0
  19. data/lib/corl/action/lookup.rb +35 -0
  20. data/lib/corl/action/machines.rb +51 -0
  21. data/lib/corl/action/provision.rb +37 -0
  22. data/lib/corl/action/remove.rb +51 -0
  23. data/lib/corl/action/save.rb +53 -0
  24. data/lib/corl/action/seed.rb +115 -0
  25. data/lib/corl/action/spawn.rb +75 -0
  26. data/lib/corl/action/start.rb +37 -0
  27. data/lib/corl/action/stop.rb +30 -0
  28. data/lib/corl/action/update.rb +37 -0
  29. data/lib/corl/command/shell.rb +164 -0
  30. data/lib/corl/configuration/file.rb +386 -0
  31. data/lib/corl/event/puppet.rb +90 -0
  32. data/lib/corl/event/regex.rb +52 -0
  33. data/lib/corl/extension/puppetloader.rb +24 -0
  34. data/lib/corl/machine/fog.rb +310 -0
  35. data/lib/corl/machine/physical.rb +161 -0
  36. data/lib/corl/network/default.rb +26 -0
  37. data/lib/corl/node/aws.rb +90 -0
  38. data/lib/corl/node/fog.rb +198 -0
  39. data/lib/corl/node/google.rb +115 -0
  40. data/lib/corl/node/local.rb +26 -0
  41. data/lib/corl/node/rackspace.rb +89 -0
  42. data/lib/corl/project/git.rb +465 -0
  43. data/lib/corl/project/github.rb +108 -0
  44. data/lib/corl/provisioner/puppetnode/resource.rb +245 -0
  45. data/lib/corl/provisioner/puppetnode/resource_group.rb +205 -0
  46. data/lib/corl/provisioner/puppetnode.rb +407 -0
  47. data/lib/corl/template/environment.rb +73 -0
  48. data/lib/corl/template/json.rb +16 -0
  49. data/lib/corl/template/wrapper.rb +16 -0
  50. data/lib/corl/template/yaml.rb +16 -0
  51. data/lib/corl/translator/json.rb +27 -0
  52. data/lib/corl/translator/yaml.rb +27 -0
  53. data/lib/corl.rb +173 -0
  54. data/lib/corl_core/codes.rb +107 -0
  55. data/lib/corl_core/config/collection.rb +57 -0
  56. data/lib/corl_core/config/options.rb +70 -0
  57. data/lib/corl_core/config.rb +337 -0
  58. data/lib/corl_core/core.rb +59 -0
  59. data/lib/corl_core/corl.rb +254 -0
  60. data/lib/corl_core/errors.rb +84 -0
  61. data/lib/corl_core/facade.rb +126 -0
  62. data/lib/corl_core/gems.rb +72 -0
  63. data/lib/corl_core/manager.rb +425 -0
  64. data/lib/corl_core/mixin/action/commit.rb +58 -0
  65. data/lib/corl_core/mixin/action/keypair.rb +105 -0
  66. data/lib/corl_core/mixin/action/node.rb +129 -0
  67. data/lib/corl_core/mixin/action/project.rb +53 -0
  68. data/lib/corl_core/mixin/action/push.rb +52 -0
  69. data/lib/corl_core/mixin/config/collection.rb +53 -0
  70. data/lib/corl_core/mixin/config/ops.rb +53 -0
  71. data/lib/corl_core/mixin/config/options.rb +39 -0
  72. data/lib/corl_core/mixin/lookup.rb +196 -0
  73. data/lib/corl_core/mixin/macro/object_interface.rb +361 -0
  74. data/lib/corl_core/mixin/macro/plugin_interface.rb +380 -0
  75. data/lib/corl_core/mixin/settings.rb +46 -0
  76. data/lib/corl_core/mixin/sub_config.rb +148 -0
  77. data/lib/corl_core/mod/hash.rb +29 -0
  78. data/lib/corl_core/mod/hiera_backend.rb +63 -0
  79. data/lib/corl_core/plugin/action.rb +381 -0
  80. data/lib/corl_core/plugin/base.rb +374 -0
  81. data/lib/corl_core/plugin/command.rb +98 -0
  82. data/lib/corl_core/plugin/configuration.rb +177 -0
  83. data/lib/corl_core/plugin/event.rb +53 -0
  84. data/lib/corl_core/plugin/extension.rb +12 -0
  85. data/lib/corl_core/plugin/machine.rb +266 -0
  86. data/lib/corl_core/plugin/network.rb +359 -0
  87. data/lib/corl_core/plugin/node.rb +904 -0
  88. data/lib/corl_core/plugin/project.rb +927 -0
  89. data/lib/corl_core/plugin/provisioner.rb +51 -0
  90. data/lib/corl_core/plugin/template.rb +80 -0
  91. data/lib/corl_core/plugin/translator.rb +38 -0
  92. data/lib/corl_core/util/cli.rb +352 -0
  93. data/lib/corl_core/util/data.rb +404 -0
  94. data/lib/corl_core/util/disk.rb +114 -0
  95. data/lib/corl_core/util/git.rb +47 -0
  96. data/lib/corl_core/util/interface.rb +319 -0
  97. data/lib/corl_core/util/liquid.rb +17 -0
  98. data/lib/corl_core/util/package.rb +93 -0
  99. data/lib/corl_core/util/shell.rb +239 -0
  100. data/lib/corl_core/util/ssh.rb +286 -0
  101. data/lib/facter/corl_config_ready.rb +13 -0
  102. data/lib/facter/corl_exists.rb +15 -0
  103. data/lib/facter/corl_network.rb +17 -0
  104. data/lib/hiera/corl_logger.rb +18 -0
  105. data/lib/puppet/indirector/corl.rb +27 -0
  106. data/lib/puppet/indirector/data_binding/corl.rb +6 -0
  107. data/lib/puppet/parser/functions/config_initialized.rb +26 -0
  108. data/lib/puppet/parser/functions/corl_include.rb +44 -0
  109. data/lib/puppet/parser/functions/corl_resources.rb +58 -0
  110. data/lib/puppet/parser/functions/deep_merge.rb +21 -0
  111. data/lib/puppet/parser/functions/ensure.rb +29 -0
  112. data/lib/puppet/parser/functions/file_exists.rb +19 -0
  113. data/lib/puppet/parser/functions/global_array.rb +35 -0
  114. data/lib/puppet/parser/functions/global_hash.rb +35 -0
  115. data/lib/puppet/parser/functions/global_options.rb +23 -0
  116. data/lib/puppet/parser/functions/global_param.rb +43 -0
  117. data/lib/puppet/parser/functions/interpolate.rb +26 -0
  118. data/lib/puppet/parser/functions/is_false.rb +21 -0
  119. data/lib/puppet/parser/functions/is_true.rb +21 -0
  120. data/lib/puppet/parser/functions/module_array.rb +38 -0
  121. data/lib/puppet/parser/functions/module_hash.rb +38 -0
  122. data/lib/puppet/parser/functions/module_options.rb +23 -0
  123. data/lib/puppet/parser/functions/module_param.rb +48 -0
  124. data/lib/puppet/parser/functions/name.rb +21 -0
  125. data/lib/puppet/parser/functions/render.rb +33 -0
  126. data/lib/puppet/parser/functions/value.rb +21 -0
  127. data/locales/en.yml +232 -0
  128. data/spec/corl_core/interface_spec.rb +489 -0
  129. data/spec/corl_mock_input.rb +29 -0
  130. data/spec/corl_test_kernel.rb +22 -0
  131. data/spec/spec_helper.rb +15 -0
  132. 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