Module: Dopi::Cli

Extended by:
Cli
Includes:
GLI::App
Included in:
Cli
Defined in:
lib/dopi/cli.rb,
lib/dopi/cli/log.rb,
lib/dopi/cli/command_add.rb,
lib/dopi/cli/command_run.rb,
lib/dopi/cli/command_list.rb,
lib/dopi/cli/command_show.rb,
lib/dopi/cli/command_reset.rb,
lib/dopi/cli/command_remove.rb,
lib/dopi/cli/command_update.rb,
lib/dopi/cli/global_options.rb,
lib/dopi/cli/command_validate.rb

Class Method Summary collapse

Class Method Details

.command_add(base) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/dopi/cli/command_add.rb', line 4

def self.command_add(base)
  base.class_eval do

    desc 'Add a new plan file to the plan cache'
    arg_name 'plan_file'
    command :add do |c|
      c.desc 'update the plan if it already exists'
      c.default_value false
      c.switch [:update, :u]

      c.action do |global_options,options,args|
        help_now!('Specify a plan file to add') if args.empty?
        help_now!('You can only add one plan') if args.length > 1
        plan_file = args[0]
        begin
          puts Dopi.add(plan_file)
        rescue DopCommon::PlanExistsError => e
          if options[:update]
            puts Dopi.update_plan(plan_file, {})
          else
            raise e
          end
		end
      end
    end

  end
end

.command_list(base) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
# File 'lib/dopi/cli/command_list.rb', line 4

def self.command_list(base)
  base.class_eval do

    desc 'Show the list of plans in the dopi plan cache'
    command :list do |c|
      c.action do |global_options,options,args|
        puts Dopi.list
      end
    end

  end
end

.command_remove(base) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/dopi/cli/command_remove.rb', line 4

def self.command_remove(base)
  base.class_eval do

    desc 'Remove an existing plan from the plan cache'
    arg_name 'name'
    command :remove do |c|
      c.desc 'Keep the DOPi state file'
      c.default_value false
      c.switch [:keep_dopi_state]

      c.desc 'Remove the DOPv state file (THIS WILL REMOVE THE DISK INFO)'
      c.default_value false
      c.switch [:remove_dopv_state]

      c.action do |global_options,options,args|
        help_now!('Specify a plan name to remove') if args.empty?
        help_now!('You can only remove one plan') if args.length > 1
        plan_name = args[0]
        Dopi.remove(plan_name, !options[:keep_dopi_state], options[:remove_dopv_state])
      end
    end

  end
end

.command_reset(base) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/dopi/cli/command_reset.rb', line 4

def self.command_reset(base)
  base.class_eval do

    desc 'Reset a failed plan'
    arg_name 'name'
    command :reset do |c|
      c.desc 'Force reset the states back to ready from every state'
      c.default_value false
      c.switch [:force, :f]

      c.action do |global_options,options,args|
        help_now!('Specify a plan name to run') if args.empty?
        help_now!('You can only run one plan') if args.length > 1
        plan_name = args[0]
        Dopi.reset(plan_name, options[:force])
      end
    end

  end
end

.command_run(base) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/dopi/cli/command_run.rb', line 17

def self.command_run(base)
  base.class_eval do

    desc 'Run the plan'
    arg_name 'id'
    command :run do |c|
      run_options(c)
      c.action do |global_options,options,args|
        help_now!('Specify a plan name to run') if args.empty?
        help_now!('You can only run one plan') if args.length > 1
        options[:run_for_nodes] = DopCommon::Cli.parse_node_select_options(options)
        plan_name = args[0]
        begin
          Dopi.run(plan_name, options)
        rescue Dopi::StateTransitionError => e
          Dopi.log.error(e.message)
          exit_now!("Some steps are in a state where they can't be started again. Try to reset the plan.")
        ensure
          print_state(plan_name)
          exit_now!('Errors during plan run detected!') if Dopi.show(plan_name).state_failed?
        end
      end
    end

    desc 'Add a plan, run it and then remove it again (This is mainly for testing)'
    arg_name 'plan_file'
    command :oneshot do |c|
      run_options(c)
      c.action do |global_options,options,args|
        help_now!('Specify a plan file to add') if args.empty?
        help_now!('You can only add one plan') if args.length > 1
        options[:run_for_nodes] = DopCommon::Cli.parse_node_select_options(options)
        plan_file = args[0]
        plan_name = Dopi.add(plan_file)
        begin
          Dopi.run(plan_name, options)
        ensure
          print_state(plan_name)
          failed = Dopi.show(plan_name).state_failed?
          Dopi.remove(plan_name, true)
          exit_now!('Errors during plan run detected!') if failed
        end
      end
    end

  end
end

.command_show(base) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/dopi/cli/command_show.rb', line 5

def self.command_show(base)
  base.class_eval do

    desc 'Show plan details and state'
    arg_name 'name'
    command :show do |c|
      c.desc 'Do not exit and continuously update the display'
      c.default_value false
      c.switch [:follow, :f]

      c.desc 'Display all details of the tree'
      c.default_value false
      c.switch [:detailed, :d]

      c.action do |global_options,options,args|
        help_now!('Specify a plan name to show') if args.empty?
        help_now!('You can only show one plan') if args.length > 1
        plan_name = args[0]
        if options[:follow]
          begin
            Curses.noecho
            Curses.curs_set(0)
            Curses.init_screen
            Curses.start_color
            Curses.init_pair(1, Curses::COLOR_BLACK, Curses::COLOR_WHITE)
            Curses.init_pair(2, Curses::COLOR_WHITE, Curses::COLOR_BLACK)
            Curses.init_pair(3, Curses::COLOR_BLUE, Curses::COLOR_BLACK)
            Curses.init_pair(4, Curses::COLOR_GREEN, Curses::COLOR_BLACK)
            Curses.init_pair(5, Curses::COLOR_YELLOW, Curses::COLOR_BLACK)
            Curses.init_pair(6, Curses::COLOR_RED, Curses::COLOR_BLACK)
            draw_screen(plan_name, options[:detailed])
            Curses.refresh
            Dopi.on_state_change(plan_name) do
              Curses.clear
              draw_screen(plan_name, options[:detailed])
              Curses.refresh
            end
          ensure
            Curses.close_screen
          end
        else
          print_state(plan_name, options[:detailed])
        end
      end
    end

  end
end

.command_update(base) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/dopi/cli/command_update.rb', line 4

def self.command_update(base)
  base.class_eval do

    desc 'Update the plan and/or the plan state for a given plan yaml or plan name.'
    arg_name 'plan'
    command :update do |c|
      c.desc 'Remove the existing DOPi state and start with a clean state'
      c.default_value false
      c.switch [:clear, :c]

      c.desc 'Ignore the update and keep the state as it is, only update the internal version string'
      c.default_value false
      c.switch [:ignore, :i]

      c.action do |global_options,options,args|
        help_now!('Specify a plan name or  to update') if args.empty?
        help_now!('You can only update one plan') if args.length > 1
        plan = args[0]
        if Dopi.list.include?(plan)
          Dopi.update_state(plan, options)
        elsif File.exists?(plan)
          Dopi.update_plan(plan, options)
        else
          help_now!("the provided plan '#{plan}' is not an existing file or plan name")
        end
      end
    end

  end
end

.command_validate(base) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/dopi/cli/command_validate.rb', line 4

def self.command_validate(base)
  base.class_eval do

    desc 'Validate a plan file'
    arg_name 'plan_file'
    command :validate do |c|
      c.action do |global_options,options,args|
        help_now!('Specify a plan file to add') if args.empty?
        help_now!('You can only add one plan') if args.length > 1
        plan_file = args[0]
        if Dopi.valid?(plan_file)
          puts "Plan is valid"
        else
          exit_now!("Plan is NOT valid")
        end
      end
    end

  end
end

.draw_command_set(command_set, detailed) ⇒ Object



98
99
100
101
102
103
104
105
# File 'lib/dopi/cli/command_show.rb', line 98

def self.draw_command_set(command_set, detailed)
  str_state_color(command_set.state, "     - [ #{command_set.state.to_s} ] #{command_set.node.name}\n")
  if detailed or command_set.state_running? or command_set.state_children_partial?
    command_set.commands.each do |command|
      str_state_color(command.state, "       - [ #{command.state.to_s} ] #{command.title}\n")
    end
  end
end

.draw_screen(plan_name, detailed) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/dopi/cli/command_show.rb', line 68

def self.draw_screen(plan_name, detailed)
  plan = Dopi.show(plan_name)
  Curses.setpos(0, 0)
  Curses.attrset(Curses.color_pair(1))
  Curses.addstr("DOPi #{Dopi::VERSION} - #{plan.name} [ #{plan.state.to_s} ]".ljust(Curses.cols))
  Curses.setpos(1, 0)
  Curses.attrset(Curses.color_pair(2))
  plan.step_sets.each do |step_set|
    draw_step_set(step_set, detailed)
  end
end

.draw_step(step, detailed) ⇒ Object



89
90
91
92
93
94
95
96
# File 'lib/dopi/cli/command_show.rb', line 89

def self.draw_step(step, detailed)
  str_state_color(step.state, '   - [' + step.state.to_s + '] ' + step.name + "\n")
  if detailed or step.state_running? or step.state_children_partial?
    step.command_sets.each do |command_set|
      draw_command_set(command_set, detailed)
    end
  end
end

.draw_step_set(step_set, detailed) ⇒ Object



80
81
82
83
84
85
86
87
# File 'lib/dopi/cli/command_show.rb', line 80

def self.draw_step_set(step_set, detailed)
  str_state_color(step_set.state, ' - [' + step_set.state.to_s + '] ' + step_set.name + "\n")
  if detailed or step_set.state_running? or step_set.state_children_partial?
    step_set.steps.each do |step|
      draw_step(step, detailed)
    end
  end
end

.global_options(base) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/dopi/cli/global_options.rb', line 8

def self.global_options(base)
  base.class_eval do
    desc 'Use Hiera to get the role for the nodes'
    default_value DopCommon.config.use_hiera
    switch [:use_hiera, :h]

    desc 'Specify the hiera configuration file'
    default_value DopCommon.config.hiera_yaml
    arg_name 'YAML'
    flag [:hiera_yaml]

    desc 'Try to load the scope for the nodes from existing facts'
    default_value DopCommon.config.load_facts
    switch [:load_facts]

    desc 'Specify the directory where dopi can find facts'
    default_value DopCommon.config.facts_dir
    arg_name 'DIR'
    flag [:facts_dir]

    desc 'Set the name of the variable DOPi should use as the roles variable'
    default_value DopCommon.config.role_variable
    arg_name 'VARIABLE_NAME'
    flag [:role_variable]

    desc 'Set the default value for the node role'
    default_value DopCommon.config.role_default
    arg_name 'ROLE'
    flag [:role_default]

    desc 'Set the MCollective client configuration.'
    default_value DopCommon.config.mco_config
    arg_name 'FILE'
    flag [:mco_config]

    desc 'Use the DOPi logger to capture MCollective logs (this is enabled by default)'
    default_value DopCommon.config.mco_dopi_logger
    switch [:mco_dopi_logger]

    desc 'Time until a connection check is marked as failure'
    default_value DopCommon.config.connection_check_timeout
    arg_name 'SECONDS'
    flag [:connection_check_timeout]
  end
end


28
29
30
# File 'lib/dopi/cli/log.rb', line 28

def self.print_state(plan_name, detailed = false)
  puts state(plan_name, detailed)
end

.run_options(command) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
# File 'lib/dopi/cli/command_run.rb', line 4

def self.run_options(command)
  DopCommon::Cli.node_select_options(command)

  command.desc 'Show only stuff the run would do but don\'t execute commands (verify commands will still be executed)'
  command.default_value false
  command.switch [:noop, :n]

  command.desc 'Select the step set to run (if nothing is specified it will try to run the step set "default")'
  command.default_value 'default'
  command.arg_name 'STEPSET'
  command.flag [:step_set, :s]
end

.state(plan_name, detailed = false) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/dopi/cli/log.rb', line 8

def self.state(plan_name, detailed = false)
  plan = Dopi.show(plan_name)
  result = "[#{plan.state.to_s}] #{plan.name}\n"
  plan.step_sets.each do |step_set|
    result << "  [#{step_set.state.to_s}] #{step_set.name}\n"
    step_set.steps.each do |step|
      result << "    [#{step.state.to_s}] #{step.name}\n"
      if detailed or step.state_running? or step.state_children_partial?
        step.command_sets.each do |command_set|
          result << "      [#{command_set.state.to_s}] #{command_set.name}\n"
          command_set.commands.each do |command|
            result << "        [#{command.state.to_s}] #{command.title}\n"
          end
        end
      end
    end
  end
  return result
end

.str_state_color(state, string) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/dopi/cli/command_show.rb', line 54

def self.str_state_color(state, string)
  attr = case state
  when :ready then Curses.color_pair(3)
  when :done then Curses.color_pair(4)
  when :partial then Curses.color_pair(5)
  when :running, :started then Curses.color_pair(5) | Curses::A_BLINK
  when :failed then Curses.color_pair(6)
  else Curses.color_pair(2)
  end
  Curses.attrset(attr)
  Curses.addstr(string)
  Curses.attrset(Curses.color_pair(2))
end