bareos-restore 1.0.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.
- checksums.yaml +7 -0
- data/lib/bareos-restore.rb +268 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d77f4b6e3042c4e2453c5ed4ab9842d62da720b161024298bb0c3394daf2f931
|
4
|
+
data.tar.gz: 127cc098dd8b2002d504fbaa7cb82fcf9c2f8d4970488cff8e2cbf62494b1133
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b8baad71dfec5b5107ff71141e181d22405f87203e7184810b95555d630f3c88bf45fcb3c0b52bcd47cea0156e763100da4a2ce0be878ceef33066d570db2efc
|
7
|
+
data.tar.gz: ff2989fea490a11247003dfebc343226caf305d018889bf24da73da0f04a1f91f56b640419a3eb94db68b22b7a33ba228bee828ab4b934da0f12b27105fa7355
|
@@ -0,0 +1,268 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# todo:
|
4
|
+
# - bareos-job abbrechen, wenn SIGINT kommt
|
5
|
+
|
6
|
+
# Prerequisites:
|
7
|
+
# - apt install ruby-sys-filesystem
|
8
|
+
|
9
|
+
require 'open3'
|
10
|
+
require 'json'
|
11
|
+
require 'pty'
|
12
|
+
require 'expect'
|
13
|
+
require 'time'
|
14
|
+
require 'logger'
|
15
|
+
|
16
|
+
# Bareos bconsole configuration
|
17
|
+
BCONSOLE_PATH = '/usr/sbin/bconsole'
|
18
|
+
|
19
|
+
JOBSTATUS = {
|
20
|
+
C: {
|
21
|
+
text: 'Created, not yet running',
|
22
|
+
pending: true,
|
23
|
+
running: false,
|
24
|
+
failed: false
|
25
|
+
},
|
26
|
+
R: {
|
27
|
+
text: 'Running',
|
28
|
+
pending: false,
|
29
|
+
running: true,
|
30
|
+
failed: false
|
31
|
+
},
|
32
|
+
B: {
|
33
|
+
text: 'Blocked',
|
34
|
+
pending: true,
|
35
|
+
running: true,
|
36
|
+
failed: false
|
37
|
+
},
|
38
|
+
T: {
|
39
|
+
text: 'Completed successfully',
|
40
|
+
pending: false,
|
41
|
+
running: false,
|
42
|
+
failed: false
|
43
|
+
},
|
44
|
+
E: {
|
45
|
+
text: 'Terminated with errors',
|
46
|
+
pending: false,
|
47
|
+
running: false,
|
48
|
+
failed: true
|
49
|
+
},
|
50
|
+
e: {
|
51
|
+
text: 'Non-fatal error',
|
52
|
+
pending: false,
|
53
|
+
running: false,
|
54
|
+
failed: false
|
55
|
+
},
|
56
|
+
f: {
|
57
|
+
text: 'Fatal error',
|
58
|
+
pending: false,
|
59
|
+
running: false,
|
60
|
+
failed: true
|
61
|
+
},
|
62
|
+
D: {
|
63
|
+
text: 'Verify found differences',
|
64
|
+
pending: false,
|
65
|
+
running: false,
|
66
|
+
failed: true
|
67
|
+
},
|
68
|
+
A: {
|
69
|
+
text: 'Cancelled by user',
|
70
|
+
pending: false,
|
71
|
+
running: false,
|
72
|
+
failed: true
|
73
|
+
},
|
74
|
+
I: {
|
75
|
+
text: 'Incomplete job',
|
76
|
+
pending: false,
|
77
|
+
running: false,
|
78
|
+
failed: true
|
79
|
+
},
|
80
|
+
L: {
|
81
|
+
text: 'Comitting data',
|
82
|
+
pending: false,
|
83
|
+
running: true,
|
84
|
+
failed: false
|
85
|
+
},
|
86
|
+
W: {
|
87
|
+
text: 'Terminated with warnings',
|
88
|
+
pending: false,
|
89
|
+
running: false,
|
90
|
+
failed: false
|
91
|
+
},
|
92
|
+
l: {
|
93
|
+
text: 'Doing data despooling',
|
94
|
+
pending: false,
|
95
|
+
running: true,
|
96
|
+
failed: false
|
97
|
+
},
|
98
|
+
q: {
|
99
|
+
text: 'Queued waiting for device',
|
100
|
+
pending: true,
|
101
|
+
running: false,
|
102
|
+
failed: false
|
103
|
+
},
|
104
|
+
F: {
|
105
|
+
text: 'Waiting for client',
|
106
|
+
pending: true,
|
107
|
+
running: false,
|
108
|
+
failed: false
|
109
|
+
},
|
110
|
+
S: {
|
111
|
+
text: 'Waiting for storage daemon',
|
112
|
+
pending: true,
|
113
|
+
running: true,
|
114
|
+
failed: false
|
115
|
+
},
|
116
|
+
m: {
|
117
|
+
text: 'Waiting for new media',
|
118
|
+
pending: true,
|
119
|
+
running: true,
|
120
|
+
failed: false
|
121
|
+
},
|
122
|
+
M: {
|
123
|
+
text: 'Waiting for media mount',
|
124
|
+
pending: true,
|
125
|
+
running: true,
|
126
|
+
failed: false
|
127
|
+
},
|
128
|
+
s: {
|
129
|
+
text: 'Waiting for storage resource',
|
130
|
+
pending: true,
|
131
|
+
running: true,
|
132
|
+
failed: false
|
133
|
+
},
|
134
|
+
j: {
|
135
|
+
text: 'Waiting for job resource',
|
136
|
+
pending: true,
|
137
|
+
running: true,
|
138
|
+
failed: false
|
139
|
+
},
|
140
|
+
c: {
|
141
|
+
text: 'Waiting for client resource',
|
142
|
+
pending: true,
|
143
|
+
running: true,
|
144
|
+
failed: false
|
145
|
+
},
|
146
|
+
d: {
|
147
|
+
text: 'Waiting on maximum jobs',
|
148
|
+
pending: true,
|
149
|
+
running: true,
|
150
|
+
failed: false
|
151
|
+
},
|
152
|
+
t: {
|
153
|
+
text: 'Waiting on start time',
|
154
|
+
pending: true,
|
155
|
+
running: true,
|
156
|
+
failed: false
|
157
|
+
},
|
158
|
+
p: {
|
159
|
+
text: 'Waiting on higher priority jobs',
|
160
|
+
pending: true,
|
161
|
+
running: true,
|
162
|
+
failed: false
|
163
|
+
},
|
164
|
+
i: {
|
165
|
+
text: 'Doing batch insert file records',
|
166
|
+
pending: false,
|
167
|
+
running: true,
|
168
|
+
failed: false
|
169
|
+
},
|
170
|
+
a: {
|
171
|
+
text: 'Despooling attributes',
|
172
|
+
pending: false,
|
173
|
+
running: true,
|
174
|
+
failed: false
|
175
|
+
}
|
176
|
+
}.freeze
|
177
|
+
|
178
|
+
module BareosRestore
|
179
|
+
$logger = Logger.new(STDOUT)
|
180
|
+
$logger.level = Logger::INFO
|
181
|
+
#$logger.level = Logger::DEBUG
|
182
|
+
|
183
|
+
# Function to run a command in bconsole
|
184
|
+
# Sends input commands to bconsole and captures the output
|
185
|
+
# apilevel=1 -> Text-based output
|
186
|
+
# apilevel=2 -> JSON-based output
|
187
|
+
def self.bconsole(command, apilevel: 2)
|
188
|
+
bconsole_cmd = ".api #{apilevel}\n#{command}\n"
|
189
|
+
output = nil
|
190
|
+
|
191
|
+
$logger.debug "Running #{bconsole_cmd}"
|
192
|
+
|
193
|
+
IO.popen(BCONSOLE_PATH, 'r+') do |io|
|
194
|
+
io.puts(bconsole_cmd) # Pipe the input to the command
|
195
|
+
io.close_write # Close the write stream to signal EOF
|
196
|
+
output = io.read # Read the output from the command
|
197
|
+
end
|
198
|
+
|
199
|
+
# Get the last block of { … }
|
200
|
+
output = output.scan(/(^{$.+?^})/m).last
|
201
|
+
|
202
|
+
if apilevel == 1
|
203
|
+
output
|
204
|
+
elsif apilevel == 2
|
205
|
+
# Parse from JSON and isolate the result sub-structure
|
206
|
+
JSON.parse(::Regexp.last_match(1))['result']
|
207
|
+
end
|
208
|
+
rescue JSON::ParserError => e
|
209
|
+
raise "Error parsing JSON: #{e.message}"
|
210
|
+
end
|
211
|
+
|
212
|
+
def self.latest_jobid(backup_name)
|
213
|
+
$logger.info "Getting latest job ID for backup #{backup_name}"
|
214
|
+
$logger.info "list jobs job=#{backup_name} jobstatus=T jobtype=B last"
|
215
|
+
output = bconsole "list jobs job=#{backup_name} jobstatus=T jobtype=B last"
|
216
|
+
$logger.debug output.inspect
|
217
|
+
raise "No successful backup job found." unless output['jobs'].length>0
|
218
|
+
output['jobs'].first['jobid']
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.get_joblog(job_id)
|
222
|
+
$logger.info "Getting job log for job #{job_id}"
|
223
|
+
output = bconsole "list joblog jobid=#{job_id}"
|
224
|
+
textblock = []
|
225
|
+
output['joblog'].each do |entry|
|
226
|
+
textblock << "#{entry['time']} #{entry['logtext']}"
|
227
|
+
end
|
228
|
+
$logger.info "JOBLOG…\n#{textblock.join("\n")}"
|
229
|
+
end
|
230
|
+
|
231
|
+
# Function to initiate restore and wait for job completion
|
232
|
+
def self.perform_restore(job_id, restore_dir, bareos_client)
|
233
|
+
$logger.info "Gathering Full/Diff/Incr jobs to get a complete restore of Job ID #{job_id}"
|
234
|
+
output = bconsole ".bvfs_get_jobids jobid=#{job_id}"
|
235
|
+
all_jobs = output['jobids'].map { |x| x['id'] }.join(',')
|
236
|
+
|
237
|
+
$logger.info "These jobs are needed apparently: #{all_jobs}"
|
238
|
+
|
239
|
+
$logger.info 'Initiating restore'
|
240
|
+
output = bconsole "restore client=#{bareos_client} where=#{restore_dir} restorejob=RestoreFiles jobid=#{all_jobs} all done yes"
|
241
|
+
|
242
|
+
restore_job = output['run']['jobid']
|
243
|
+
$logger.info "Restore job queued. ID=#{restore_job}"
|
244
|
+
|
245
|
+
loop do
|
246
|
+
output = bconsole "list jobid=#{restore_job}"
|
247
|
+
$logger.debug output
|
248
|
+
jobstatus = JOBSTATUS[output['jobs'].first['jobstatus'].to_sym]
|
249
|
+
# puts "Status: #{jobstatus}"
|
250
|
+
# $logger.info "Status: #{jobstatus} (#{jobstatus[:text]})"
|
251
|
+
|
252
|
+
if jobstatus[:failed]
|
253
|
+
$logger.error 'Job failed.'
|
254
|
+
get_joblog(restore_job)
|
255
|
+
exit 10
|
256
|
+
elsif jobstatus[:running]
|
257
|
+
$logger.info 'Job running.'
|
258
|
+
elsif jobstatus[:pending]
|
259
|
+
$logger.info 'Job pending.'
|
260
|
+
else
|
261
|
+
$logger.info 'Job finished.'
|
262
|
+
get_joblog(restore_job)
|
263
|
+
break
|
264
|
+
end
|
265
|
+
sleep 5
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bareos-restore
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christoph Haas
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-04-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sys-filesystem
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.4'
|
27
|
+
description: |2
|
28
|
+
Automatically restore a Bareos Backup. Useful for automated restore tests.
|
29
|
+
This simple module cannot do more than that.
|
30
|
+
email: rubygems@christoph-haas.de
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- lib/bareos-restore.rb
|
36
|
+
homepage: https://rubygems.org/gems/bareosrestore
|
37
|
+
licenses:
|
38
|
+
- MIT
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubygems_version: 3.4.20
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: Restore a Bareos backup
|
59
|
+
test_files: []
|