dopi 0.17.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 (149) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +322 -0
  6. data/Gemfile +7 -0
  7. data/Gemfile.lock +102 -0
  8. data/LICENSE.txt +177 -0
  9. data/README.md +309 -0
  10. data/Rakefile +44 -0
  11. data/Vagrantfile +64 -0
  12. data/bin/dopi +4 -0
  13. data/doc/getting_started.md +247 -0
  14. data/doc/getting_started_examples/001_hello_world.yaml +17 -0
  15. data/doc/getting_started_examples/002_connecting_over_ssh.yaml +35 -0
  16. data/doc/plugins/custom.md +88 -0
  17. data/doc/plugins/mco/rpc.md +82 -0
  18. data/doc/plugins/ssh/custom.md +141 -0
  19. data/doc/plugins/ssh/file_contains.md +37 -0
  20. data/doc/plugins/ssh/file_deploy.md +52 -0
  21. data/doc/plugins/ssh/file_exists.md +31 -0
  22. data/doc/plugins/ssh/file_replace.md +37 -0
  23. data/doc/plugins/ssh/puppet_agent_run.md +50 -0
  24. data/doc/plugins/ssh/reboot.md +22 -0
  25. data/doc/plugins/ssh/wait_for_login.md +53 -0
  26. data/doc/plugins/winrm/cmd.md +161 -0
  27. data/doc/plugins/winrm/file_contains.md +39 -0
  28. data/doc/plugins/winrm/file_exists.md +31 -0
  29. data/doc/plugins/winrm/powershell.md +27 -0
  30. data/doc/plugins/winrm/puppet_agent_run.md +49 -0
  31. data/doc/plugins/winrm/reboot.md +17 -0
  32. data/doc/plugins/winrm/wait_for_login.md +55 -0
  33. data/dopi.gemspec +42 -0
  34. data/lib/dopi/cli/command_add.rb +35 -0
  35. data/lib/dopi/cli/command_list.rb +19 -0
  36. data/lib/dopi/cli/command_remove.rb +31 -0
  37. data/lib/dopi/cli/command_reset.rb +27 -0
  38. data/lib/dopi/cli/command_run.rb +68 -0
  39. data/lib/dopi/cli/command_show.rb +109 -0
  40. data/lib/dopi/cli/command_update.rb +37 -0
  41. data/lib/dopi/cli/command_validate.rb +27 -0
  42. data/lib/dopi/cli/global_options.rb +55 -0
  43. data/lib/dopi/cli/log.rb +33 -0
  44. data/lib/dopi/cli.rb +57 -0
  45. data/lib/dopi/command/custom.rb +52 -0
  46. data/lib/dopi/command/dummy.rb +27 -0
  47. data/lib/dopi/command/mco/rpc.rb +158 -0
  48. data/lib/dopi/command/ssh/custom.rb +48 -0
  49. data/lib/dopi/command/ssh/file_contains.rb +70 -0
  50. data/lib/dopi/command/ssh/file_deploy.rb +71 -0
  51. data/lib/dopi/command/ssh/file_exists.rb +54 -0
  52. data/lib/dopi/command/ssh/file_replace.rb +96 -0
  53. data/lib/dopi/command/ssh/puppet_agent_run.rb +63 -0
  54. data/lib/dopi/command/ssh/reboot.rb +50 -0
  55. data/lib/dopi/command/ssh/wait_for_login.rb +68 -0
  56. data/lib/dopi/command/winrm/cmd.rb +44 -0
  57. data/lib/dopi/command/winrm/file_contains.rb +66 -0
  58. data/lib/dopi/command/winrm/file_exists.rb +51 -0
  59. data/lib/dopi/command/winrm/powershell.rb +16 -0
  60. data/lib/dopi/command/winrm/puppet_agent_run.rb +61 -0
  61. data/lib/dopi/command/winrm/reboot.rb +33 -0
  62. data/lib/dopi/command/winrm/wait_for_login.rb +49 -0
  63. data/lib/dopi/command.rb +239 -0
  64. data/lib/dopi/command_parser/arguments.rb +38 -0
  65. data/lib/dopi/command_parser/credentials.rb +59 -0
  66. data/lib/dopi/command_parser/env.rb +37 -0
  67. data/lib/dopi/command_parser/exec.rb +27 -0
  68. data/lib/dopi/command_parser/exit_code.rb +73 -0
  69. data/lib/dopi/command_parser/output.rb +126 -0
  70. data/lib/dopi/command_set.rb +66 -0
  71. data/lib/dopi/connector/local.rb +77 -0
  72. data/lib/dopi/connector/ssh.rb +170 -0
  73. data/lib/dopi/connector/winrm.rb +167 -0
  74. data/lib/dopi/error.rb +43 -0
  75. data/lib/dopi/log.rb +18 -0
  76. data/lib/dopi/node.rb +70 -0
  77. data/lib/dopi/plan.rb +99 -0
  78. data/lib/dopi/pluginmanager.rb +62 -0
  79. data/lib/dopi/state.rb +226 -0
  80. data/lib/dopi/state_store.rb +155 -0
  81. data/lib/dopi/step.rb +227 -0
  82. data/lib/dopi/step_set.rb +70 -0
  83. data/lib/dopi/version.rb +3 -0
  84. data/lib/dopi.rb +165 -0
  85. data/spec/command_helper.rb +11 -0
  86. data/spec/fixtures/mco_client.cfg +26 -0
  87. data/spec/fixtures/plans/fail_on_timeout.yaml +20 -0
  88. data/spec/fixtures/plans/hello_world.yaml +34 -0
  89. data/spec/fixtures/plans/non_existing_node.yaml +26 -0
  90. data/spec/fixtures/plans/test_role_variable.yaml +29 -0
  91. data/spec/fixtures/puppet/Puppetfile +8 -0
  92. data/spec/fixtures/puppet/Puppetfile.lock +57 -0
  93. data/spec/fixtures/puppet/hiera.yaml +6 -0
  94. data/spec/fixtures/puppet/manifests/site.pp +52 -0
  95. data/spec/fixtures/test_configuration.yaml +54 -0
  96. data/spec/fixtures/test_credentials.yaml +11 -0
  97. data/spec/fixtures/test_deloyed_file.txt +5 -0
  98. data/spec/fixtures/test_infrastructure.yaml +12 -0
  99. data/spec/fixtures/test_nodes.yaml +45 -0
  100. data/spec/fixtures/testenv_plan.yaml +159 -0
  101. data/spec/integration/dopi/addrun_spec.rb +31 -0
  102. data/spec/integration/dopi/cli/command_run_spec.rb +38 -0
  103. data/spec/integration/dopi/cli/global_options_spec.rb +128 -0
  104. data/spec/integration/dopi/command_spec.rb +66 -0
  105. data/spec/integration/dopi/fail_check_plans/file_exists_fails.yaml +38 -0
  106. data/spec/integration/dopi/fail_check_plans/output_parser.yaml +39 -0
  107. data/spec/integration/dopi/fail_check_plans/powershell_fail.yaml +25 -0
  108. data/spec/integration/dopi/fail_check_plans/timeout.yaml +29 -0
  109. data/spec/integration/dopi/fail_check_plans/verify_commands.yaml +33 -0
  110. data/spec/integration/dopi/failplan.rb +27 -0
  111. data/spec/integration/dopi/plan.rb +27 -0
  112. data/spec/integration/dopi/plans/dummy.yaml +29 -0
  113. data/spec/integration/dopi/plans/max_per_role.yaml +55 -0
  114. data/spec/integration/dopi/plans/no_timeout.yaml +29 -0
  115. data/spec/integration/dopi/plans/node_and_role_patterns.yaml +58 -0
  116. data/spec/integration/dopi/plans/node_by_config.yaml +116 -0
  117. data/spec/integration/dopi/plans/plugin_defaults.yaml +86 -0
  118. data/spec/integration/dopi/plans/plugins/mco/rpc.yaml +33 -0
  119. data/spec/integration/dopi/plans/plugins/ssh/custom.yaml +97 -0
  120. data/spec/integration/dopi/plans/plugins/ssh/file_contains.yaml +51 -0
  121. data/spec/integration/dopi/plans/plugins/ssh/file_deploy.yaml +82 -0
  122. data/spec/integration/dopi/plans/plugins/ssh/file_exists.yaml +69 -0
  123. data/spec/integration/dopi/plans/plugins/ssh/file_replace.yaml +55 -0
  124. data/spec/integration/dopi/plans/plugins/ssh/puppet_agent_run.yaml +45 -0
  125. data/spec/integration/dopi/plans/plugins/ssh/reboot.yaml +43 -0
  126. data/spec/integration/dopi/plans/plugins/ssh/wait_for_login.yaml +45 -0
  127. data/spec/integration/dopi/plans/plugins/winrm/cmd.yaml +39 -0
  128. data/spec/integration/dopi/plans/plugins/winrm/file_contains.yaml +51 -0
  129. data/spec/integration/dopi/plans/plugins/winrm/file_exists.yaml +69 -0
  130. data/spec/integration/dopi/plans/plugins/winrm/reboot.yaml +31 -0
  131. data/spec/integration/dopi/plans/resolve_roles_on_validate.yaml +23 -0
  132. data/spec/integration/dopi/plans/ssh_parallel.yaml +37 -0
  133. data/spec/integration/dopi/plans/verify_commands.yaml +49 -0
  134. data/spec/spec_helper.rb +104 -0
  135. data/spec/unit/dopi/command/custom_spec.rb +58 -0
  136. data/spec/unit/dopi/command/mco/rpc_spec.rb +157 -0
  137. data/spec/unit/dopi/command/ssh/custom_spec.rb +30 -0
  138. data/spec/unit/dopi/command/ssh/file_deploy_spec.rb +42 -0
  139. data/spec/unit/dopi/command/ssh/file_replace_spec.rb +35 -0
  140. data/spec/unit/dopi/command_parser/credentials_spec.rb +53 -0
  141. data/spec/unit/dopi/command_parser/exit_code_spec.rb +63 -0
  142. data/spec/unit/dopi/command_parser/output_spec.rb +129 -0
  143. data/spec/unit/dopi/command_spec.rb +14 -0
  144. data/spec/unit/dopi/connector/winrm_spec.rb +111 -0
  145. data/spec/unit/dopi/node_spec.rb +24 -0
  146. data/spec/unit/dopi/plan_spec.rb +31 -0
  147. data/spec/unit/dopi/state_spec.rb +109 -0
  148. data/spec/unit/dopi/step_spec.rb +13 -0
  149. metadata +448 -0
@@ -0,0 +1,109 @@
1
+
2
+ module Dopi
3
+ module Cli
4
+
5
+ def self.command_show(base)
6
+ base.class_eval do
7
+
8
+ desc 'Show plan details and state'
9
+ arg_name 'name'
10
+ command :show do |c|
11
+ c.desc 'Do not exit and continuously update the display'
12
+ c.default_value false
13
+ c.switch [:follow, :f]
14
+
15
+ c.desc 'Display all details of the tree'
16
+ c.default_value false
17
+ c.switch [:detailed, :d]
18
+
19
+ c.action do |global_options,options,args|
20
+ help_now!('Specify a plan name to show') if args.empty?
21
+ help_now!('You can only show one plan') if args.length > 1
22
+ plan_name = args[0]
23
+ if options[:follow]
24
+ begin
25
+ Curses.noecho
26
+ Curses.curs_set(0)
27
+ Curses.init_screen
28
+ Curses.start_color
29
+ Curses.init_pair(1, Curses::COLOR_BLACK, Curses::COLOR_WHITE)
30
+ Curses.init_pair(2, Curses::COLOR_WHITE, Curses::COLOR_BLACK)
31
+ Curses.init_pair(3, Curses::COLOR_BLUE, Curses::COLOR_BLACK)
32
+ Curses.init_pair(4, Curses::COLOR_GREEN, Curses::COLOR_BLACK)
33
+ Curses.init_pair(5, Curses::COLOR_YELLOW, Curses::COLOR_BLACK)
34
+ Curses.init_pair(6, Curses::COLOR_RED, Curses::COLOR_BLACK)
35
+ draw_screen(plan_name, options[:detailed])
36
+ Curses.refresh
37
+ Dopi.on_state_change(plan_name) do
38
+ Curses.clear
39
+ draw_screen(plan_name, options[:detailed])
40
+ Curses.refresh
41
+ end
42
+ ensure
43
+ Curses.close_screen
44
+ end
45
+ else
46
+ print_state(plan_name, options[:detailed])
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+
54
+ def self.str_state_color(state, string)
55
+ attr = case state
56
+ when :ready then Curses.color_pair(3)
57
+ when :done then Curses.color_pair(4)
58
+ when :partial then Curses.color_pair(5)
59
+ when :running, :started then Curses.color_pair(5) | Curses::A_BLINK
60
+ when :failed then Curses.color_pair(6)
61
+ else Curses.color_pair(2)
62
+ end
63
+ Curses.attrset(attr)
64
+ Curses.addstr(string)
65
+ Curses.attrset(Curses.color_pair(2))
66
+ end
67
+
68
+ def self.draw_screen(plan_name, detailed)
69
+ plan = Dopi.show(plan_name)
70
+ Curses.setpos(0, 0)
71
+ Curses.attrset(Curses.color_pair(1))
72
+ Curses.addstr("DOPi #{Dopi::VERSION} - #{plan.name} [ #{plan.state.to_s} ]".ljust(Curses.cols))
73
+ Curses.setpos(1, 0)
74
+ Curses.attrset(Curses.color_pair(2))
75
+ plan.step_sets.each do |step_set|
76
+ draw_step_set(step_set, detailed)
77
+ end
78
+ end
79
+
80
+ def self.draw_step_set(step_set, detailed)
81
+ str_state_color(step_set.state, ' - [' + step_set.state.to_s + '] ' + step_set.name + "\n")
82
+ if detailed or step_set.state_running? or step_set.state_children_partial?
83
+ step_set.steps.each do |step|
84
+ draw_step(step, detailed)
85
+ end
86
+ end
87
+ end
88
+
89
+ def self.draw_step(step, detailed)
90
+ str_state_color(step.state, ' - [' + step.state.to_s + '] ' + step.name + "\n")
91
+ if detailed or step.state_running? or step.state_children_partial?
92
+ step.command_sets.each do |command_set|
93
+ draw_command_set(command_set, detailed)
94
+ end
95
+ end
96
+ end
97
+
98
+ def self.draw_command_set(command_set, detailed)
99
+ str_state_color(command_set.state, " - [ #{command_set.state.to_s} ] #{command_set.node.name}\n")
100
+ if detailed or command_set.state_running? or command_set.state_children_partial?
101
+ command_set.commands.each do |command|
102
+ str_state_color(command.state, " - [ #{command.state.to_s} ] #{command.title}\n")
103
+ end
104
+ end
105
+ end
106
+
107
+ end
108
+ end
109
+
@@ -0,0 +1,37 @@
1
+ module Dopi
2
+ module Cli
3
+
4
+ def self.command_update(base)
5
+ base.class_eval do
6
+
7
+ desc 'Update the plan and/or the plan state for a given plan yaml or plan name.'
8
+ arg_name 'plan'
9
+ command :update do |c|
10
+ c.desc 'Remove the existing DOPi state and start with a clean state'
11
+ c.default_value false
12
+ c.switch [:clear, :c]
13
+
14
+ c.desc 'Ignore the update and keep the state as it is, only update the internal version string'
15
+ c.default_value false
16
+ c.switch [:ignore, :i]
17
+
18
+ c.action do |global_options,options,args|
19
+ help_now!('Specify a plan name or to update') if args.empty?
20
+ help_now!('You can only update one plan') if args.length > 1
21
+ plan = args[0]
22
+ if Dopi.list.include?(plan)
23
+ Dopi.update_state(plan, options)
24
+ elsif File.exists?(plan)
25
+ Dopi.update_plan(plan, options)
26
+ else
27
+ help_now!("the provided plan '#{plan}' is not an existing file or plan name")
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+
@@ -0,0 +1,27 @@
1
+ module Dopi
2
+ module Cli
3
+
4
+ def self.command_validate(base)
5
+ base.class_eval do
6
+
7
+ desc 'Validate a plan file'
8
+ arg_name 'plan_file'
9
+ command :validate do |c|
10
+ c.action do |global_options,options,args|
11
+ help_now!('Specify a plan file to add') if args.empty?
12
+ help_now!('You can only add one plan') if args.length > 1
13
+ plan_file = args[0]
14
+ if Dopi.valid?(plan_file)
15
+ puts "Plan is valid"
16
+ else
17
+ exit_now!("Plan is NOT valid")
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+
@@ -0,0 +1,55 @@
1
+ #
2
+ # DOPi CLI gloable options
3
+ #
4
+
5
+ module Dopi
6
+ module Cli
7
+
8
+ def self.global_options(base)
9
+ base.class_eval do
10
+ desc 'Use Hiera to get the role for the nodes'
11
+ default_value DopCommon.config.use_hiera
12
+ switch [:use_hiera, :h]
13
+
14
+ desc 'Specify the hiera configuration file'
15
+ default_value DopCommon.config.hiera_yaml
16
+ arg_name 'YAML'
17
+ flag [:hiera_yaml]
18
+
19
+ desc 'Try to load the scope for the nodes from existing facts'
20
+ default_value DopCommon.config.load_facts
21
+ switch [:load_facts]
22
+
23
+ desc 'Specify the directory where dopi can find facts'
24
+ default_value DopCommon.config.facts_dir
25
+ arg_name 'DIR'
26
+ flag [:facts_dir]
27
+
28
+ desc 'Set the name of the variable DOPi should use as the roles variable'
29
+ default_value DopCommon.config.role_variable
30
+ arg_name 'VARIABLE_NAME'
31
+ flag [:role_variable]
32
+
33
+ desc 'Set the default value for the node role'
34
+ default_value DopCommon.config.role_default
35
+ arg_name 'ROLE'
36
+ flag [:role_default]
37
+
38
+ desc 'Set the MCollective client configuration.'
39
+ default_value DopCommon.config.mco_config
40
+ arg_name 'FILE'
41
+ flag [:mco_config]
42
+
43
+ desc 'Use the DOPi logger to capture MCollective logs (this is enabled by default)'
44
+ default_value DopCommon.config.mco_dopi_logger
45
+ switch [:mco_dopi_logger]
46
+
47
+ desc 'Time until a connection check is marked as failure'
48
+ default_value DopCommon.config.connection_check_timeout
49
+ arg_name 'SECONDS'
50
+ flag [:connection_check_timeout]
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,33 @@
1
+ #
2
+ # Initializes the logger for the CLI
3
+ #
4
+
5
+ module Dopi
6
+ module Cli
7
+
8
+ def self.state(plan_name, detailed = false)
9
+ plan = Dopi.show(plan_name)
10
+ result = "[#{plan.state.to_s}] #{plan.name}\n"
11
+ plan.step_sets.each do |step_set|
12
+ result << " [#{step_set.state.to_s}] #{step_set.name}\n"
13
+ step_set.steps.each do |step|
14
+ result << " [#{step.state.to_s}] #{step.name}\n"
15
+ if detailed or step.state_running? or step.state_children_partial?
16
+ step.command_sets.each do |command_set|
17
+ result << " [#{command_set.state.to_s}] #{command_set.name}\n"
18
+ command_set.commands.each do |command|
19
+ result << " [#{command.state.to_s}] #{command.title}\n"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ return result
26
+ end
27
+
28
+ def self.print_state(plan_name, detailed = false)
29
+ puts state(plan_name, detailed)
30
+ end
31
+
32
+ end
33
+ end
data/lib/dopi/cli.rb ADDED
@@ -0,0 +1,57 @@
1
+ #
2
+ # The DOPi CLI main module
3
+ #
4
+ require 'gli'
5
+ require 'dopi'
6
+ require 'dop_common/cli/node_selection'
7
+ require 'dop_common/cli/log'
8
+ require 'dop_common/cli/global_options'
9
+ require 'dopi/cli/log'
10
+ require 'dopi/cli/global_options'
11
+ require 'dopi/cli/command_run'
12
+ require 'dopi/cli/command_validate'
13
+ require 'dopi/cli/command_add'
14
+ require 'dopi/cli/command_update'
15
+ require 'dopi/cli/command_list'
16
+ require 'dopi/cli/command_remove'
17
+ require 'dopi/cli/command_show'
18
+ require 'dopi/cli/command_reset'
19
+ require 'logger/colors'
20
+ require 'curses'
21
+ require 'yaml'
22
+ require 'fileutils'
23
+
24
+ module Dopi
25
+ module Cli
26
+ include GLI::App
27
+ extend self
28
+
29
+ program_desc 'DOPi Command line Client'
30
+ version Dopi::VERSION
31
+
32
+ subcommand_option_handling :normal
33
+ arguments :strict
34
+
35
+ config_file DopCommon.config.config_file
36
+
37
+ DopCommon::Cli.global_options(self)
38
+ global_options(self)
39
+
40
+ pre do |global,command,options,args|
41
+ DopCommon.configure = global
42
+ ENV['GLI_DEBUG'] = 'true' if global[:trace] == true
43
+ DopCommon::Cli.initialize_logger('dopi.log', global[:log_level], global[:verbosity], global[:trace])
44
+ true
45
+ end
46
+
47
+ command_run(self)
48
+ command_validate(self)
49
+ command_add(self)
50
+ command_update(self)
51
+ command_list(self)
52
+ command_remove(self)
53
+ command_show(self)
54
+ command_reset(self)
55
+
56
+ end
57
+ end
@@ -0,0 +1,52 @@
1
+ #
2
+ # DOPi Plugin: Custom Command
3
+ #
4
+
5
+ module Dopi
6
+ class Command
7
+ class Custom < Dopi::Command
8
+ include Dopi::Connector::Local
9
+ include Dopi::CommandParser::Exec
10
+ include Dopi::CommandParser::Env
11
+ include Dopi::CommandParser::Arguments
12
+ include Dopi::CommandParser::ExitCode
13
+ include Dopi::CommandParser::Output
14
+
15
+ public
16
+ def validate
17
+ #validate_exec
18
+ # remove after the refactoring is complete
19
+ unless Dopi::Command::Custom > self.class && self.method(:exec).owner == self.class
20
+ log_validation_method('exec_valid?', CommandParsingError)
21
+ end
22
+ validate_env
23
+ validate_arguments
24
+ validate_exit_code
25
+ validate_output
26
+ end
27
+
28
+ def run
29
+ result = []
30
+ cmd_stdout, cmd_stderr, cmd_exit_code = local_command(env, command_string)
31
+ # Output Parser
32
+ result << check_output(cmd_stdout)
33
+ result << check_output(cmd_stderr)
34
+ # Exit Code Parser
35
+ result << check_exit_code(cmd_exit_code)
36
+ result.all?
37
+ end
38
+
39
+ def run_noop
40
+ log(:info, "(NOOP) Executing '#{command_string}' for command #{name}")
41
+ log(:info, "(NOOP) Environment: #{env.to_s}")
42
+ end
43
+
44
+ private
45
+
46
+ def command_string
47
+ exec + ' ' + arguments
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,27 @@
1
+ #
2
+ # DOPi dummy command.
3
+ #
4
+ # this simply prints the command hash if there was one given
5
+ #
6
+
7
+ module Dopi
8
+ class Command
9
+ class Dummy < Dopi::Command
10
+
11
+ def validate
12
+ true
13
+ end
14
+
15
+ def run
16
+ log(:info, "Running dummy command")
17
+ if @command_hash.class == Hash
18
+ log(:info, "Command hash was: #{hash.inspect}")
19
+ end
20
+ true
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+
27
+
@@ -0,0 +1,158 @@
1
+ #
2
+ # DOPi Plugin: MCO RPC
3
+ #
4
+ require 'mcollective'
5
+
6
+ module Dopi
7
+ class Command
8
+ module Mco
9
+ class Rpc < Dopi::Command
10
+ include MCollective::RPC
11
+ include Dopi::CommandParser::ExitCode
12
+
13
+ def validate
14
+ log_validation_method('options_valid?', CommandParsingError)
15
+ log_validation_method('arguments_valid?', CommandParsingError)
16
+ # Skip validation in subclasses that overwrite the non optional methods
17
+ unless Dopi::Command::Mco::Rpc > self.class && self.method(:agent).owner == self.class
18
+ log_validation_method('agent_valid?', CommandParsingError)
19
+ end
20
+ unless Dopi::Command::Mco::Rpc > self.class && self.method(:action).owner == self.class
21
+ log_validation_method('action_valid?', CommandParsingError)
22
+ end
23
+ validate_exit_code
24
+ end
25
+
26
+ def run
27
+ result_ok = true
28
+ flags = {
29
+ :configfile => DopCommon.config.mco_config,
30
+ :options => options,
31
+ :exit_on_failure => false
32
+ }
33
+ mc = rpcclient(agent, flags)
34
+ results = mc.custom_request(action, arguments, [@node.name], {'identity' => @node.name})
35
+ if results.empty?
36
+ log(:error, "No answer from node recieved")
37
+ result_ok = false
38
+ else
39
+ result_ok = false unless parse_mco_result(results.first)
40
+ end
41
+ mc.disconnect
42
+ result_ok
43
+ end
44
+
45
+ def run_noop
46
+ log(:info, "(NOOP) command #{name}: agent: #{agent}")
47
+ log(:info, "(NOOP) command #{name}: options: #{options}")
48
+ log(:info, "(NOOP) command #{name}: arguments: #{arguments}")
49
+ end
50
+
51
+ def agent
52
+ @agent ||= agent_valid? ?
53
+ hash[:agent] : nil
54
+ end
55
+
56
+ def options
57
+ @options ||= options_valid? ?
58
+ options_defaults.merge(hash[:options]) :
59
+ options_defaults
60
+ end
61
+
62
+ def action
63
+ @action ||= action_valid? ?
64
+ hash[:action] : nil
65
+ end
66
+
67
+ def arguments
68
+ @arguments ||= arguments_valid? ?
69
+ hash[:arguments] : {}
70
+ end
71
+
72
+ private
73
+
74
+ def expect_exit_codes_defaults
75
+ 0
76
+ end
77
+
78
+ def options_defaults
79
+ MCollective::Util.default_options.merge({
80
+ :config => DopCommon.config.mco_config
81
+ })
82
+ end
83
+
84
+ def parse_mco_result(result)
85
+ result_ok = true
86
+ unless check_exit_code(result[:statuscode])
87
+ log(:error, result[:statusmsg])
88
+ result_ok = false
89
+ end
90
+ result_ok = false unless parse_mco_result_data(result[:data])
91
+ result_ok
92
+ end
93
+
94
+ def parse_mco_result_data(data)
95
+ warning = "You are using the RPC plugin to run the #{agent} MCollective agent."
96
+ warning += " DOPi will not know what to expect in the resulting data as this is plugin specific."
97
+ warning += " Not all errors may be detected."
98
+ log(:warn, warning)
99
+ log(:debug, 'Receieved data: ' + data.inspect)
100
+ return true
101
+ end
102
+
103
+ def agent_valid?
104
+ hash[:agent] or
105
+ raise CommandParsingError, "No agent defined"
106
+ hash[:agent].kind_of?(String) or
107
+ raise CommandParsingError, "The value for 'agent' has to be a String"
108
+ begin
109
+ MCollective::DDL.new(hash[:agent])
110
+ rescue => e
111
+ raise CommandParsingError, "Unable to load the MCollective agent #{hash[:agent]}on this system: #{e.message}"
112
+ end
113
+ true
114
+ end
115
+
116
+ def options_valid?
117
+ return false if hash[:options].nil? # is optional
118
+ hash[:options].kind_of?(Hash) or
119
+ raise CommandParsingError, "The value for 'options' has to be a Hash"
120
+ end
121
+
122
+ def action_valid?
123
+ hash[:action] or
124
+ raise CommandParsingError, "No action defined"
125
+ hash[:action].kind_of?(String) or
126
+ raise CommandParsingError, "The value for 'action' has to be a String"
127
+ agent_ddl = nil
128
+ begin
129
+ agent_ddl = MCollective::DDL.new(agent)
130
+ rescue CommandParsingError
131
+ raise CommandParsingError, "Agent not valid, unable to verify the action #{hash[:action]}"
132
+ else
133
+ agent_ddl.actions.include?(hash[:action]) or
134
+ raise CommandParsingError, "The action #{hash[:action]} for agent #{agent} does not exist"
135
+ end
136
+ true
137
+ end
138
+
139
+ def arguments_valid?
140
+ hash.nil? or hash[:arguments].nil? or hash[:arguments].kind_of?(Hash) or
141
+ raise CommandParsingError, "The value for 'arguments' has to be a Hash"
142
+ begin
143
+ args = hash ? ( hash[:arguments] or {} ) : {}
144
+ agent_ddl = MCollective::DDL.new(agent)
145
+ agent_ddl.validate_rpc_request(action, args)
146
+ rescue CommandParsingError
147
+ raise CommandParsingError, "Agent and/or Action not valid, unable to verify the action #{hash[:action]}"
148
+ rescue MCollective::DDLValidationError => e
149
+ raise CommandParsingError, "Error while parsing arguments for agent #{agent} and action #{action}: #{e.message}"
150
+ end
151
+ return false if hash[:arguments].nil? # is completely optional if no exception was raised so far
152
+ true
153
+ end
154
+
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,48 @@
1
+ #
2
+ # DOPi Plugin: Custom Command
3
+ #
4
+
5
+ module Dopi
6
+ class Command
7
+ class Ssh
8
+ class Custom < Dopi::Command
9
+ include Dopi::Connector::Ssh
10
+ include Dopi::CommandParser::Exec
11
+ include Dopi::CommandParser::Env
12
+ include Dopi::CommandParser::Arguments
13
+ include Dopi::CommandParser::ExitCode
14
+ include Dopi::CommandParser::Output
15
+
16
+ public
17
+
18
+ def validate
19
+ validate_ssh
20
+ validate_exec
21
+ validate_env
22
+ validate_arguments
23
+ validate_exit_code
24
+ validate_output
25
+ end
26
+
27
+ def run
28
+ cmd_stdout, cmd_stderr, cmd_exit_code = ssh_command(env, command_string)
29
+ check_output(cmd_stdout) &&
30
+ check_output(cmd_stderr) &&
31
+ check_exit_code(cmd_exit_code)
32
+ end
33
+
34
+ def run_noop
35
+ log(:info, "(NOOP) Executing '#{command_string}' for command #{name}")
36
+ log(:info, "(NOOP) Environment: #{env.to_s}")
37
+ end
38
+
39
+ private
40
+
41
+ def command_string
42
+ exec + ' ' + arguments
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,70 @@
1
+ #
2
+ # DOPi Plugin: File Contains
3
+ #
4
+ require 'pathname'
5
+
6
+ module Dopi
7
+ class Command
8
+ class Ssh
9
+ class FileContains < Dopi::Command
10
+ include Dopi::Connector::Ssh
11
+ include Dopi::CommandParser::ExitCode
12
+
13
+ public
14
+
15
+ def validate
16
+ validate_ssh
17
+ validate_exit_code
18
+ log_validation_method(:file_valid?, CommandParsingError)
19
+ log_validation_method(:pattern_valid?, CommandParsingError)
20
+ end
21
+
22
+ def run
23
+ cmd_stdout, cmd_stderr, cmd_exit_code = ssh_command({}, command_string)
24
+ check_exit_code(cmd_exit_code)
25
+ end
26
+
27
+ def run_noop
28
+ log(:info, "(NOOP) Executing '#{command_string}' for command #{name}")
29
+ end
30
+
31
+ def file
32
+ @file ||= file_valid? ? hash[:file] : nil
33
+ end
34
+
35
+ def pattern
36
+ @pattern ||= pattern_valid? ?
37
+ hash[:pattern] : nil
38
+ end
39
+
40
+ private
41
+
42
+ def command_string
43
+ "grep -e \"#{pattern}\" #{file}"
44
+ end
45
+
46
+ def file_valid?
47
+ hash[:file] or
48
+ raise CommandParsingError, "Plugin #{name}: The key 'file' needs to be specified"
49
+ begin
50
+ Pathname.new(hash[:file]).absolute? or
51
+ raise CommandParsingError, "Plugin #{name}: The path for 'file' has to be absolute"
52
+ rescue ArgumentError => e
53
+ raise CommandParsingError, "Plugin #{name}: The value in 'file' is not a valid file path: #{e.message}"
54
+ end
55
+ end
56
+
57
+ def pattern_valid?
58
+ hash[:pattern] or
59
+ raise CommandParsingError, "Plugin #{name}: The key 'pattern' needs to be specified"
60
+ begin
61
+ Regexp.new(hash[:pattern])
62
+ rescue
63
+ raise CommandParsingError, "Plugin #{name}: The value in 'pattern' is not a valid regexp: #{e.message}"
64
+ end
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+ end