App-BorgRestore
view release on metacpan or search on metacpan
lib/App/BorgRestore.pm view on Meta::CPAN
my $backup_path = $self->map_path_to_backup_path($abs_path);
$destination //= dirname($abs_path);
my $archives = $self->find_archives($backup_path);
my $selected_archive = $self->select_archive_timespec($archives, $timespec);
$self->restore($backup_path, $selected_archive, $destination);
}
=head3 search_path
my $paths = $app->search_path($pattern)
Returns a arrayref of paths that match the pattern. The pattern is matched as
an sqlite LIKE pattern. If no % occurs in the pattern, the patterns is
automatically wrapped between two % so it may match anywhere in the path.
=cut
method search_path($pattern) {
$pattern = '%'.$pattern.'%' if $pattern !~ m/%/;
return $self->{deps}->{db}->search_path($pattern);
}
=head3 get_missing_items
my $items = $app->get_missing_items($have, $want);
Returns an arrayref of items that are part of C<$want>, but not of C<$have>.
=cut
method get_missing_items($have, $want) {
my $ret = [];
for my $item (@$want) {
my $exists = any { $_ eq $item } @$have;
push @$ret, $item if not $exists;
}
return $ret;
}
method _handle_removed_archives($borg_archives) {
my $start = Time::HiRes::gettimeofday();
my $existing_archives = $self->{deps}->{db}->get_archive_names();
# TODO this name is slightly confusing, but it works as expected and
# returns elements that are in the previous list, but missing in the new
# one
my $remove_archives = $self->get_missing_items($borg_archives, $existing_archives);
if (@$remove_archives) {
for my $archive (@$remove_archives) {
$log->infof("Removing archive %s", $archive);
$self->{deps}->{db}->begin_work;
$self->{deps}->{db}->remove_archive($archive);
$self->{deps}->{db}->commit;
$self->{deps}->{db}->vacuum;
$self->{deps}->{db}->verify_cache_fill_rate_ok();
}
my $end = Time::HiRes::gettimeofday();
$log->debugf("Removing archives finished after: %.5fs", $end - $start);
}
}
method _handle_added_archives($borg_archives) {
my $archives = $self->{deps}->{db}->get_archive_names();
my $add_archives = $self->get_missing_items($archives, $borg_archives);
for my $archive (@$add_archives) {
my $start = Time::HiRes::gettimeofday();
my $lookuptable_class = $self->{config}->{cache}->{prepare_data_in_memory} == 1 ? "Memory" : "DB";
$log->debugf("Using '%s' class for PathTimeTable", $lookuptable_class);
my $lookuptable = "App::BorgRestore::PathTimeTable::$lookuptable_class"->new({db => $self->{deps}->{db}});
$log->infof("Adding archive %s", $archive);
$self->{deps}->{db}->begin_work;
$self->{deps}->{db}->add_archive_name($archive);
my $archive_id = $self->{deps}->{db}->get_archive_id($archive);
$lookuptable->set_archive_id($archive_id);
$self->{deps}->{borg}->list_archive($archive, sub {
my $line = shift;
# roll our own parsing of timestamps for speed since we will be parsing
# a huge number of lines here
# XXX: this also exists in BorgRestore::Helper::parse_borg_time()
# example timestamp: "Wed, 2016-01-27 10:31:59"
if ($line =~ m/^.{4} (?<year>....)-(?<month>..)-(?<day>..) (?<hour>..):(?<minute>..):(?<second>..) (?<path>.+)$/) {
my $time = POSIX::mktime($+{second},$+{minute},$+{hour},$+{day},$+{month}-1,$+{year}-1900);
#$log->debugf("Adding path %s with time %s", $+{path}, $time);
$lookuptable->add_path($+{path}, $time);
}
});
my $borg_time = Time::HiRes::gettimeofday;
$lookuptable->save_nodes();
$self->{deps}->{db}->commit;
$self->{deps}->{db}->vacuum;
$self->{deps}->{db}->verify_cache_fill_rate_ok();
my $end = Time::HiRes::gettimeofday();
$log->debugf("Adding archive finished after: %.5fs (parsing borg output took %.5fs)", $end - $start, $borg_time - $start);
}
}
=head3 update_cache
$app->update_cache();
Updates the database used by e.g. C<find_archives>.
=cut
method update_cache() {
my $v2_basedir = $self->{deps}->{settings}->get_cache_base_dir_path("v2");
if (-e $v2_basedir) {
$log->info("Removing old v2 cache directory: $v2_basedir");
path($v2_basedir)->remove_tree;
}
$log->debug("Updating cache if required");
my $borg_archives = $self->{deps}->{borg}->borg_list();
# write operations benefit from the large cache so set the cache size here
$self->{deps}->{db}->set_cache_size();
$self->_handle_removed_archives($borg_archives);
$self->_handle_added_archives($borg_archives);
$log->debugf("DB contains information for %d archives in %d rows", scalar(@{$self->{deps}->{db}->get_archive_names()}), $self->{deps}->{db}->get_archive_row_count());
$self->{deps}->{db}->verify_cache_fill_rate_ok();
}
1;
__END__
=head1 LICENSE
Copyright (C) 2016-2018 Florian Pritz E<lt>bluewind@xinu.atE<gt>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
See LICENSE for the full license text.
=head1 AUTHOR
Florian Pritz E<lt>bluewind@xinu.atE<gt>
=cut
( run in 2.699 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )