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.
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