????
Current Path : /scripts/ |
Current File : //scripts/transfer_eximstats |
#!/usr/local/cpanel/3rdparty/bin/perl # cpanel - scripts/transfer_eximstats # Copyright 2015 cPanel, Inc. # All rights Reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited package scripts::transfer_eximstats; use strict; use Try::Tiny; use Getopt::Long (); use Cpanel::Sys::Hostname (); use Cpanel::Rand (); use Cpanel::Logger (); use Cpanel::Binaries (); use Cpanel::Exception (); use Cpanel::EximStats (); use Cpanel::MysqlUtils (); use Cpanel::MysqlUtils::Stream (); use Cpanel::MysqlUtils::Command (); use Cpanel::MysqlUtils::Quote (); use Cpanel::DIp::MainIP (); use Cpanel::SafeRun::Errors (); use Cpanel::MysqlUtils::RemoteMySQL::ProfileManager (); exit run(@ARGV) unless caller(); sub run { my @cmdline_args = @_; return usage(1) if !@cmdline_args; unless ( $> == 0 && $< == 0 ) { return usage( 1, "[!] This program can only be run by root!\n" ); } my $opts = {}; Getopt::Long::GetOptionsFromArray( \@cmdline_args, 'from=s' => \$opts->{'from'}, 'help|h' => \$opts->{'help'}, ); return usage(0) if $opts->{'help'}; return transfer_eximstats( $opts->{'from'} ) if $opts->{'from'}; return usage(1); } sub transfer_eximstats { my $from_profile = shift; my $profile_manager = Cpanel::MysqlUtils::RemoteMySQL::ProfileManager->new( { 'read_only' => 1 } ); print "[*] Transferring eximstats database from '$from_profile' MySQL profile …\n\n"; try { $profile_manager->validate_profile($from_profile); my $from_profile_hr = $profile_manager->read_profiles()->{$from_profile}; my $eximstats_db = 'eximstats'; my $schema_file = Cpanel::Rand::gettmpfile(); my $data_file = Cpanel::Rand::gettmpfile(); my $local_eximstats_schema = sub { if ( open( my $fh, '>', $schema_file ) ) { Cpanel::MysqlUtils::Stream::stream_mysqldump_to_filehandle( { 'dbuser' => $from_profile_hr->{'mysql_user'}, 'dbpass' => $from_profile_hr->{'mysql_pass'}, 'dbhost' => $from_profile_hr->{'mysql_host'}, 'dbport' => $from_profile_hr->{'mysql_port'}, 'db' => $eximstats_db, 'options' => ['-d'], 'filehandle' => $fh } ); close($fh); return $schema_file; } }; my $local_eximstats_data = sub { if ( open( my $fh, '>', $data_file ) ) { Cpanel::MysqlUtils::Stream::stream_mysqldump_to_filehandle( { 'dbuser' => $from_profile_hr->{'mysql_user'}, 'dbpass' => $from_profile_hr->{'mysql_pass'}, 'dbhost' => $from_profile_hr->{'mysql_host'}, 'dbport' => $from_profile_hr->{'mysql_port'}, 'db' => $eximstats_db, 'options' => [ '--disable-keys', '--skip-create-options', '--skip-add-drop-table', '--no-create-db', '--no-create-info', '--skip-add-locks', '--skip-comments', '--skip-disable-keys', '--force', '--complete-insert', '--replace', ], 'filehandle' => $fh } ); return $data_file; } }; my $sql_execute = Cpanel::MysqlUtils::build_mysql_exec_env( { 'dbuser' => $from_profile_hr->{'mysql_user'}, 'dbpass' => $from_profile_hr->{'mysql_pass'}, 'dbhost' => $from_profile_hr->{'mysql_host'}, 'dbport' => $from_profile_hr->{'mysql_port'}, 'db' => $eximstats_db, } ); my $cleanup_eximstats_files = sub { unlink $schema_file || print "[!] Unable to clean up '$schema_file' (eximstats schema file): $!\n"; unlink $data_file || print "[!] Unable to clean up '$data_file' (eximstats data file): $!\n"; }; my $eximstats_schema_file = $local_eximstats_schema->(); if ( $eximstats_schema_file && -e $eximstats_schema_file && -s _ ) { if ( !Cpanel::MysqlUtils::Command::db_exists('eximstats') ) { print "[*] Creating the eximstats database on the current active MySQL server …\n"; Cpanel::MysqlUtils::Command::sqlcmd('CREATE DATABASE eximstats'); if ( open( my $fh, '<', $eximstats_schema_file ) ) { Cpanel::MysqlUtils::Stream::stream_filehandle_to_mysql( { 'filehandle' => $fh, 'db' => 'eximstats', } ); close($fh); } print "[+] Creating the eximstats database on the current active MySQL server … Done.\n"; } my $changes_to_schema = Cpanel::SafeRun::Errors::saferunnoerror( Cpanel::Binaries::get_binary_location('mysqldiff'), $eximstats_schema_file, 'eximstats' ); if ( $changes_to_schema =~ m/^\s*DROP/im ) { $cleanup_eximstats_files->(); die "[!] The eximstats transfer cannot continue. The database schema differs too much; you must perform the transfer manually.\n"; } else { print "[*] Making changes to the eximstats database on the '$from_profile' server …\n"; $sql_execute->($changes_to_schema); print "[*] Making changes to the eximstats database on the '$from_profile' server … Done.\n"; print "[*] Downloading eximstats data from '$from_profile' server …\n"; my $eximstats_data_file = $local_eximstats_data->(); if ( $eximstats_data_file && -e $eximstats_data_file && -s _ ) { print "[*] Downloading eximstats data from '$from_profile' server … Done.\n"; print "[*] Sending data to active MySQL server …\n"; if ( open( my $fh, '<', $eximstats_data_file ) ) { Cpanel::MysqlUtils::Stream::stream_filehandle_to_mysql( { 'filehandle' => $fh, 'db' => 'eximstats', } ); close($fh); } print "[+] Sending data to active MySQL server … Done.\n"; } else { print "[!] Failed to download eximstats data from '$from_profile' server.\n"; } } } else { print "[!] Unable to fetch eximstats schema from '$from_profile' server.\n"; } $cleanup_eximstats_files->(); print "[*] Configuring 'eximstats' database user …\n"; Cpanel::SafeRun::Errors::saferunnoerror('/usr/local/cpanel/bin/generate_eximstats_pass') if !-e '/var/cpanel/eximstatspass'; my $eximpass = Cpanel::MysqlUtils::Quote::safesqlstring( Cpanel::EximStats::fetch_pass() ); my $mainip = Cpanel::MysqlUtils::Quote::safesqlstring( Cpanel::DIp::MainIP::getmainserverip() ); my $hostname = Cpanel::MysqlUtils::Quote::safesqlstring( Cpanel::Sys::Hostname::gethostname() ); Cpanel::MysqlUtils::Command::sqlcmd("GRANT ALL PRIVILEGES ON eximstats.* TO eximstats\@'$hostname' IDENTIFIED BY '$eximpass' WITH GRANT OPTION;"); Cpanel::MysqlUtils::Command::sqlcmd("GRANT ALL PRIVILEGES ON eximstats.* TO eximstats\@'$mainip' IDENTIFIED BY '$eximpass' WITH GRANT OPTION;"); # This addresses concerns with edge cases where 'skip_name_resolve' is set # on the remote MySQL server, and we can't depend on the 'hostname' authentication. # Especially, when the mysql server sees the connecting ip as something different than what the 'main ip' is, etc. # # In order to ensure that we are granting access to the 'proper' IP - we check the process list in mysql # to see what the 'connecting' IP is according to the remote MySQL server, and apply the grants on that IP address. my $clientip = Cpanel::MysqlUtils::Quote::safesqlstring( Cpanel::MysqlUtils::Command::sqlcmd('SELECT SUBSTRING_INDEX(`host`,":",1) FROM `information_schema`.`processlist` WHERE ID = CONNECTION_ID();') ); Cpanel::MysqlUtils::Command::sqlcmd("GRANT ALL PRIVILEGES ON eximstats.* TO eximstats\@'$clientip' IDENTIFIED BY '$eximpass' WITH GRANT OPTION;"); print "[+] Configuring 'eximstats' database user … Done.\n"; print "\n[+] Transferring eximstats database from '$from_profile' MySQL profile … Done.\n"; } catch { _handle_failure( { 'action' => 'activate', 'exception' => $_ } ); }; return 1; } sub usage { my ( $retval, $msg ) = @_; my $fh = $retval ? \*STDERR : \*STDOUT; if ( !defined $msg ) { $msg = <<USAGE; $0 Utility to transfer the 'eximstats' database from one MySQL profile to the active MySQL profile. --from [profile name] Transfers eximstats from the specified MySQL profile. --help Displays this help message. USAGE } print {$fh} $msg; return $retval; } sub _handle_failure { my $opts = shift; my $action = $opts->{'action'}; my $exceptions = ref $opts->{'exception'} eq 'HASH' ? $opts->{'exception'}->{'exceptions'} : [ $opts->{'exception'} ]; my $logger = Cpanel::Logger->new(); $logger->info( "Failed to $action MySQL profile. " . scalar @{$exceptions} . " error(s) occurred." ); my $index = 1; foreach my $error ( @{$exceptions} ) { $logger->info( "Error $index: " . Cpanel::Exception::get_string($error) ); $index++; } return 1; }