Class: OsCtld::Container::StopHandler

Inherits:
Object
  • Object
show all
Includes:
OsCtl::Lib::Utils::Exception, OsCtl::Lib::Utils::Log
Defined in:
lib/osctld/container/stop_handler.rb

Overview

The same exit cleanup for console-managed and direct LXC starts.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ct, run_conf = nil) ⇒ StopHandler

Returns a new instance of StopHandler.



16
17
18
19
# File 'lib/osctld/container/stop_handler.rb', line 16

def initialize(ct, run_conf = nil)
  @ct = ct
  @run_conf = run_conf
end

Instance Attribute Details

#ct ⇒ Object (readonly)

Returns the value of attribute ct.



14
15
16
# File 'lib/osctld/container/stop_handler.rb', line 14

def ct
  @ct
end

Class Method Details

.schedule(ct, run_conf = nil) ⇒ Object



9
10
11
12
# File 'lib/osctld/container/stop_handler.rb', line 9

def self.schedule(ct, run_conf = nil)
  thread = Thread.new { new(ct, run_conf).run }
  ThreadReaper.add(thread, nil)
end

Instance Method Details

#handle_ct_stop(ctrc) ⇒ Object (protected)



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/osctld/container/stop_handler.rb', line 81

def handle_ct_stop(ctrc)
  if ctrc.aborted?
    log(:info, ctrc, 'Container was aborted, performing cleanup')
    recovery = Container::Recovery.new(ctrc.ct)
    recovery.cleanup_or_taint
  end

  if !ct.ephemeral? && !ctrc.destroy_dataset_on_stop? && Daemon.get.config.writeout_dirtied_pages?
    # Force write-out of dirtied pages
    force_mount = false

    begin
      ct.unmount(force: true)
    rescue SystemCommandFailed => e
      log(:warn, ctrc, "Unable to unmount dataset for writeback: #{e.message}")
      force_mount = true
    end

    ct.mount(force: force_mount)
  end

  if ctrc.destroy_dataset_on_stop?
    GarbageCollector.free_container_run_dataset(ctrc, ctrc.dataset)
  end

  ctrc.fulfil_exit

  if ctrc.reboot?
    sleep(1)
    reboot_ct

  elsif ctrc.aborted?
    nil

  elsif ct.ephemeral? && !ct.is_being_manipulated?
    Commands::Container::Delete.run(
      pool: ct.pool.name,
      id: ct.id,
      force: true,
      manipulation_lock: 'wait'
    )
  end
end

#handle_improper_ct_stop ⇒ Object (protected)



74
75
76
77
78
79
# File 'lib/osctld/container/stop_handler.rb', line 74

def handle_improper_ct_stop
  # In this scenario, it is possible that the veth-down hooks weren't
  # run either. Use the same all-record preflight and durable taint gate as
  # forced deletion; never perform an unchecked direct teardown.
  Container::Recovery.new(ct).cleanup_or_taint
end

#reboot_ct ⇒ Object (protected)



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/osctld/container/stop_handler.rb', line 125

def reboot_ct
  ct.pool.request_reboot(ct)

  until ct.pool.imported?
    log(:info, ct, 'Waiting for pool import to reboot')
    sleep(1)
  end

  begin
    ret = Commands::Container::Start.run(
      pool: ct.pool.name,
      id: ct.id,
      manipulation_lock: 'wait'
    )
  rescue CommandFailed => e
    log(:warn, ct, "Reboot failed: #{e.message}")
  else
    if !ret.is_a?(Hash)
      log(:warn, ct, 'Reboot failed: reason unknown')
    elsif !ret[:status]
      log(:warn, ct, "Reboot failed: #{ret[:message]}")
    end
  end
end

#run ⇒ Object



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
64
65
66
67
68
69
70
# File 'lib/osctld/container/stop_handler.rb', line 21

def run
  # The TTY may have closed due to an unforeseen error, check if the
  # container is actually stopped.
  60.times do
    break if ct.state == :stopped

    log(:info, ct, 'Exit handler waiting for stopped state')
    sleep(1)
  end

  unless ct.state == :stopped
    log(:fatal, ct, 'Exit handler ran, but container is not stopped')
    return
  end

  ctrc = @run_conf || ct.get_past_run_conf
  return if @run_conf && !ct.get_past_run_conf.equal?(@run_conf)
  return if ctrc && !ctrc.claim_exit_handling

  CpuScheduler.unschedule_ct(ct)

  begin
    ct.update_hints
  rescue Exception => e # rubocop:disable Lint/RescueException
    log(:warn, ct, "Unable to update hints: #{e.message} (#{e.class})")
    log(:warn, ct, denixstorify(e.backtrace))
  end

  if ctrc.nil?
    # This means that {UserControl::Commands::CtPostStop} hasn't run for some
    # reason.
    log(:fatal, ct, 'Unable to properly handle container stop')
    handle_improper_ct_stop
    return
  end

  # Send events about halt/reboot from the inside
  if !ctrc.aborted? && !ct.is_being_manipulated?
    Eventd.report(
      :ct_exit,
      pool: ct.pool.name,
      id: ct.id,
      exit_type: ctrc.reboot? ? 'reboot' : 'halt'
    )
  end

  handle_ct_stop(ctrc)

  ct.forget_past_run_conf(ctrc)
end