rbarman 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +20 -8
- data/lib/rbarman/backup.rb +122 -12
- data/lib/rbarman/backups.rb +17 -2
- data/lib/rbarman/cli_command.rb +170 -11
- data/lib/rbarman/configuration.rb +29 -0
- data/lib/rbarman/server.rb +83 -0
- data/lib/rbarman/string.rb +7 -0
- data/lib/rbarman/version.rb +1 -1
- data/lib/rbarman/wal_file.rb +58 -2
- data/lib/rbarman/wal_files.rb +22 -0
- data/lib/rbarman.rb +4 -0
- metadata +8 -4
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# rbarman
|
2
2
|
|
3
|
-
rbarman - Ruby Wrapper for 2ndQuadrant's PostgreSQL backup tool [
|
3
|
+
rbarman - Ruby Wrapper for 2ndQuadrant's PostgreSQL backup tool [barman](http://pgbarman.org)
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
|
7
|
+
barman has to be installed and configured on the same host where this gem is to be used, otherwise it cannot get any useful information about your backups ;)
|
8
8
|
|
9
9
|
|
10
10
|
Add this line to your application's Gemfile:
|
@@ -21,20 +21,32 @@ Or install it yourself as:
|
|
21
21
|
|
22
22
|
## Usage
|
23
23
|
|
24
|
+
Just a few examples..
|
25
|
+
|
24
26
|
### Get all your backups!
|
25
27
|
|
26
|
-
This will call
|
28
|
+
This will call various barman commands and some other sources (backup.info, xlog.db) to get information about your backups and could take several minutes (and memory) if you have many backups with thousand of wal files.
|
27
29
|
|
28
30
|
<pre>
|
29
|
-
|
30
|
-
|
31
|
+
servers = RBarman::Servers.all({:with_backups => true, :with_wal_files => true })
|
32
|
+
servers.count
|
31
33
|
=> 3
|
32
34
|
|
33
|
-
|
35
|
+
servers[0].name
|
36
|
+
=> "pgmaster"
|
37
|
+
|
38
|
+
servers[0].ssh_cmd
|
39
|
+
=> "ssh postgres@10.118.19.4"
|
40
|
+
|
41
|
+
servers[0].backups.count
|
42
|
+
=> 2
|
43
|
+
|
44
|
+
servers[0].backups.each { |b| p "id: #{b.id} }
|
34
45
|
=> "id: 20130304T080002"
|
35
46
|
=> "id: 20130225T192654"
|
36
47
|
=> "id: 20130218T080002"
|
37
48
|
|
49
|
+
backups = servers[0].backups
|
38
50
|
backups.latest.id
|
39
51
|
=> "20130304T080002"
|
40
52
|
|
@@ -75,7 +87,7 @@ backups[0].wal_files[1022].compression
|
|
75
87
|
### Get just one backup without wal files
|
76
88
|
|
77
89
|
<pre>
|
78
|
-
backup = RBarman::Backup.by_id('
|
90
|
+
backup = RBarman::Backup.by_id('pg_master', '20130225T192654')
|
79
91
|
p "id: #{backup.id}|size: #{backup.size / (1024 ** 3) } GB|wal size: #{backup.wal_file_size / (1024 ** 3)} GB"
|
80
92
|
=> "id: 20130225T192654|size: 217GB|wal size: 72 GB"
|
81
93
|
</pre>
|
@@ -95,7 +107,7 @@ p b.id
|
|
95
107
|
This tells pgbarman to delete the specified backup
|
96
108
|
|
97
109
|
<pre>
|
98
|
-
backup = RBarman::Backup.by_id('server', '20130225T192654', false)
|
110
|
+
backup = RBarman::Backup.by_id('server', '20130225T192654', { :with_wal_files => false })
|
99
111
|
p backup.deleted
|
100
112
|
=> false
|
101
113
|
backup.delete
|
data/lib/rbarman/backup.rb
CHANGED
@@ -1,29 +1,120 @@
|
|
1
1
|
require 'time'
|
2
2
|
|
3
|
+
# @author Holger Amann <holger@sauspiel.de>
|
3
4
|
module RBarman
|
4
5
|
|
5
6
|
class InvalidBackupIdError < RuntimeError
|
6
7
|
end
|
7
8
|
|
9
|
+
|
10
|
+
# Represents a barman Backup
|
8
11
|
class Backup
|
12
|
+
|
13
|
+
# @return [String, nil] name of the server to which the backup belongs.
|
9
14
|
attr_accessor :server
|
10
|
-
|
15
|
+
|
16
|
+
# @overload id
|
17
|
+
# @return [String, nil] id (like '20130304T080002') which identifies
|
18
|
+
# the backup and is unique among all backups of a server.
|
19
|
+
# @overload id=
|
20
|
+
# Id of the backup
|
21
|
+
# @param [#to_s] id id of the backup
|
22
|
+
# @raise [InvalidBackupIdError] if the id is not valid
|
23
|
+
# @see backup_id_valid?
|
24
|
+
attr_reader :id
|
25
|
+
|
26
|
+
# @overload backup_start
|
27
|
+
# @return [Time, nil] time when the backup started.
|
28
|
+
# @overload backup_start=
|
29
|
+
# Start of the backup
|
30
|
+
# @param [#to_s] start time when the backup started
|
31
|
+
attr_reader :backup_start
|
32
|
+
|
33
|
+
# @overload backup_end
|
34
|
+
# @return [Time, nil] time when the backup stopped.
|
35
|
+
# @overload backup_end=
|
36
|
+
# End of the backup
|
37
|
+
# @param [#to_s] b_end time when the backup stopped
|
38
|
+
attr_reader :backup_end
|
39
|
+
|
40
|
+
# @overload status
|
41
|
+
# @return [Symbol, nil] status of the backup, `:empty`, `:started`, `:done` or `:failed`
|
42
|
+
# @overload status=
|
43
|
+
# Status of the backup
|
44
|
+
# @param [Symbol] status status of the backup
|
45
|
+
# @raise [ArgumentError] if status is not one of `:empty`, `:started`, `:done` or `:failed`
|
46
|
+
attr_reader :status
|
47
|
+
|
48
|
+
# @overload wal_files
|
49
|
+
# @return [WalFiles, nil] All wal files
|
50
|
+
# @overload wal_files=
|
51
|
+
# All wal files
|
52
|
+
# @param [Array, WalFiles] wal_files all wal files
|
53
|
+
# @raise [ArgumentError] if argument is not an Array
|
54
|
+
attr_reader :wal_files
|
55
|
+
|
56
|
+
# @overload size
|
57
|
+
# @return [Integer, nil] size of data (in bytes)
|
58
|
+
# @overload size=
|
59
|
+
# Size of data (in bytes)
|
60
|
+
# @param [#to_i] size size (in bytes
|
61
|
+
attr_reader :size
|
62
|
+
|
63
|
+
# @overload wal_file_size
|
64
|
+
# @return [Integer, nil] size of wal files (in bytes)
|
65
|
+
# @overload wal_file_size=
|
66
|
+
# Size of wal files (in bytes)
|
67
|
+
# @param [#to_i] size size of wal files (in bytes)
|
68
|
+
attr_reader :wal_file_size
|
69
|
+
|
70
|
+
# @overload begin_wal
|
71
|
+
# @return [WalFile, nil] first wal file after backup started
|
72
|
+
# @overload begin_wal=
|
73
|
+
# First wal file after backup started
|
74
|
+
# @param [String,WalFile] wal_file the wal file
|
75
|
+
attr_reader :begin_wal
|
76
|
+
|
77
|
+
# @overload end_wal
|
78
|
+
# @return [WalFile, nil] last wal file after backup stopped
|
79
|
+
# @overload end_wal=
|
80
|
+
# Last wal file after backup stopped
|
81
|
+
# @param [String,WalFile] wal_file the wal file
|
82
|
+
attr_reader :end_wal
|
83
|
+
|
84
|
+
# @overload timeline
|
85
|
+
# @return [Integer, nil] timeline of the backup.
|
86
|
+
# @overload timeline=
|
87
|
+
# Timeline of the backup
|
88
|
+
# @param [Integer] i timeline of the backup
|
89
|
+
# @raise [ArgumentError] if timeline == 0
|
90
|
+
attr_reader :timeline
|
91
|
+
|
92
|
+
# @overload pgdata
|
93
|
+
# @return [String, nil] server's data directory
|
94
|
+
# @overload pgdata=
|
95
|
+
# Server's data directory
|
96
|
+
# @param [String] path path to directory
|
97
|
+
# @raise [ArgumentError] if path is empty
|
98
|
+
attr_reader :pgdata
|
99
|
+
|
100
|
+
# @return [Boolean] if the backup has been deleted
|
101
|
+
attr_reader :deleted
|
11
102
|
|
12
103
|
def initialize
|
13
104
|
@deleted = false
|
14
105
|
end
|
15
106
|
|
16
107
|
def id=(id)
|
17
|
-
raise InvalidBackupIdError if !Backup.backup_id_valid?(id)
|
18
|
-
@id = id
|
108
|
+
raise InvalidBackupIdError if !Backup.backup_id_valid?(id.to_s)
|
109
|
+
@id = id.to_s
|
19
110
|
end
|
20
111
|
|
21
112
|
def backup_start=(start)
|
22
|
-
@backup_start = Time.parse(start)
|
113
|
+
@backup_start = Time.parse(start.to_s)
|
23
114
|
end
|
24
115
|
|
25
116
|
def backup_end=(b_end)
|
26
|
-
@backup_end = Time.parse(b_end)
|
117
|
+
@backup_end = Time.parse(b_end.to_s)
|
27
118
|
end
|
28
119
|
|
29
120
|
def status=(status)
|
@@ -54,7 +145,6 @@ module RBarman
|
|
54
145
|
@end_wal = WalFile.parse(wal_file)
|
55
146
|
end
|
56
147
|
|
57
|
-
|
58
148
|
def wal_file_size=(size)
|
59
149
|
@wal_file_size = size.to_i
|
60
150
|
end
|
@@ -69,38 +159,58 @@ module RBarman
|
|
69
159
|
@pgdata = path
|
70
160
|
end
|
71
161
|
|
162
|
+
# Adds a wal file to the backup
|
163
|
+
# @param [String, WalFile] wal_file the wal file
|
164
|
+
# @return [void]
|
72
165
|
def add_wal_file(wal_file)
|
73
|
-
@wal_files =
|
166
|
+
@wal_files = WalFiles.new if @wal_files.nil?
|
74
167
|
@wal_files << WalFile.parse(wal_file)
|
75
168
|
end
|
76
169
|
|
170
|
+
# @return [Boolean] if the wal file is already added to the backup
|
171
|
+
# @param [String, WalFile] wal_file the wal file
|
77
172
|
def wal_file_already_added?(wal_file)
|
78
173
|
return false if @wal_files.nil?
|
79
174
|
return @wal_files.include?(WalFile.parse(wal_file))
|
80
175
|
end
|
81
176
|
|
82
177
|
|
178
|
+
# @return [Boolean] if id is a valid backup id
|
179
|
+
# @param [#to_s] id the backup id
|
180
|
+
# @example Check if a backup id is valid
|
181
|
+
# Backup.backup_id_valid?("20130304T080002") #=> true
|
182
|
+
# Backup.backup_id_valid?("213") #=> false
|
83
183
|
def self.backup_id_valid?(id)
|
84
184
|
return false if id.nil?
|
85
|
-
return !id.match(/\d{8,8}T\d{6,6}/).nil?
|
185
|
+
return !id.to_s.match(/\d{8,8}T\d{6,6}/).nil?
|
86
186
|
end
|
87
187
|
|
188
|
+
# Instructs the underlying (barman) command to delete the backup and sets its flag {#deleted} to true
|
88
189
|
def delete
|
89
190
|
cmd = CliCommand.new
|
90
191
|
cmd.delete(@server, @id)
|
91
192
|
@deleted = true
|
92
193
|
end
|
93
194
|
|
195
|
+
# Instructs the underlying (barman) command to create a new backup.
|
196
|
+
# @param [String] server server name for which a backup should be created
|
197
|
+
# @return [Backup] a new backup object with wal files
|
94
198
|
def self.create(server)
|
95
199
|
cmd = CliCommand.new
|
96
200
|
cmd.create(server)
|
97
|
-
backups = Backups.all(server, false)
|
98
|
-
return Backup.by_id(server, backups.latest.id, true)
|
201
|
+
backups = Backups.all(server, { :with_wal_files => false })
|
202
|
+
return Backup.by_id(server, backups.latest.id, { :with_wal_files => true })
|
99
203
|
end
|
100
204
|
|
101
|
-
|
205
|
+
# Get a specific backup
|
206
|
+
# @param [String] server server name
|
207
|
+
# @param [String] backup_id id of the backup
|
208
|
+
# @param [Hash] opts options for creating a {Backup}
|
209
|
+
# @option opts [Boolean] :with_wal_files whether to include {WalFiles} in {Backup}
|
210
|
+
# @return [Backup] the backup
|
211
|
+
def self.by_id(server, backup_id, opts = {})
|
102
212
|
cmd = CliCommand.new
|
103
|
-
return cmd.backup(server, backup_id,
|
213
|
+
return cmd.backup(server, backup_id, opts)
|
104
214
|
end
|
105
215
|
end
|
106
216
|
end
|
data/lib/rbarman/backups.rb
CHANGED
@@ -1,18 +1,33 @@
|
|
1
|
+
# @author Holger Amann <holger@sauspiel.de
|
1
2
|
module RBarman
|
3
|
+
|
4
|
+
# An array of {Backup}
|
2
5
|
class Backups < Array
|
6
|
+
|
7
|
+
# Initializes a new Array of {Backup}
|
8
|
+
# @param [Array,Backups] other appends all backups from another array
|
3
9
|
def initialize(other=nil)
|
4
10
|
self.concat(other) if !other.nil? and other.is_a? Array
|
5
11
|
end
|
6
12
|
|
7
|
-
|
13
|
+
# Instructs the underlying (barman) command to get all backups for a specific server
|
14
|
+
# @param [String] server server name
|
15
|
+
# @param [Hash] opts options for creating {Backups}
|
16
|
+
# @option opts [Boolean] :with_wal_files whether to include {WalFiles}
|
17
|
+
# @return [Backups] an array of {Backup}
|
18
|
+
def self.all(server, opts={})
|
8
19
|
cmd = CliCommand.new
|
9
|
-
return Backups.new(cmd.backups(server,
|
20
|
+
return Backups.new(cmd.backups(server, opts))
|
10
21
|
end
|
11
22
|
|
23
|
+
# Get the latest (newest) backup of all backups in the array
|
24
|
+
# @return [Backup] the latest {Backup}
|
12
25
|
def latest
|
13
26
|
self.sort_by { |d| Time.parse(d.id) }.reverse.first
|
14
27
|
end
|
15
28
|
|
29
|
+
# Get the oldest backup of all backups in the array
|
30
|
+
# @return [Backup] the oldest {Backup}
|
16
31
|
def oldest
|
17
32
|
self.sort_by { |d| Time.parse(d.id) }.first
|
18
33
|
end
|
data/lib/rbarman/cli_command.rb
CHANGED
@@ -1,9 +1,30 @@
|
|
1
1
|
require 'mixlib/shellout'
|
2
2
|
|
3
|
+
# @author Holger Amann <holger@sauspiel.de>
|
3
4
|
module RBarman
|
5
|
+
|
6
|
+
# Wrapper for the barman command line tool
|
4
7
|
class CliCommand
|
5
|
-
attr_reader :binary, :barman_home
|
6
8
|
|
9
|
+
# @overload binary
|
10
|
+
# @return [String] path to the barman binary
|
11
|
+
# @overload binary=
|
12
|
+
# Path to the barman binary
|
13
|
+
# @param [String] path path to the binary
|
14
|
+
# @raise [ArgumentError] if path doesn't exist or path doesn't end with 'barman'
|
15
|
+
attr_reader :binary
|
16
|
+
|
17
|
+
# @overload barman_home
|
18
|
+
# @return [String] base path where barman stores its backups
|
19
|
+
# @overload barman_home=
|
20
|
+
# Path to the base directory of barman's backups
|
21
|
+
# @param [String] path path to the base directory
|
22
|
+
# @raise [ArgumentError] if path doesn't exist
|
23
|
+
attr_reader :barman_home
|
24
|
+
|
25
|
+
# Creates a new instance of CliCommand
|
26
|
+
# @param [String] path_to_binary see {#binary}. If nil, it will be initialized from {Configuration}
|
27
|
+
# @param [String] path_to_barman_home see {#barman_home}. If nil, it will be initialized from {Configuration}
|
7
28
|
def initialize(path_to_binary=nil, path_to_barman_home=nil)
|
8
29
|
self.binary=path_to_binary || Configuration.instance.binary
|
9
30
|
self.barman_home=path_to_barman_home || Configuration.instance.barman_home
|
@@ -20,19 +41,34 @@ module RBarman
|
|
20
41
|
@barman_home = path
|
21
42
|
end
|
22
43
|
|
23
|
-
|
44
|
+
# Instructs barman to get information about a specific backup
|
45
|
+
# @param [String] server server name
|
46
|
+
# @param [String] backup_id id of the backup
|
47
|
+
# @param [Hash] opts options for creating a {Backup}
|
48
|
+
# @option opts [Boolean] :with_wal_files whether to include {WalFiles} in each {Backup}
|
49
|
+
# @return [Backup] a new {Backup} object
|
50
|
+
# @raise [ArgumentError] if backup_id is nil
|
51
|
+
def backup(server, backup_id, opts = {})
|
24
52
|
raise(ArgumentError, "backup id must not be nil!") if backup_id.nil?
|
25
|
-
|
53
|
+
opts[:backup_id] = backup_id
|
54
|
+
return backups(server, opts)[0]
|
26
55
|
end
|
27
56
|
|
28
|
-
|
57
|
+
# Instructs barman to get information about backups
|
58
|
+
# @param [String] server server name
|
59
|
+
# @param [String] backup_id when given, only information about this backup id will be retrieved
|
60
|
+
# @param [Hash] opts options for creating {Backups}
|
61
|
+
# @option opts [Boolean] :with_wal_files whether to include {WalFiles} in each {Backup}
|
62
|
+
# @option opts [String] :backup_id retrieve just one {Backup} specified by this backup id
|
63
|
+
# @return [Backups] an array of {Backup}
|
64
|
+
def backups(server, opts = {})
|
29
65
|
list = run_barman_command("list-backup #{server}")
|
30
|
-
list = list.grep(/#{backup_id}/) if !backup_id.nil?
|
66
|
+
list = list.grep(/#{opts[:backup_id]}/) if !opts[:backup_id].nil?
|
31
67
|
|
32
68
|
backups = parse_backup_list(list)
|
33
69
|
backups.each do |backup|
|
34
70
|
parse_backup_info_file(backup)
|
35
|
-
if with_wal_files
|
71
|
+
if opts[:with_wal_files]
|
36
72
|
wals = wal_files(backup.server, backup.id)
|
37
73
|
wals.each { |w| backup.add_wal_file(w) }
|
38
74
|
end
|
@@ -40,6 +76,42 @@ module RBarman
|
|
40
76
|
return backups
|
41
77
|
end
|
42
78
|
|
79
|
+
# Instructs barman to get information about a server
|
80
|
+
# @param [String] name name of the server
|
81
|
+
# @param [Hash] opts options for creating {Server}
|
82
|
+
# @option opts [Boolean] :with_backups whether to include {Backups} in {Server}
|
83
|
+
# @option opts [Boolean] :with_wal_files whether to include {WalFiles} in each {Backup}
|
84
|
+
# @return [Server] a new {Server}
|
85
|
+
def server(name, opts = {})
|
86
|
+
lines = run_barman_command("show-server #{name}")
|
87
|
+
server = parse_show_server_lines(name, lines)
|
88
|
+
lines = run_barman_command("check #{name}")
|
89
|
+
parse_check_lines(server, lines)
|
90
|
+
server.backups = backups(server.name, opts) if opts[:with_backups]
|
91
|
+
return server
|
92
|
+
end
|
93
|
+
|
94
|
+
# Instructs barman to get information about all servers
|
95
|
+
# @param [Hash] opts options for creating {Servers}
|
96
|
+
# @option opts [Boolean] :with_backups whether to include {Backups}
|
97
|
+
# @option opts [Boolean] :with_wal_files whether to include {WalFiles}
|
98
|
+
# @return [Servers] an array of {Server}
|
99
|
+
def servers(opts = {})
|
100
|
+
result = Servers.new
|
101
|
+
lines = run_barman_command("list-server")
|
102
|
+
server_names = parse_list_server_lines(lines)
|
103
|
+
server_names.each do |name|
|
104
|
+
result << server(name, opts)
|
105
|
+
end
|
106
|
+
return result
|
107
|
+
end
|
108
|
+
|
109
|
+
# Instructs barman to list all wal files for a specific backup id
|
110
|
+
# @param [String] server server name
|
111
|
+
# @param [String] backup_id id of the backup
|
112
|
+
# @return [WalFiles] an array of {WalFile}
|
113
|
+
# @raise [RuntimeError] if wal file duplicates are found in xlog.db
|
114
|
+
# @raise [RuntimeError] if barman lists a wal file but no information could be found in xlog.db
|
43
115
|
def wal_files(server, backup_id)
|
44
116
|
lines = run_barman_command("list-files --target wal #{server} #{backup_id}")
|
45
117
|
wal_files = parse_wal_files_list(lines)
|
@@ -54,6 +126,65 @@ module RBarman
|
|
54
126
|
return wal_files
|
55
127
|
end
|
56
128
|
|
129
|
+
|
130
|
+
# Parses lines reported by barman's `list-server`
|
131
|
+
# @param [Array<String>] lines an array of lines from output of barman's `list-server` cmd
|
132
|
+
# @return [Array<String>] an array of server names
|
133
|
+
def parse_list_server_lines(lines)
|
134
|
+
result = Array.new
|
135
|
+
lines.each do |l|
|
136
|
+
result << l.split("-")[0].strip
|
137
|
+
end
|
138
|
+
return result
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
# Creates a {Server} object by parsing lines reported by barman's `show-server`
|
143
|
+
# @param [String] server name of the server
|
144
|
+
# @param [Array<String>] lines an array of lines from output of barman's `show-server` cmd
|
145
|
+
# @return [Server] a new {Server} object
|
146
|
+
def parse_show_server_lines(server, lines)
|
147
|
+
s = Server.new(server)
|
148
|
+
lines.each do |l|
|
149
|
+
key, value = l.gsub("\t","").split(": ")
|
150
|
+
case key.chomp
|
151
|
+
when "active"
|
152
|
+
s.active = value.to_bool
|
153
|
+
when "ssh_command"
|
154
|
+
s.ssh_cmd = value
|
155
|
+
when "conninfo"
|
156
|
+
s.conn_info = value
|
157
|
+
when "backup_directory"
|
158
|
+
s.backup_dir = value
|
159
|
+
when "basebackups_directory"
|
160
|
+
s.base_backups_dir = value
|
161
|
+
when "wals_directory"
|
162
|
+
s.wals_dir = value
|
163
|
+
end
|
164
|
+
end
|
165
|
+
return s
|
166
|
+
end
|
167
|
+
|
168
|
+
# Parses lines reported by barman's `check` and assigns according values
|
169
|
+
# @param [Server] server the server
|
170
|
+
# @param [Array<String>] lines an array of lines from output of barman's `check` cmd
|
171
|
+
# @raise [ArgumentError] if server is nil
|
172
|
+
def parse_check_lines(server, lines)
|
173
|
+
raise(ArgumentError, 'arg server not of type Server') if !server.is_a? Server
|
174
|
+
lines.each do |l|
|
175
|
+
key, value = l.gsub("\t","").split(": ")
|
176
|
+
case key.chomp
|
177
|
+
when "ssh"
|
178
|
+
server.ssh_check_ok = value == "OK" ? true : false
|
179
|
+
when "PostgreSQL"
|
180
|
+
server.pg_conn_ok = value == "OK" ? true : false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Creates a {WalFiles} object by parsing lines reported by barman
|
186
|
+
# @param [Array<String>] lines an array of lines like '/var/lib/barman/test/wals/00000001000005A9/00000001000005A9000000BC'
|
187
|
+
# @return [WalFiles] the {WalFiles}
|
57
188
|
def parse_wal_files_list(lines)
|
58
189
|
wal_files = Array.new
|
59
190
|
lines.each do |line|
|
@@ -62,6 +193,9 @@ module RBarman
|
|
62
193
|
return wal_files
|
63
194
|
end
|
64
195
|
|
196
|
+
# Creates an array of {Backup} by parsing lines reported by barman
|
197
|
+
# @param [Array<String>] lines an array of lines like 'test 20130218T080002 - Mon Feb 18 18:11:16 2013 - Size: 213.0 GiB - WAL Size: 130.0 GiB'
|
198
|
+
# @return [Array<Backup>] an array of {Backup}
|
65
199
|
def parse_backup_list(lines)
|
66
200
|
result = Array.new
|
67
201
|
lines.each do |l|
|
@@ -75,14 +209,20 @@ module RBarman
|
|
75
209
|
|
76
210
|
if b.status == :done
|
77
211
|
sizematch = l.match(/.+Size:\s(.+)\s(.+)\s-.+Size:\s(.+)\s(.+)/)
|
78
|
-
b.size = size_in_bytes(sizematch[1].to_f, sizematch[2])
|
79
|
-
b.wal_file_size = size_in_bytes(sizematch[3].to_f, sizematch[4])
|
212
|
+
b.size = CliCommand.size_in_bytes(sizematch[1].to_f, sizematch[2])
|
213
|
+
b.wal_file_size = CliCommand.size_in_bytes(sizematch[3].to_f, sizematch[4])
|
80
214
|
end
|
81
215
|
result << b
|
82
216
|
end
|
83
217
|
return result
|
84
218
|
end
|
85
219
|
|
220
|
+
# Assigns various values to a {Backup} by parsing the according "backup.info"
|
221
|
+
# @param [Backup] backup the backup
|
222
|
+
# @return [void]
|
223
|
+
# @raise [ArgumentError] if backup is not of type {Backup}
|
224
|
+
# @raise [ArgumentError] if backup.id is not set
|
225
|
+
# @raise [ArgumentError] if backup.server is not set
|
86
226
|
def parse_backup_info_file(backup)
|
87
227
|
raise(ArgumentError, "arg not of type Backup") if !backup.is_a? Backup
|
88
228
|
raise(ArgumentError, "Backup.id not set") if backup.id.nil?
|
@@ -111,6 +251,10 @@ module RBarman
|
|
111
251
|
end
|
112
252
|
end
|
113
253
|
|
254
|
+
# Assigns size, created and compression values to a {WalFile} by parsing a line from xlog.db
|
255
|
+
# @param [WalFile] wal_file the wal file
|
256
|
+
# @param [String] line a string like '00000001000005A9000000BC\\t4684503\t1360568429.0\\tbzip2'
|
257
|
+
# @return [void]
|
114
258
|
def wal_file_info_from_xlog_db_line(wal_file, line)
|
115
259
|
splitted = line.split("\t")
|
116
260
|
wal_file.size = splitted[1]
|
@@ -118,10 +262,18 @@ module RBarman
|
|
118
262
|
wal_file.compression = splitted[3].downcase.to_sym
|
119
263
|
end
|
120
264
|
|
121
|
-
|
122
|
-
|
265
|
+
# Converts the size according to the unit to bytes
|
266
|
+
# @param [Numeric] size the size
|
267
|
+
# @param [String] unit the unit, like `B`, `KiB`, `MiB`, `GiB` or `TiB`
|
268
|
+
# @return [Integer] the size in bytes
|
269
|
+
# @raise [ArgumentError] if unit is not one of B|KiB|MiB|GiB|TiB
|
270
|
+
# @example
|
271
|
+
# CliCommand.size_in_bytes(2048, 'B') #=> 2048
|
272
|
+
# CliCommand.size_in_bytes(2048, 'MiB') #=> 2048 * 1024 ** 2
|
273
|
+
def self.size_in_bytes(size, unit)
|
274
|
+
raise(ArgumentError, "unit not one of B|KiB|MiB|GiB|TiB") if !unit.match(/(B|KiB|MiB|GiB|TiB)/)
|
123
275
|
size_b = 0
|
124
|
-
case
|
276
|
+
case unit
|
125
277
|
when "B"
|
126
278
|
size_b = size
|
127
279
|
when "KiB"
|
@@ -136,10 +288,17 @@ module RBarman
|
|
136
288
|
return size_b.to_i
|
137
289
|
end
|
138
290
|
|
291
|
+
# Instructs barman to delete a specific backup
|
292
|
+
# @param [String] server server name
|
293
|
+
# @param [String] backup_id id of the backup
|
294
|
+
# @return [void]
|
139
295
|
def delete(server, backup_id)
|
140
296
|
run_barman_command("delete #{server} #{backup_id}")
|
141
297
|
end
|
142
298
|
|
299
|
+
# Instructs barman to create a backup
|
300
|
+
# @param [String] server server name
|
301
|
+
# @return [void]
|
143
302
|
def create(server)
|
144
303
|
run_barman_command("backup #{server}")
|
145
304
|
end
|
@@ -1,25 +1,39 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
|
3
3
|
module RBarman
|
4
|
+
|
5
|
+
# A very flexible configuration class, to be used as Singleton
|
4
6
|
class Configuration
|
5
7
|
include Singleton
|
6
8
|
|
9
|
+
# Creates a new instance of {Configuration}
|
10
|
+
# @param [Hash] data added as configuration parameters
|
7
11
|
def initialize(data={})
|
8
12
|
@data = {}
|
9
13
|
update!(data)
|
10
14
|
basic_configuration
|
11
15
|
end
|
12
16
|
|
17
|
+
# Adds parameters
|
18
|
+
# @param [Hash] data parameters to add
|
19
|
+
# @return [void]
|
13
20
|
def update!(data)
|
14
21
|
data.each do |key, value|
|
15
22
|
self[key] = value
|
16
23
|
end
|
17
24
|
end
|
18
25
|
|
26
|
+
# Gives access to parameters
|
27
|
+
# @param [String,Symbol] key the key
|
28
|
+
# @return [Object, nil] the value
|
19
29
|
def [](key)
|
20
30
|
@data[key.to_sym]
|
21
31
|
end
|
22
32
|
|
33
|
+
# Adds a new parameter
|
34
|
+
# @param [String] key the key
|
35
|
+
# @param [Object] value the value
|
36
|
+
# @return [void]
|
23
37
|
def []=(key, value)
|
24
38
|
if value.class == Hash
|
25
39
|
@data[key.to_sym] = Config.new(value)
|
@@ -28,6 +42,16 @@ module RBarman
|
|
28
42
|
end
|
29
43
|
end
|
30
44
|
|
45
|
+
# For catching [NoMethodError] and trying to add a new parameter or returning a parameter value, based on the "missing" method name
|
46
|
+
# @param [Symbol] sym the method name
|
47
|
+
# @param [Array] args the arguments passed to the method.
|
48
|
+
# @return [Object, nil, void] when method name doesn't end with an `=` operator, a parameter value will be returned (if found, otherwise nil)
|
49
|
+
# @example
|
50
|
+
# Configuration.instance[:some_key] = "some_value"
|
51
|
+
# Configuration.instance.some_key #=> "some_value"
|
52
|
+
# Configuration.instance.another_key = { :a => 1 }
|
53
|
+
# Configuration.instance[:another_key] #=> { :a => 1}
|
54
|
+
# Configuration.instance[:missing] #=> nil
|
31
55
|
def method_missing(sym, *args)
|
32
56
|
if sym.to_s =~ /(.+)=$/
|
33
57
|
self[$1] = args.first
|
@@ -36,6 +60,11 @@ module RBarman
|
|
36
60
|
end
|
37
61
|
end
|
38
62
|
|
63
|
+
# adds `:binary` with path to barman binary as value and `:barman_home` (default $HOME) with path to barman's backup base directory as value. If `which` reports a path for barman, that path will be used, otherwise `/usr/bin/barman`
|
64
|
+
# @return [void]
|
65
|
+
# @example
|
66
|
+
# Configuration.Instance.binary #=> "/usr/bin/barman"
|
67
|
+
# Configuration.Instance.barman_home #=> "/var/lib/barman"
|
39
68
|
def basic_configuration
|
40
69
|
b_path = `which barman`.chomp
|
41
70
|
self[:binary] = b_path.empty? ? '/usr/bin/barman' : b_path
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# @author Holger Amann <holger@sauspiel.de>
|
2
|
+
|
3
|
+
module RBarman
|
4
|
+
|
5
|
+
# Represents a server for which barman should do/have backups
|
6
|
+
class Server
|
7
|
+
|
8
|
+
# @param [String] name name of the server
|
9
|
+
# @return [String] name of the server
|
10
|
+
attr_accessor :name
|
11
|
+
|
12
|
+
# @param [Boolean] active if server is active
|
13
|
+
# @return [Boolean, nil] if server is active
|
14
|
+
attr_accessor :active
|
15
|
+
|
16
|
+
# @param [String] cmd ssh command
|
17
|
+
# @return [String, nil] the ssh command
|
18
|
+
attr_accessor :ssh_cmd
|
19
|
+
|
20
|
+
# @param [String] the connection info
|
21
|
+
# @return [String, nil] the connection info
|
22
|
+
attr_accessor :conn_info
|
23
|
+
|
24
|
+
# @param [String] path to backup directory
|
25
|
+
# @return [String, nil] the path to backup directory
|
26
|
+
attr_accessor :backup_dir
|
27
|
+
|
28
|
+
# @param [String] path to base backups directory
|
29
|
+
# @return [String, nil] the path to base backups directory
|
30
|
+
attr_accessor :base_backups_dir
|
31
|
+
|
32
|
+
# @param [String] path to wals directory
|
33
|
+
# @return [String, nil] the path to wals directory
|
34
|
+
attr_accessor :wals_dir
|
35
|
+
|
36
|
+
# @param [Boolean] if SSH connection is working
|
37
|
+
# @return [Boolean, nil] if SSH connection is working
|
38
|
+
attr_accessor :ssh_check_ok
|
39
|
+
|
40
|
+
# @param [Boolean] if PostgreSQL connection is working
|
41
|
+
# @return [Boolean, nil] if PostgreSQL connection is working
|
42
|
+
attr_accessor :pg_conn_ok
|
43
|
+
|
44
|
+
# @param [Backups] server backups
|
45
|
+
# @return [Backups, nil] server backups
|
46
|
+
attr_accessor :backups
|
47
|
+
|
48
|
+
|
49
|
+
# Creates a new instance of {Server}
|
50
|
+
def initialize(name)
|
51
|
+
@name = name
|
52
|
+
end
|
53
|
+
|
54
|
+
# Instructs the underlying (barman) command to get information about a server
|
55
|
+
# @param [String] name name of the server
|
56
|
+
# @param [Hash] opts options for creating {Server}
|
57
|
+
# @option opts [Boolean] :with_backups whether to include {Backups}
|
58
|
+
# @option opts [Boolean] :with_wal_files whether to include {WalFiles} in each {Backup}
|
59
|
+
def self.by_name(name, opts = {})
|
60
|
+
cmd = CliCommand.new
|
61
|
+
return cmd.server(name, opts)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# An array of {Server}
|
66
|
+
class Servers < Array
|
67
|
+
# Initializes a new Array of {Server}
|
68
|
+
# @param [Array, Servers] other appends all servers from another array
|
69
|
+
def initialize(other=nil)
|
70
|
+
self.concat(other) if !other.nil? and other.is_a? Array
|
71
|
+
end
|
72
|
+
|
73
|
+
# Instructs the underlying (barman) command to get all servers
|
74
|
+
# @param [Hash] opts options for creating {Servers}
|
75
|
+
# @option opts [Boolean] :with_backups whether to include {Backups}
|
76
|
+
# @option opts [Boolean] :with_wal_files whether to include {WalFiles}
|
77
|
+
# @return [Servers] an array of {Server}
|
78
|
+
def self.all(opts={})
|
79
|
+
cmd = CliCommand.new
|
80
|
+
return Servers.new(cmd.servers(opts))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/rbarman/version.rb
CHANGED
data/lib/rbarman/wal_file.rb
CHANGED
@@ -1,14 +1,62 @@
|
|
1
1
|
require 'time'
|
2
2
|
|
3
|
+
# @author Holger Amann <holger@sauspiel.de>
|
3
4
|
module RBarman
|
4
5
|
|
5
6
|
class InvalidWalFileNameError < RuntimeError
|
6
7
|
end
|
7
8
|
|
9
|
+
# Represents a wal file
|
8
10
|
class WalFile
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
+
# @overload timeline
|
13
|
+
# @return [String, nil] timeline part of the wal file
|
14
|
+
# @overload timeline=
|
15
|
+
# Timeline part of the wal file
|
16
|
+
# @param [#to_s] timeline the timeline part
|
17
|
+
# @raise [ArgumentError] if timeline length != 8
|
18
|
+
attr_reader :timeline
|
19
|
+
|
20
|
+
# @overload xlog
|
21
|
+
# @return [String, nil] xlog part of the wal file
|
22
|
+
# @overload xlog=
|
23
|
+
# xlog part of the wal file
|
24
|
+
# @param [#to_s] xlog the xlog part
|
25
|
+
# @raise [ArgumentError] if xlog length != 8
|
26
|
+
attr_reader :xlog
|
27
|
+
|
28
|
+
# @overload segment
|
29
|
+
# @return [String, nil] segment part of the wal file
|
30
|
+
# @overload segment=
|
31
|
+
# segment part of the wal file
|
32
|
+
# @param [#to_s] segment the segment part
|
33
|
+
# @raise [ArgumentError] if segment length != 8
|
34
|
+
attr_reader :segment
|
35
|
+
|
36
|
+
# @overload created
|
37
|
+
# @return [Time, nil] time when wal file has been created
|
38
|
+
# @overload created=
|
39
|
+
# Time when wal file has been created
|
40
|
+
# @param [Time,Numeric,String] created the time
|
41
|
+
attr_reader :created
|
42
|
+
|
43
|
+
# @overload compression
|
44
|
+
# @return [Symbol, nil] compression type of wal file, `:none`, `:gzip`, `:bzip2`, `:custom`
|
45
|
+
# @overload compression=
|
46
|
+
# Compression type of wal file
|
47
|
+
# @param [Symbol] compression compression type
|
48
|
+
# @raise [ArgumentError] if compression is not one of `:none`, `:gzip`, `:bzip2`, `:custom`
|
49
|
+
attr_reader :compression
|
50
|
+
|
51
|
+
|
52
|
+
# @overload size
|
53
|
+
# @return [Integer, nil] size of wal file (in bytes)
|
54
|
+
# @overload size=
|
55
|
+
# Size of wal file (in bytes)
|
56
|
+
# @param [#to_i] size size of wal file (in bytes)
|
57
|
+
attr_reader :size
|
58
|
+
|
59
|
+
# Creates a new instance of {WalFile}
|
12
60
|
def initialize
|
13
61
|
end
|
14
62
|
|
@@ -48,6 +96,11 @@ module RBarman
|
|
48
96
|
@created = Time.parse(created) if created.is_a? String
|
49
97
|
end
|
50
98
|
|
99
|
+
# Creates a new WalFile from the given argument
|
100
|
+
# @param [String, WalFile] name the wal file name
|
101
|
+
# @return [WalFile] the created WalFile
|
102
|
+
# @raise [InvalidWalFileNameError] if name is a string and string's length isn't exactly 24 chars or
|
103
|
+
# name could not be splitted in 3 parts (timeline|xlog|segment)
|
51
104
|
def self.parse(name)
|
52
105
|
raise(InvalidWalFileNameError, "name has to be exactly 24 chars") if !name.is_a? WalFile and name.to_s.size != 24
|
53
106
|
|
@@ -66,6 +119,9 @@ module RBarman
|
|
66
119
|
return wal_file
|
67
120
|
end
|
68
121
|
|
122
|
+
# Checks if other is equal to self by comparing timeline, xlog and segment
|
123
|
+
# @param [String, WalFile] other other wal file
|
124
|
+
# @return [Boolean] if other is equal to self
|
69
125
|
def ==(other)
|
70
126
|
o = other
|
71
127
|
o = WalFile.parse(other.to_s) if !other.is_a? WalFile
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# @author Holger Amann <holger@sauspiel.de>
|
2
|
+
module RBarman
|
3
|
+
|
4
|
+
# An Array of WalFile
|
5
|
+
class WalFiles < Array
|
6
|
+
|
7
|
+
# Initializes a new Array of {WalFile}
|
8
|
+
# @param [Array,WalFiles] other appends all wal files from another array
|
9
|
+
def initialize(other=nil)
|
10
|
+
self.concat(other) if !other.nil? and other.is_a? Array
|
11
|
+
end
|
12
|
+
|
13
|
+
# Instructs the underlying (barman) command to get all wal files for a specific backup id
|
14
|
+
# @param [String] server server name
|
15
|
+
# @param [String] backup_id id of the backup
|
16
|
+
# @return [WalFiles] an array of {WalFile}
|
17
|
+
def self.by_id(server, backup_id)
|
18
|
+
cmd = CliCommand.new
|
19
|
+
return WalFiles.new(cmd.wal_files(server, backup_id))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/rbarman.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
+
require "rbarman/string"
|
1
2
|
require "rbarman/version"
|
2
3
|
require "rbarman/backup"
|
3
4
|
require "rbarman/backups"
|
4
5
|
require "rbarman/wal_file"
|
6
|
+
require "rbarman/wal_files"
|
5
7
|
require "rbarman/cli_command"
|
6
8
|
require "rbarman/configuration"
|
9
|
+
require "rbarman/server"
|
7
10
|
|
11
|
+
# The RBarman module contains all aspects of backups made by barman and dealing with them
|
8
12
|
module RBarman
|
9
13
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbarman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -75,7 +75,7 @@ dependencies:
|
|
75
75
|
- - ~>
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: 1.1.0
|
78
|
-
description: Wrapper about 2ndQuadrant's postgresql backup tool
|
78
|
+
description: Wrapper about 2ndQuadrant's postgresql backup tool 'barman'
|
79
79
|
email:
|
80
80
|
- holger@sauspiel.de
|
81
81
|
executables: []
|
@@ -86,8 +86,11 @@ files:
|
|
86
86
|
- lib/rbarman/backups.rb
|
87
87
|
- lib/rbarman/cli_command.rb
|
88
88
|
- lib/rbarman/configuration.rb
|
89
|
+
- lib/rbarman/server.rb
|
90
|
+
- lib/rbarman/string.rb
|
89
91
|
- lib/rbarman/version.rb
|
90
92
|
- lib/rbarman/wal_file.rb
|
93
|
+
- lib/rbarman/wal_files.rb
|
91
94
|
- lib/rbarman.rb
|
92
95
|
- LICENSE.txt
|
93
96
|
- README.md
|
@@ -115,5 +118,6 @@ rubyforge_project:
|
|
115
118
|
rubygems_version: 1.8.23
|
116
119
|
signing_key:
|
117
120
|
specification_version: 3
|
118
|
-
summary: Wrapper about 2ndQuadrant's postgresql backup tool
|
121
|
+
summary: Wrapper about 2ndQuadrant's postgresql backup tool 'barman'
|
119
122
|
test_files: []
|
123
|
+
has_rdoc:
|