diff options
Diffstat (limited to 'rt/lib/RT/Migrate/Importer/File.pm')
-rw-r--r-- | rt/lib/RT/Migrate/Importer/File.pm | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/rt/lib/RT/Migrate/Importer/File.pm b/rt/lib/RT/Migrate/Importer/File.pm new file mode 100644 index 000000000..176bc2653 --- /dev/null +++ b/rt/lib/RT/Migrate/Importer/File.pm @@ -0,0 +1,208 @@ +# BEGIN BPS TAGGED BLOCK {{{ +# +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC +# <sales@bestpractical.com> +# +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: +# +# This work is made available to you under the terms of Version 2 of +# the GNU General Public License. A copy of that license should have +# been provided with this software, but in any event can be snarfed +# from www.gnu.org. +# +# This work 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 or visit their web page on the internet at +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) +# +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# END BPS TAGGED BLOCK }}} + +package RT::Migrate::Importer::File; + +use strict; +use warnings; +use base qw(RT::Migrate::Importer); + +sub Init { + my $self = shift; + my %args = ( + Directory => undef, + Resume => undef, + @_ + ); + + # Directory is required + die "Directory is required" unless $args{Directory}; + die "Invalid path $args{Directory}" unless -d $args{Directory}; + $self->{Directory} = $args{Directory}; + + # Load metadata, if present + if (-e "$args{Directory}/rt-serialized") { + my $dat = eval { Storable::retrieve("$args{Directory}/rt-serialized"); } + or die "Failed to load metadata" . ($@ ? ": $@" : ""); + $self->LoadMetadata($dat); + } + + # Support resuming + $self->{Statefile} = $args{Statefile} || "$args{Directory}/partial-import"; + unlink $self->{Statefile} + if -f $self->{Statefile} and not $args{Resume}; + + return $self->SUPER::Init(@_); +} + +sub Import { + my $self = shift; + my $dir = $self->{Directory}; + + if ($self->{Metadata} and $self->{Metadata}{Files}) { + $self->{Files} = [ map {s|^.*/|$dir/|;$_} @{$self->{Metadata}{Files}} ]; + } else { + $self->{Files} = [ <$dir/*.dat> ]; + } + $self->{Files} = [ map {File::Spec->rel2abs($_)} @{ $self->{Files} } ]; + + $self->RestoreState( $self->{Statefile} ); + + local $SIG{ INT } = sub { $self->{INT} = 1 }; + local $SIG{__DIE__} = sub { warn "\n", @_; $self->SaveState; exit 1 }; + + $self->{Progress}->(undef) if $self->{Progress}; + while (@{$self->{Files}}) { + $self->{Filename} = shift @{$self->{Files}}; + open(my $fh, "<", $self->{Filename}) + or die "Can't read $self->{Filename}: $!"; + if ($self->{Seek}) { + seek($fh, $self->{Seek}, 0) + or die "Can't seek to $self->{Seek} in $self->{Filename}"; + $self->{Seek} = undef; + } + while (not eof($fh)) { + $self->{Position} = tell($fh); + + # Stop when we're at a good stopping point + die "Caught interrupt, quitting.\n" if $self->{INT}; + + $self->ReadStream( $fh ); + } + } + + $self->CloseStream; + + # Return creation counts + return $self->ObjectCount; +} + +sub List { + my $self = shift; + my $dir = $self->{Directory}; + + my %found = ( "RT::System" => 1 ); + my @files = ($self->{Metadata} and $self->{Metadata}{Files}) ? + @{ $self->{Metadata}{Files} } : <$dir/*.dat>; + @files = map {File::Spec->rel2abs($_)} @files; + + for my $filename (@files) { + open(my $fh, "<", $filename) + or die "Can't read $filename: $!"; + while (not eof($fh)) { + my $loaded = Storable::fd_retrieve($fh); + if (ref $loaded eq "HASH") { + $self->LoadMetadata( $loaded ); + next; + } + + if ($self->{DumpObjects}) { + print STDERR Data::Dumper::Dumper($loaded), "\n" + if $self->{DumpObjects}{ $loaded->[0] }; + } + + my ($class, $uid, $data) = @{$loaded}; + $self->{ObjectCount}{$class}++; + $found{$uid} = 1; + delete $self->{Pending}{$uid}; + for (grep {ref $data->{$_}} keys %{$data}) { + my $uid_ref = ${ $data->{$_} }; + unless (defined $uid_ref) { + push @{ $self->{Invalid} }, { uid => $uid, column => $_ }; + next; + } + next if $found{$uid_ref}; + next if $uid_ref =~ /^RT::Principal-/; + push @{$self->{Pending}{$uid_ref} ||= []}, {uid => $uid}; + } + } + } + + return $self->ObjectCount; +} + +sub RestoreState { + my $self = shift; + my ($statefile) = @_; + return unless $statefile && -f $statefile; + + my $state = Storable::retrieve( $self->{Statefile} ); + $self->{$_} = $state->{$_} for keys %{$state}; + unlink $self->{Statefile}; + + print STDERR "Resuming partial import...\n"; + sleep 2; + return 1; +} + +sub SaveState { + my $self = shift; + + my %data; + unshift @{$self->{Files}}, $self->{Filename}; + $self->{Seek} = $self->{Position}; + $data{$_} = $self->{$_} for + qw/Filename Seek Position Files + Organization ObjectCount + NewQueues NewCFs + SkipTransactions Pending Invalid + UIDs + OriginalId Clone + /; + Storable::nstore(\%data, $self->{Statefile}); + + print STDERR <<EOT; + +Importer state has been written to the file: + $self->{Statefile} + +It may be possible to resume the import by re-running rt-importer. +EOT +} + +1; |