????

Your IP : 18.227.183.131


Current Path : /scripts/
Upload File :
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;
}