#!/usr/bin/env perl
use strict;
use warnings;

my %df = (Filesystem => 0, SizeMB => 1, UsedMB => 2, AvailMB => 3, UsePct => 4, MountedOn => 5);

my $alter_space = (@ARGV && $ARGV[0] eq '--alter-space') ? 1 : 0;

# disk info
my $cmd = q(for i in $(ls -1 /dev/sd* | grep -vP '\d+$' | sort); do i=${i##/dev/}; rotational=$(grep -q '^0' /sys/block/$i/queue/rotational && echo ssd || echo hdd); size=$(smartctl -i /dev/$i | grep 'User Capacity:' | grep -o '\[.*$'); echo "$rotational $size"; done | sort | uniq -c | grep -oP '\d+.*$' | tr '\n' '\t');
my $hdd_info = qx($cmd);
$hdd_info =~ s/^\s+|\s+$//g;
print "$hdd_info\n";

$cmd = q(cat /proc/cpuinfo | grep 'model name' | sort | uniq -c);
# 32 model name	: Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz
my $cpuinfo = qx($cmd);
$cpuinfo =~ /(\d+)\s+model\s+name\s+:\s+(.*)$/;
print "$1\tcores of\t$2\n";

$cmd = q(free -g | grep Mem: | awk '{ print $2 }');
chomp(my $total_mem = qx($cmd));
print "$total_mem\tGB total ram\n";

my %instances = map { $_ => {'datadir' => undef, 'log-bin' => undef, 'innodb_buffer_pool_size' => undef} } split /\n/, qx(lm --complete);

for my $db (sort keys %instances) {
    my $conf = $instances{$db};
    my %dir_usage;
    for my $key (sort keys %{$conf}) {
        $conf->{$key} = (split(/\s*=\s*/, qx(grep $key /etc/mysql/$db.cnf /etc/mysql/$db.conf.d/* | tail -n 1)))[1];
        next if not $conf->{$key};
        chomp $conf->{$key};
        next if $key eq 'innodb_buffer_pool_size';

        $conf->{$key} =~ s|^(/.*)/.*$|$1| if $key eq 'log-bin';
        $dir_usage{$key} = { df => get_size($conf->{$key}) };
        $dir_usage{$key}->{raid_type} = get_raid_type($dir_usage{$key}->{df}->[$df{Filesystem}]);
    }

    print "$db usage % and disk types, " . join("/", sort keys %dir_usage) . ":\t";
    print join("/", map { $dir_usage{$_}->{df}->[$df{UsePct}] } sort keys %dir_usage) . "\t";
    print join("/", map { $dir_usage{$_}->{raid_type} } sort keys %dir_usage) . "\n";

    if ($alter_space) {
        my $cmd = q(echo "select table_name as tbl, ceil((data_length + index_length) / 1024 / 1024) as size from information_schema.tables order by size desc limit 1" | lm -A ) . $db . q( | tail -n 1 | awk '{ print $NF }');
        my $max_tbl_size = qx($cmd);
        my $free_on_alter = $dir_usage{datadir}[$df{AvailMB}] - $max_tbl_size * 2;
        printf "$db space for alters: %s\n", ($free_on_alter > 0 ? "seems ok, but don't trust me" : sprintf "bad, max table: %.2f GB, avail: %.2f GB", $max_tbl_size / 1024, $dir_usage{datadir}[$df{AvailMB}] / 1024);
    }

    if ($conf->{innodb_buffer_pool_size}) {
        $conf->{innodb_buffer_pool_size} =~ s/\D+//g; # assume GB
        my $cmd = q(echo 'show variables like "innodb_buffer_pool_size"' | lm -A ) . $db . q( | tail -n 1 | awk '{ print $NF }');
        $conf->{innodb_buffer_pool_size_cur} = qx($cmd);
        $conf->{innodb_buffer_pool_size_cur} /= 1024**3;
        printf "$db innodb buffer pool config/current/ram:\t%d/%d/%d\n", $conf->{innodb_buffer_pool_size}, $conf->{innodb_buffer_pool_size_cur}, $total_mem;
    }
}

sub get_size {
    my ($dir) = @_;
    my @df_out = map { chomp; $_ } split /\s+/, qx(df -m $dir | tail -n+2);
    $df_out[$df{UsePct}] =~ s/%//;
    $df_out[$df{UsePct}] = $df_out[$df{UsePct}] > 10 ? int($df_out[$df{UsePct}] / 10 + 0.5) * 10 : int($df_out[$df{UsePct}] / 2 + 1) * 2;
    return \@df_out if $df_out[$df{Filesystem}] =~ '/dev/';

    my $cmd = q(stat ) . $dir . q( | grep -oP 'Device: \S+' | cut -d' ' -f2 | cut -d/ -f2);
    chomp(my $device_dec = qx($cmd));
    $device_dec =~ s/d//;
    my ($maj, $min) = ($device_dec >> 8, $device_dec & 0xff);
    $cmd = q(grep -P '^\s*) . $maj . q(\s+) . $min . q(\s+' /proc/partitions | awk '{ print $NF }');
    chomp($df_out[$df{Filesystem}] = qx($cmd));
    $df_out[$df{Filesystem}] = '/dev/' . $df_out[$df{Filesystem}];
    return \@df_out;
}

sub get_raid_type {
    my ($fs) = @_;
    my $cmd = q(mdadm --detail ) . $fs . q( 2>&1 | perl -lne '(/RaidDevice/ ... /_END_/) && print' | tail -n+2);
    my $md_out = qx($cmd);
    my ($hdd_type, $is_raid, $is_lvm) = ('unknown', $md_out && $md_out !~ /does not appear to be an md device/, $fs =~ m|/vg\d+|); 

    my @disks;
    if ($is_raid) {
        @disks = split /\n/, $md_out;
    } elsif ($is_lvm) {
        @disks = split /\n/, qx(pvs | grep '/dev' | grep -v '/md' | awk '{ print \$1 }'); 
    }

    if (@disks) {
        my %disks = map { m|/dev/(\D+)|; ($1 => undef) } @disks;
        @disks = sort keys %disks;
    
        my @rotational;
        for my $disk (@disks) {
            chomp(my $rot = qx(cat /sys/block/$disk/queue/rotational));
            push @rotational, $rot;
        }
        @rotational = sort {$a <=> $b} @rotational;
        if ($rotational[0] != $rotational[-1]) {
            $hdd_type = 'ssd-hdd-mixed';
        } else {
            $hdd_type = $rotational[0] ? 'hdd' : 'ssd';
        }
    }

    my @status = ($hdd_type);
    push(@status, "raid") if $is_raid;
    push(@status, "lvm") if $is_lvm;

    return join "_", @status;
}
