Class: OsCtl::Cli::Bisect

Inherits:
Object
  • Object
show all
Defined in:
lib/osctl/cli/bisect.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cts, suspend_action: nil, cols: nil) ⇒ Bisect

Returns a new instance of Bisect.

Parameters:

  • cts (Array)
  • suspend_action (:freeze, :stop) (defaults to: nil)
  • cols (Array) (defaults to: nil)


8
9
10
11
12
13
14
# File 'lib/osctl/cli/bisect.rb', line 8

def initialize(cts, suspend_action: nil, cols: nil)
  @cts = cts
  @suspend_action = suspend_action
  @cols = cols
  @mutex = Mutex.new
  @last_manifested = true
end

Instance Attribute Details

#colsObject (readonly, protected)

Returns the value of attribute cols.



40
41
42
# File 'lib/osctl/cli/bisect.rb', line 40

def cols
  @cols
end

#ctsObject (readonly, protected)

Returns the value of attribute cts.



40
41
42
# File 'lib/osctl/cli/bisect.rb', line 40

def cts
  @cts
end

#mutexObject (readonly, protected)

Returns the value of attribute mutex.



40
41
42
# File 'lib/osctl/cli/bisect.rb', line 40

def mutex
  @mutex
end

#suspend_actionObject (readonly, protected)

Returns the value of attribute suspend_action.



40
41
42
# File 'lib/osctl/cli/bisect.rb', line 40

def suspend_action
  @suspend_action
end

Instance Method Details

#action_reverse(action) ⇒ Object (protected)

Parameters:

  • action (:suspend, :resume)


178
179
180
# File 'lib/osctl/cli/bisect.rb', line 178

def action_reverse(action)
  action == :suspend ? :resume : :suspend
end

#action_str(action) ⇒ Object (protected)



182
183
184
185
186
187
188
189
190
# File 'lib/osctl/cli/bisect.rb', line 182

def action_str(action)
  if suspend_action == :freeze
    action == :suspend ? 'freeze' : 'thaw'
  elsif suspend_action == :stop
    action == :suspend ? 'stop' : 'start'
  else
    raise "invalid action '#{suspend_action}'"
  end
end

#ask_confirmation!Object (protected)



97
98
99
100
101
102
103
104
105
106
# File 'lib/osctl/cli/bisect.rb', line 97

def ask_confirmation!
  $stdout.write('Continue? [y/N]: ')
  $stdout.flush

  unless %w[y yes].include?($stdin.readline.strip.downcase)
    raise 'Aborted'
  end

  puts
end

#ask_success?Boolean (protected)

Returns:

  • (Boolean)


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/osctl/cli/bisect.rb', line 108

def ask_success?
  loop do
    $stdout.write('Is the issue manifesting? [y/n]: ')
    $stdout.flush

    s = $stdin.readline.strip.downcase
    ret = nil

    # rubocop:disable Style/NegatedIfElseCondition

    if %w[y yes].include?(s)
      ret = !@last_manifested ? true : false
      @last_manifested = true
    elsif %w[n no].include?(s)
      ret = @last_manifested ? true : false
      @last_manifested = false
    end

    # rubocop:enable Style/NegatedIfElseCondition

    puts

    return ret unless ret.nil?
  end
end

#bisectObject (protected)



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/osctl/cli/bisect.rb', line 42

def bisect
  ct_set = @cts.clone
  action = :suspend

  loop do
    if ct_set.size == 1
      execute_action_set(ct_set, :resume) if action == :resume

      ct = ct_set.first
      puts
      puts "Container identified: #{ct[:pool]}:#{ct[:id]}"
      break
    end

    left, right = ct_set.each_slice((ct_set.size / 2.0).round).to_a

    execute_action_set(left, action)
    puts

    if action == :suspend
      if ask_success?
        ct_set = left
        action = :resume
        execute_action_set(right, :resume)
      else
        ct_set = right
        action = :suspend
        execute_action_set(left, :resume)
        puts
      end

    elsif action == :resume
      if ask_success?
        ct_set = left
        action = :suspend
        execute_action_set(right, :resume)
      else
        ct_set = right
        action = :resume
        execute_action_set(left, :resume)
        puts
      end

    else
      raise 'programming error'
    end

    puts "Narrowed down to #{ct_set.size} containers"
  end
end

#execute_action_set(ct_set, action) ⇒ Object (protected)

Parameters:

  • ct_set (Array)
  • action (:suspend, :resume)


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/osctl/cli/bisect.rb', line 136

def execute_action_set(ct_set, action)
  queue = Queue.new
  ct_set.each_with_index { |ct, i| queue << [i + 1, ct] }
  n = ct_set.length

  osctl_action =
    if suspend_action == :freeze
      action == :suspend ? :ct_freeze : :ct_unfreeze
    elsif suspend_action == :stop
      action == :suspend ? :ct_stop : :ct_start
    else
      raise "invalid action '#{suspend_action}'"
    end

  threads = Etc.nprocessors.times.map do
    Thread.new do
      c = OsCtl::Client.new
      c.open

      loop do
        begin
          i, ct = queue.pop(true)
        rescue ThreadError
          break
        end

        resp = c.cmd_response(osctl_action, pool: ct[:pool], id: ct[:id])

        mutex.synchronize do
          puts "[#{i}/#{n}] #{action_str(action)} #{ct[:pool]}:#{ct[:id]} " \
               "... #{resp.ok? ? 'ok' : "error: #{resp.message}"}"
        end
      end

      c.close
    end
  end

  threads.each(&:join)
end


93
94
95
# File 'lib/osctl/cli/bisect.rb', line 93

def print_set(ct_set)
  OsCtl::Lib::Cli::OutputFormatter.print(ct_set, cols:, layout: :columns)
end

#resetObject



34
35
36
# File 'lib/osctl/cli/bisect.rb', line 34

def reset
  execute_action_set(cts, :resume)
end

#runObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/osctl/cli/bisect.rb', line 16

def run
  print_set(cts)
  puts
  puts "Selected containers: #{cts.length}"
  puts "Suspend action: #{suspend_action}"
  puts
  ask_confirmation!

  begin
    bisect
  rescue StandardError, Interrupt
    puts
    puts 'Resuming all affected containers...'
    reset
    raise
  end
end