Class: OsCtld::TrashBin

Inherits:
Object
  • Object
show all
Includes:
OsCtl::Lib::Utils::Log, OsCtl::Lib::Utils::System
Defined in:
lib/osctld/trash_bin.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pool) ⇒ TrashBin

Returns a new instance of TrashBin.

Parameters:



19
20
21
22
23
24
# File 'lib/osctld/trash_bin.rb', line 19

def initialize(pool)
  @pool = pool
  @trash_dataset = OsCtl::Lib::Zfs::Dataset.new(pool.trash_bin_ds)
  @queue = OsCtl::Lib::Queue.new
  @stop = false
end

Instance Attribute Details

#poolPool (readonly)

Returns:



16
17
18
# File 'lib/osctld/trash_bin.rb', line 16

def pool
  @pool
end

Class Method Details

.add_dataset(pool, dataset) ⇒ Object

Parameters:

  • pool (Pool)
  • dataset (OsCtl::Lib::Zfs::Dataset)


8
9
10
# File 'lib/osctld/trash_bin.rb', line 8

def self.add_dataset(pool, dataset)
  pool.trash_bin.add_dataset(dataset)
end

Instance Method Details

#add_dataset(dataset) ⇒ Object

Parameters:

  • dataset (OsCtl::Lib::Zfs::Dataset)


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
# File 'lib/osctld/trash_bin.rb', line 49

def add_dataset(dataset)
  # Set canmount on the dataset and umount it with and all its descendants
  dataset.list.reverse_each do |ds|
    zfs(:set, 'canmount=noauto', ds.name)

    # We ignore errors, because the dataset may belong to
    # a hung container, etc.
    begin
      zfs(:unmount, nil, ds.name)
    rescue SystemCommandFailed => e
      unless e.output.include?('not currently mounted')
        log(:warn, "Unable to unmount #{ds}: #{e.message}")
      end
    end
  end

  # Move the dataset to trash
  trash_ds, t = trash_path(dataset)
  zfs(:rename, nil, "#{dataset} #{trash_ds}")

  # Set metadata properties
  meta = {
    original_name: dataset.name,
    trashed_at: t.to_i
  }

  zfs(
    :set,
    meta.map { |k, v| "org.vpsadminos.osctl.trash-bin:#{k}=#{v}" }.join(' '),
    trash_ds
  )
end

#log_typeObject



82
83
84
# File 'lib/osctld/trash_bin.rb', line 82

def log_type
  "#{pool.name}:trash"
end

#pruneObject



44
45
46
# File 'lib/osctld/trash_bin.rb', line 44

def prune
  @queue << :prune
end

#prune_datasetsObject (protected)



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/osctld/trash_bin.rb', line 98

def prune_datasets
  txg_timeout = File.read('/sys/module/zfs/parameters/zfs_txg_timeout').strip.to_i

  @trash_dataset.list(depth: 1, include_self: false).each do |ds|
    break if @stop

    unless ds.name.start_with?("#{@trash_dataset}/")
      raise "programming error: refusing to destroy dataset #{ds.name.inspect}"
    end

    log(:info, "Destroying #{ds}")

    begin
      ds.destroy!(recursive: true)
    rescue SystemCommandFailed => e
      log(:warn, "Unable to destroy #{ds}: #{e.message}")
      next
    end

    break if @stop

    sleep([txg_timeout, 5].max)
  end
end

#run_gcObject (protected)



88
89
90
91
92
93
94
95
96
# File 'lib/osctld/trash_bin.rb', line 88

def run_gc
  loop do
    v = @queue.pop(timeout: Daemon.get.config.trash_bin.prune_interval)
    return if v == :stop

    log(:info, 'Pruning')
    prune_datasets
  end
end

#startObject



26
27
28
29
# File 'lib/osctld/trash_bin.rb', line 26

def start
  @stop = false
  @thread = Thread.new { run_gc }
end

#started?Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/osctld/trash_bin.rb', line 40

def started?
  !@stop
end

#stopObject



31
32
33
34
35
36
37
38
# File 'lib/osctld/trash_bin.rb', line 31

def stop
  return unless @thread

  @stop = true
  @queue << :stop
  @thread.join
  @thread = nil
end

#trash_path(dataset) ⇒ Object (protected)



123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/osctld/trash_bin.rb', line 123

def trash_path(dataset)
  t = Time.now

  path = File.join(
    @trash_dataset.name,
    [
      dataset.name.split('/')[1..].join('-'),
      t.to_i,
      SecureRandom.hex(3)
    ].join('.')
  )

  [path, t]
end