2 # BEGIN BPS TAGGED BLOCK {{{
6 # This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
7 # <sales@bestpractical.com>
9 # (Except where explicitly superseded by other copyright notices)
14 # This work is made available to you under the terms of Version 2 of
15 # the GNU General Public License. A copy of that license should have
16 # been provided with this software, but in any event can be snarfed
19 # This work is distributed in the hope that it will be useful, but
20 # WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 # General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 # 02110-1301 or visit their web page on the internet at
28 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
31 # CONTRIBUTION SUBMISSION POLICY:
33 # (The following paragraph is not intended to limit the rights granted
34 # to you to modify and distribute this software under the terms of
35 # the GNU General Public License and is only of importance to you if
36 # you choose to contribute your changes and enhancements to the
37 # community by submitting them to Best Practical Solutions, LLC.)
39 # By intentionally submitting any modifications, corrections or
40 # derivatives to this work, or any other work intended for use with
41 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
42 # you are the copyright holder for those contributions and you grant
43 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
44 # royalty-free, perpetual, license to use, copy, create derivative
45 # works based on those contributions, and sublicense and distribute
46 # those contributions and any derivatives thereof.
48 # END BPS TAGGED BLOCK }}}
52 # fix lib paths, some may be relative
55 my @libs = ("@RT_LIB_PATH@", "@LOCAL_LIB_PATH@");
59 unless ( File::Spec->file_name_is_absolute($lib) ) {
61 if ( File::Spec->file_name_is_absolute(__FILE__) ) {
62 $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
67 $bin_path = $FindBin::Bin;
70 $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
81 @RT::Record::ISA = qw( DBIx::SearchBuilder::Record RT::Base );
84 use RT::Migrate::Importer::File;
89 my %OPT = (resume => 1);
103 ) or Pod::Usage::pod2usage();
105 Pod::Usage::pod2usage(-verbose => 1) if $OPT{help};
107 Pod::Usage::pod2usage() unless @ARGV == 1;
110 die "No such directory $dir\n" unless -d $dir;
111 die "$dir doesn't appear to contain serialized data\n"
112 unless -f "$dir/001.dat";
115 die "Dumping objects only works in conjunction with --list\n"
118 $OPT{dump} = [ split /,/, join(',', @{$OPT{dump}}) ];
123 die "Interactive mode (--ask) doesn't work when STDERR and STDIN aren't terminals.\n"
124 unless -t STDERR and -t STDIN;
126 $error_handler = sub {
127 my $importer = shift;
129 print STDERR "\n", @_, "\n";
130 print STDERR "Hit any key to abort import, or type 'ignore' to continue anyway.\n";
131 print STDERR "Continuing may leave you with a corrupt database. > ";
132 chomp( my $resp = <STDIN> );
133 return lc($resp) eq 'ignore';
136 elsif ($OPT{'ignore-errors'}) {
137 $error_handler = sub {
138 my $importer = shift;
139 warn "Ignoring error: ", @_;
144 my $import = RT::Migrate::Importer::File->new(
146 OriginalId => $OPT{originalid},
147 DumpObjects => $OPT{dump},
148 Resume => $OPT{resume},
149 HandleError => $error_handler,
152 if ($import->Metadata and -t STDOUT and not $OPT{quiet}) {
154 RT::Migrate::progress(
155 counts => sub { $import->ObjectCount },
156 max => $import->Metadata->{ObjectCount},
161 my $log = RT::Migrate::setup_logging( $dir => 'importer.log' );
162 print "Logging warnings and errors to $log\n" if $log;
166 %counts = $import->List;
168 my $org = $import->Organization;
169 print "=========== Dump of $org ===========\n\n";
171 %counts = $import->Import;
173 my $org = $import->Organization;
174 print "========== Import of $org ==========\n\n";
177 print "Total object counts:\n";
178 for (sort {$counts{$b} <=> $counts{$a}} keys %counts) {
179 printf "%8d %s\n", $counts{$_}, $_;
182 my @missing = $import->Missing;
184 warn "The following UIDs were expected but never observed:\n";
185 warn " $_\n" for @missing;
188 my @invalid = $import->Invalid;
190 warn "The following UIDs (serialized => imported) referred to objects missing from the original database:\n";
191 for my $info (@invalid) {
192 my $uid = delete $info->{uid};
193 my $obj = $import->LookupObj($uid);
194 warn sprintf " %s => %s (%s)\n",
196 ($obj && $obj->Id ? $obj->UID : '(not imported)'),
197 join(", ", map { "$_ => $info->{$_}" }
198 grep { defined $info->{$_} }
203 if ($log and -s $log) {
204 print STDERR "\n! Some warnings or errors occurred during import."
205 ."\n! Please see $log for details.\n\n";
212 rt-importer - Import a serialized RT database on top of the current one
216 rt-importer path/to/export/directory
218 This script is used to import the contents of a dump created by
219 C<rt-serializer>. It will create all of the objects in the dump in the
220 current database; this may include users, queues, and tickets.
222 It is possible to stop the import process with ^C; it can be later
223 resumed by re-running the importer.
231 Print a summary of the data contained in the dump.
233 =item B<--originalid> I<cfname>
235 Places the original ticket organization and ID into a global custom
236 field with the given name. If no global ticket custom field with that
237 name is found in the current database, it will create one.
241 Prompt for action when an error occurs inserting a record into the
242 database. This can often happen when importing data from very old RTs
243 where some attachments (usually spam) contain invalid UTF-8.
245 The importer will pause and ask if you want to ignore the error and
246 continue on or abort (potentially to restart later). Ignoring errors
247 will result in missing records in the database, which may cause database
248 integrity problems later. If you ignored any errors, you should run
249 C<rt-validator> after import.
251 =item B<--ignore-errors>
253 Ignore all record creation errors and continue on when importing. This
254 is equivalent to running with C<--ask> and manually typing "ignore" at
255 every prompt. You should always run C<rt-validator> after importing
258 B<This option can be dangerous and leave you with a broken RT!>
260 =item B<--dump> I<class>[,I<class>]
262 Prints L<Data::Dumper> representations of the objects of type I<class> in the
263 serialized data. This is mostly useful for debugging.
265 Works only in conjunction with C<--list>.
272 Some dumps may have been taken as complete clones of the RT system,
273 which are only suitable for inserting into a schema with no data in it.
274 You can setup the required database state for the receiving RT instance
277 @RT_SBIN_PATH_R@/rt-setup-database --action create,schema,acl --prompt-for-dba-password
279 The normal C<make initdb> step will B<not> work because it also inserts