rbarman 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +22 -0
- data/README.md +125 -0
- data/lib/rbarman/backup.rb +106 -0
- data/lib/rbarman/backups.rb +20 -0
- data/lib/rbarman/cli_command.rb +163 -0
- data/lib/rbarman/configuration.rb +46 -0
- data/lib/rbarman/version.rb +3 -0
- data/lib/rbarman/wal_file.rb +82 -0
- data/lib/rbarman.rb +9 -0
- metadata +119 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Holger Amann <holger@sauspiel.de>
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# rbarman
|
2
|
+
|
3
|
+
rbarman - Ruby Wrapper for 2ndQuadrant's PostgreSQL backup tool [pgbarman](http://pgbarman.org)
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
pgbarman 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
|
+
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'rbarman'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install rbarman
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Get all your backups!
|
25
|
+
|
26
|
+
This will call pgbarman and some other sources (backup.info, xlog.db) to get information about your backups. This could take several minutes (and memory) if you have many backups with thousand of wal files. If you don't want to scan for wal file information, use RBarman::Backups.all('server', false)
|
27
|
+
|
28
|
+
<pre>
|
29
|
+
backups = RBarman::Backups.all('server', true)
|
30
|
+
backups.count
|
31
|
+
=> 3
|
32
|
+
|
33
|
+
backups.each { |b| p "id: #{b.id} }
|
34
|
+
=> "id: 20130304T080002"
|
35
|
+
=> "id: 20130225T192654"
|
36
|
+
=> "id: 20130218T080002"
|
37
|
+
|
38
|
+
backups.latest.id
|
39
|
+
=> "20130304T080002"
|
40
|
+
|
41
|
+
backups.oldest.id
|
42
|
+
=> "20130218T080002"
|
43
|
+
|
44
|
+
backups[0].status
|
45
|
+
=> :done # :started, :failed, :empty
|
46
|
+
|
47
|
+
backups[0].backup_start.to_s
|
48
|
+
=> "2013-03-04 08:00:02 +0100"
|
49
|
+
|
50
|
+
backups[0].backup_end.to_s
|
51
|
+
=> "2013-03-04 16:46:28 +0100"
|
52
|
+
|
53
|
+
backups[0].size
|
54
|
+
=> 201071500850 # bytes
|
55
|
+
|
56
|
+
backups[0].wal_file_size
|
57
|
+
=> 41875931136 # bytes
|
58
|
+
|
59
|
+
backups[0].timeline
|
60
|
+
=> 1
|
61
|
+
|
62
|
+
backups[0].begin_wal.xlog
|
63
|
+
=> "0000058F"
|
64
|
+
|
65
|
+
backups[0].end_wal.segment
|
66
|
+
=> "000000A5"
|
67
|
+
|
68
|
+
backups[0].wal_files.count
|
69
|
+
=> 9019
|
70
|
+
|
71
|
+
backups[0].wal_files[1022].compression
|
72
|
+
=> :bzip2
|
73
|
+
</pre>
|
74
|
+
|
75
|
+
### Get just one backup without wal files
|
76
|
+
|
77
|
+
<pre>
|
78
|
+
backup = RBarman::Backup.by_id('server', '20130225T192654', false)
|
79
|
+
p "id: #{backup.id}|size: #{backup.size / (1024 ** 3) } GB|wal size: #{backup.wal_file_size / (1024 ** 3)} GB"
|
80
|
+
=> "id: 20130225T192654|size: 217GB|wal size: 72 GB"
|
81
|
+
</pre>
|
82
|
+
|
83
|
+
### Create a backup
|
84
|
+
|
85
|
+
Creates a new backup (and probably takes some time)
|
86
|
+
|
87
|
+
<pre>
|
88
|
+
b = RBarman::Backup.create('server')
|
89
|
+
p b.id
|
90
|
+
=> "20130304T131422"
|
91
|
+
</pre>
|
92
|
+
|
93
|
+
### Delete a backup
|
94
|
+
|
95
|
+
This tells pgbarman to delete the specified backup
|
96
|
+
|
97
|
+
<pre>
|
98
|
+
backup = RBarman::Backup.by_id('server', '20130225T192654', false)
|
99
|
+
p backup.deleted
|
100
|
+
=> false
|
101
|
+
backup.delete
|
102
|
+
p backup.deleted
|
103
|
+
=> true
|
104
|
+
</pre>
|
105
|
+
|
106
|
+
### Not yet implemented
|
107
|
+
|
108
|
+
* restore backups
|
109
|
+
|
110
|
+
|
111
|
+
## Contributing
|
112
|
+
|
113
|
+
1. Fork it
|
114
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
115
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
116
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
117
|
+
5. Create new Pull Request
|
118
|
+
|
119
|
+
|
120
|
+
## Meta
|
121
|
+
|
122
|
+
Written by [Holger Amann](http://github.com/hamann), sponsored by [Sauspiel GmbH](https://www.sauspiel.de)
|
123
|
+
|
124
|
+
Release under the MIT License: http://www.opensource.org/licenses/mit-license.php
|
125
|
+
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module RBarman
|
4
|
+
|
5
|
+
class InvalidBackupIdError < RuntimeError
|
6
|
+
end
|
7
|
+
|
8
|
+
class Backup
|
9
|
+
attr_accessor :server
|
10
|
+
attr_reader :id, :backup_start, :backup_end, :status, :wal_files, :size, :wal_file_size, :begin_wal, :end_wal, :timeline, :pgdata, :deleted
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@deleted = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def id=(id)
|
17
|
+
raise InvalidBackupIdError if !Backup.backup_id_valid?(id)
|
18
|
+
@id = id
|
19
|
+
end
|
20
|
+
|
21
|
+
def backup_start=(start)
|
22
|
+
@backup_start = Time.parse(start)
|
23
|
+
end
|
24
|
+
|
25
|
+
def backup_end=(b_end)
|
26
|
+
@backup_end = Time.parse(b_end)
|
27
|
+
end
|
28
|
+
|
29
|
+
def status=(status)
|
30
|
+
if status != :empty and
|
31
|
+
status != :started and
|
32
|
+
status != :done and
|
33
|
+
status != :failed
|
34
|
+
raise(ArgumentError,"only :empty, :started, :done or :failed allowed!")
|
35
|
+
end
|
36
|
+
|
37
|
+
@status = status
|
38
|
+
end
|
39
|
+
|
40
|
+
def wal_files=(wal_files)
|
41
|
+
raise(ArgumentError, "argument not of type array") if !wal_files.is_a? Array
|
42
|
+
@wal_files = wal_files
|
43
|
+
end
|
44
|
+
|
45
|
+
def size=(size)
|
46
|
+
@size = size.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
def begin_wal=(wal_file)
|
50
|
+
@begin_wal = WalFile.parse(wal_file)
|
51
|
+
end
|
52
|
+
|
53
|
+
def end_wal=(wal_file)
|
54
|
+
@end_wal = WalFile.parse(wal_file)
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def wal_file_size=(size)
|
59
|
+
@wal_file_size = size.to_i
|
60
|
+
end
|
61
|
+
|
62
|
+
def timeline=(i)
|
63
|
+
raise(ArgumentError, "timeline should be > 0") if i.to_i == 0
|
64
|
+
@timeline = i.to_i
|
65
|
+
end
|
66
|
+
|
67
|
+
def pgdata=(path)
|
68
|
+
raise(ArgumentError, "path is empty") if path == ""
|
69
|
+
@pgdata = path
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_wal_file(wal_file)
|
73
|
+
@wal_files = Array.new if @wal_files.nil?
|
74
|
+
@wal_files << WalFile.parse(wal_file)
|
75
|
+
end
|
76
|
+
|
77
|
+
def wal_file_already_added?(wal_file)
|
78
|
+
return false if @wal_files.nil?
|
79
|
+
return @wal_files.include?(WalFile.parse(wal_file))
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def self.backup_id_valid?(id)
|
84
|
+
return false if id.nil?
|
85
|
+
return !id.match(/\d{8,8}T\d{6,6}/).nil?
|
86
|
+
end
|
87
|
+
|
88
|
+
def delete
|
89
|
+
cmd = CliCommand.new
|
90
|
+
cmd.delete(@server, @id)
|
91
|
+
@deleted = true
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.create(server)
|
95
|
+
cmd = CliCommand.new
|
96
|
+
cmd.create(server)
|
97
|
+
backups = Backups.all(server, false)
|
98
|
+
return Backup.by_id(server, backups.latest.id, true)
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.by_id(server, backup_id, with_wal_files=true)
|
102
|
+
cmd = CliCommand.new
|
103
|
+
return cmd.backup(server, backup_id, with_wal_files)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RBarman
|
2
|
+
class Backups < Array
|
3
|
+
def initialize(other=nil)
|
4
|
+
self.concat(other) if !other.nil? and other.is_a? Array
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.all(server, with_wal_files=true)
|
8
|
+
cmd = CliCommand.new
|
9
|
+
return Backups.new(cmd.backups(server, with_wal_files))
|
10
|
+
end
|
11
|
+
|
12
|
+
def latest
|
13
|
+
self.sort_by { |d| Time.parse(d.id) }.reverse.first
|
14
|
+
end
|
15
|
+
|
16
|
+
def oldest
|
17
|
+
self.sort_by { |d| Time.parse(d.id) }.first
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'mixlib/shellout'
|
2
|
+
|
3
|
+
module RBarman
|
4
|
+
class CliCommand
|
5
|
+
attr_reader :binary, :barman_home
|
6
|
+
|
7
|
+
def initialize(path_to_binary=nil, path_to_barman_home=nil)
|
8
|
+
self.binary=path_to_binary || Configuration.instance.binary
|
9
|
+
self.barman_home=path_to_barman_home || Configuration.instance.barman_home
|
10
|
+
end
|
11
|
+
|
12
|
+
def binary=(path)
|
13
|
+
raise(ArgumentError, "binary doesn't exist") if !File.exists?(path)
|
14
|
+
raise(ArgumentError, "binary isn't called \'barman\'") if File.basename(path) != 'barman'
|
15
|
+
@binary = path
|
16
|
+
end
|
17
|
+
|
18
|
+
def barman_home=(path)
|
19
|
+
raise(ArgumentError, "path doesn't exist") if !File.exists?(path)
|
20
|
+
@barman_home = path
|
21
|
+
end
|
22
|
+
|
23
|
+
def backup(server, backup_id, with_wal_files=true)
|
24
|
+
raise(ArgumentError, "backup id must not be nil!") if backup_id.nil?
|
25
|
+
return backups(server, with_wal_files, backup_id)[0]
|
26
|
+
end
|
27
|
+
|
28
|
+
def backups(server, with_wal_files=true, backup_id=nil)
|
29
|
+
list = run_barman_command("list-backup #{server}")
|
30
|
+
list = list.grep(/#{backup_id}/) if !backup_id.nil?
|
31
|
+
|
32
|
+
backups = parse_backup_list(list)
|
33
|
+
backups.each do |backup|
|
34
|
+
parse_backup_info_file(backup)
|
35
|
+
if with_wal_files
|
36
|
+
wals = wal_files(backup.server, backup.id)
|
37
|
+
wals.each { |w| backup.add_wal_file(w) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return backups
|
41
|
+
end
|
42
|
+
|
43
|
+
def wal_files(server, backup_id)
|
44
|
+
lines = run_barman_command("list-files --target wal #{server} #{backup_id}")
|
45
|
+
wal_files = parse_wal_files_list(lines)
|
46
|
+
xlog_db_lines = file_content("#{@barman_home}/#{server}/wals/xlog.db")
|
47
|
+
wal_files.each do |w|
|
48
|
+
wal = "#{w.timeline}#{w.xlog}#{w.segment}"
|
49
|
+
lines = xlog_db_lines.grep(/^#{wal}\t.+/)
|
50
|
+
raise(RuntimeError, "Found more than one wal file entry in xlog.db for #{wal}") if lines.count > 1
|
51
|
+
raise(RuntimeError, "Could not find any entry for #{wal} in xlog.db") if lines.count == 0
|
52
|
+
wal_file_info_from_xlog_db_line(w, lines[0])
|
53
|
+
end
|
54
|
+
return wal_files
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_wal_files_list(lines)
|
58
|
+
wal_files = Array.new
|
59
|
+
lines.each do |line|
|
60
|
+
wal_files << WalFile.parse(line.split("/").last)
|
61
|
+
end
|
62
|
+
return wal_files
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_backup_list(lines)
|
66
|
+
result = Array.new
|
67
|
+
lines.each do |l|
|
68
|
+
match = l.match(/^(.+)\s(\d+T\d+)/)
|
69
|
+
b = Backup.new
|
70
|
+
b.server = match[1]
|
71
|
+
b.id = match[2]
|
72
|
+
|
73
|
+
status_match = l.match(/.+(FAILED|STARTED)/)
|
74
|
+
status_match.nil? ? b.status = :done : b.status = status_match[1].downcase.to_sym
|
75
|
+
|
76
|
+
if b.status == :done
|
77
|
+
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])
|
80
|
+
end
|
81
|
+
result << b
|
82
|
+
end
|
83
|
+
return result
|
84
|
+
end
|
85
|
+
|
86
|
+
def parse_backup_info_file(backup)
|
87
|
+
raise(ArgumentError, "arg not of type Backup") if !backup.is_a? Backup
|
88
|
+
raise(ArgumentError, "Backup.id not set") if backup.id.nil?
|
89
|
+
raise(ArgumentError, "Backup.server not set") if backup.server.nil?
|
90
|
+
backup_info = file_content("#{barman_home}/#{backup.server}/base/#{backup.id}/backup.info")
|
91
|
+
backup_info.each do |l|
|
92
|
+
key, value = l.split("=")
|
93
|
+
case key
|
94
|
+
when "begin_time"
|
95
|
+
backup.backup_start = value
|
96
|
+
when "end_time"
|
97
|
+
backup.backup_end = value
|
98
|
+
when "status"
|
99
|
+
backup.status = value.downcase.to_sym
|
100
|
+
when "size"
|
101
|
+
backup.size = value.to_i
|
102
|
+
when "timeline"
|
103
|
+
backup.timeline = value.to_i
|
104
|
+
when "begin_wal"
|
105
|
+
backup.begin_wal = WalFile.parse(value)
|
106
|
+
when "end_wal"
|
107
|
+
backup.end_wal = WalFile.parse(value)
|
108
|
+
when "pgdata"
|
109
|
+
backup.pgdata = value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def wal_file_info_from_xlog_db_line(wal_file, line)
|
115
|
+
splitted = line.split("\t")
|
116
|
+
wal_file.size = splitted[1]
|
117
|
+
wal_file.created = splitted[2].to_i
|
118
|
+
wal_file.compression = splitted[3].downcase.to_sym
|
119
|
+
end
|
120
|
+
|
121
|
+
def size_in_bytes(size, identifier)
|
122
|
+
raise(ArgumentError, "identifier not one of B|KiB|MiB|GiB|TiB") if !identifier.match(/(B|KiB|MiB|GiB|TiB)/)
|
123
|
+
size_b = 0
|
124
|
+
case identifier
|
125
|
+
when "B"
|
126
|
+
size_b = size
|
127
|
+
when "KiB"
|
128
|
+
size_b = size * 1024
|
129
|
+
when "MiB"
|
130
|
+
size_b = size * 1024 ** 2
|
131
|
+
when "GiB"
|
132
|
+
size_b = size * 1024 ** 3
|
133
|
+
when "TiB"
|
134
|
+
size_b = size * 1024 ** 4
|
135
|
+
end
|
136
|
+
return size_b.to_i
|
137
|
+
end
|
138
|
+
|
139
|
+
def delete(server, backup_id)
|
140
|
+
run_barman_command("delete #{server} #{backup_id}")
|
141
|
+
end
|
142
|
+
|
143
|
+
def create(server)
|
144
|
+
run_barman_command("backup #{server}")
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
def run_barman_command(args)
|
149
|
+
sh = Mixlib::ShellOut.new("#{@binary} #{args}")
|
150
|
+
|
151
|
+
# TODO timeout should be configurable
|
152
|
+
sh.timeout = 43200 # 12h
|
153
|
+
|
154
|
+
sh.run_command
|
155
|
+
sh.error!
|
156
|
+
return sh.stdout.split("\n")
|
157
|
+
end
|
158
|
+
|
159
|
+
def file_content(path)
|
160
|
+
return File.readlines(path).map { |l| l.chomp }
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module RBarman
|
4
|
+
class Configuration
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
def initialize(data={})
|
8
|
+
@data = {}
|
9
|
+
update!(data)
|
10
|
+
basic_configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
def update!(data)
|
14
|
+
data.each do |key, value|
|
15
|
+
self[key] = value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](key)
|
20
|
+
@data[key.to_sym]
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(key, value)
|
24
|
+
if value.class == Hash
|
25
|
+
@data[key.to_sym] = Config.new(value)
|
26
|
+
else
|
27
|
+
@data[key.to_sym] = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(sym, *args)
|
32
|
+
if sym.to_s =~ /(.+)=$/
|
33
|
+
self[$1] = args.first
|
34
|
+
else
|
35
|
+
self[sym]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def basic_configuration
|
40
|
+
b_path = `which barman`.chomp
|
41
|
+
self[:binary] = b_path.empty? ? '/usr/bin/barman' : b_path
|
42
|
+
self[:barman_home] = ENV['HOME']
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module RBarman
|
4
|
+
|
5
|
+
class InvalidWalFileNameError < RuntimeError
|
6
|
+
end
|
7
|
+
|
8
|
+
class WalFile
|
9
|
+
|
10
|
+
attr_reader :timeline, :xlog, :segment, :created, :compression, :size
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
end
|
14
|
+
|
15
|
+
def timeline=(timeline)
|
16
|
+
validate(timeline)
|
17
|
+
@timeline = timeline
|
18
|
+
end
|
19
|
+
|
20
|
+
def xlog=(xlog)
|
21
|
+
validate(xlog)
|
22
|
+
@xlog = xlog
|
23
|
+
end
|
24
|
+
|
25
|
+
def segment=(segment)
|
26
|
+
validate(segment)
|
27
|
+
@segment = segment
|
28
|
+
end
|
29
|
+
|
30
|
+
def compression=(compression)
|
31
|
+
if compression != :gzip and
|
32
|
+
compression != :bzip2 and
|
33
|
+
compression != :none and
|
34
|
+
compression != :custom
|
35
|
+
raise(ArgumentError, "only :gzip, :bzip2, :none or :custom allowed!")
|
36
|
+
end
|
37
|
+
|
38
|
+
@compression = compression
|
39
|
+
end
|
40
|
+
|
41
|
+
def size=(size)
|
42
|
+
@size = size.to_i
|
43
|
+
end
|
44
|
+
|
45
|
+
def created=(created)
|
46
|
+
@created = created if created.is_a? Time
|
47
|
+
@created = Time.at(created.to_i) if created.is_a? Numeric
|
48
|
+
@created = Time.parse(created) if created.is_a? String
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.parse(name)
|
52
|
+
raise(InvalidWalFileNameError, "name has to be exactly 24 chars") if !name.is_a? WalFile and name.to_s.size != 24
|
53
|
+
|
54
|
+
if name.is_a? WalFile
|
55
|
+
wal_file = name
|
56
|
+
else
|
57
|
+
splitted = name.to_s.scan(/.{8}/)
|
58
|
+
raise InvalidWalFileNameError if splitted.count != 3
|
59
|
+
|
60
|
+
wal_file = WalFile.new
|
61
|
+
wal_file.timeline = splitted[0]
|
62
|
+
wal_file.xlog = splitted[1]
|
63
|
+
wal_file.segment = splitted[2]
|
64
|
+
end
|
65
|
+
|
66
|
+
return wal_file
|
67
|
+
end
|
68
|
+
|
69
|
+
def ==(other)
|
70
|
+
o = other
|
71
|
+
o = WalFile.parse(other.to_s) if !other.is_a? WalFile
|
72
|
+
return o.timeline == @timeline && o.xlog == @xlog && o.segment == @segment
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def validate(arg)
|
78
|
+
raise(ArgumentError, "arg's size must be 8") if arg.to_s.size != 8
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
data/lib/rbarman.rb
ADDED
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rbarman
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Holger Amann
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.13.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.13.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: mixlib-shellout
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.1.0
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.1.0
|
78
|
+
description: Wrapper about 2ndQuadrant's postgresql backup tool \'barman\'
|
79
|
+
email:
|
80
|
+
- holger@sauspiel.de
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- lib/rbarman/backup.rb
|
86
|
+
- lib/rbarman/backups.rb
|
87
|
+
- lib/rbarman/cli_command.rb
|
88
|
+
- lib/rbarman/configuration.rb
|
89
|
+
- lib/rbarman/version.rb
|
90
|
+
- lib/rbarman/wal_file.rb
|
91
|
+
- lib/rbarman.rb
|
92
|
+
- LICENSE.txt
|
93
|
+
- README.md
|
94
|
+
homepage: https://github.com/sauspiel/rbarman
|
95
|
+
licenses:
|
96
|
+
- MIT
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ! '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 1.8.23
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: Wrapper about 2ndQuadrant's postgresql backup tool \'barman\'
|
119
|
+
test_files: []
|