support attribute cleanup for nice single value column
[gnt-info] / gnt-i
diff --git a/gnt-i b/gnt-i
index f1aa4fa..6b1dade 100755 (executable)
--- a/gnt-i
+++ b/gnt-i
@@ -2,29 +2,50 @@
 use warnings;
 use strict;
 use autodie;
+use Storable;
 
 my $DEBUG = $ENV{DEBUG} || 0;
+my $INFLUX = 'http://10.60.0.89:8186/write';
 
 my $hostname = `hostname -s`;
 chomp $hostname;
 
 use Data::Dumper;
-sub XXX { warn "XXX ",Dumper( @_ ) };
+sub XXX { $DEBUG ? warn "XXX ",Dumper( @_ ) : {} };
 
+my $stat;
+my $mac_to_name;
+my $instance_tap;
+my $tap_instance;
+my $lines;
+
+my @nodes = @ARGV;
+
+@nodes = map { chomp ; s/\s+N$// ; $_ } grep { /N$/ } `gnt-node list -o name,offline --no-header` unless @nodes;
+
+next_node:
 my $ssh = '';
-if ( @ARGV ) {
-       $hostname = $ARGV[0];
+if ( @nodes ) {
+       $hostname = shift @nodes;
        $ssh = "ssh $hostname ";
+       $hostname =~ s/\..+$//; # -s
+       warn "## hostname $hostname\n";
 }
 
 # if prefixed with _ it will be hiddden from output, _args must be last!
-my @ps_cols = qw( user pid pcpu pmem vsz cputime etimes _args);
+my @ps_cols = qw( user pid
+       pcpu pmem
+       vsz
+       rss
+       nlwp
+       cputime etimes 
+       maj_flt min_flt
+       psr
 
-sub ps_cols_all { map { my $t = $_; $t =~ s/^_//; $t } @ps_cols };
-sub ps_cols_visible { grep { ! /^_/ } @ps_cols };
+       _args);
 
-my $stat;
-my $mac_to_name;
+sub ps_cols_all { map { my $t = $_; $t =~ s/^_//; $t } @ps_cols };
+sub ps_cols_visible { 'node', grep { ! /^_/ } @ps_cols, 'rx', 'tx' };
 
 sub DD_hh_mm_ss {
        my $t = shift;
@@ -40,19 +61,26 @@ sub DD_hh_mm_ss {
                $t_sec *= $f[$i];
        }
 
-       warn "# DD-hh:mm:ss $t -> $t_sec\n";
+#      warn "# DD-hh:mm:ss $t -> $t_sec\n" if $DEBUG;
        return $t_sec;
 }
 
-my $cmd = 'ps --no-headers axwwo ' . join(',', ps_cols_all);
-warn "## $cmd\n";
-open(my $ps, '-|', $ssh . $cmd);
+sub sh {
+       my $cmd = join(' ', @_);
+       $cmd = "$ssh '$cmd'";
+       warn "## $cmd\n";
+       open(my $fh, '-|', $cmd);
+       return $fh;
+}
+
+my $ps = sh 'ps --no-headers axwwo ' . join(',', ps_cols_all);
 while(<$ps>) {
        chomp;
        s/^\s*//;
 
        my %h;
        @h{@ps_cols} = split(/\s+/, $_, $#ps_cols + 1);
+       $h{cputime} = DD_hh_mm_ss( $h{cputime} );
 
 #XXX 'h = ', \%h;
        if ( $h{user} =~ m/gnt/ && $h{_args} =~ m/qemu.*-name\s+(\S+)/ ) {
@@ -60,41 +88,104 @@ while(<$ps>) {
                my $name = $1;
                $stat->{$name}->{$_} = $h{$_} foreach ps_cols_all;
 
-               $stat->{$name}->{cputime_} = $stat->{$name}->{cputime} = DD_hh_mm_ss( $stat->{$name}->{cputime} );
+               $stat->{$name}->{node} = $hostname;
 
                while ( $h{_args} =~ m/mac=([0-9a-fA-F:]+)/g ) {
-                       $mac_to_name->{$1} = $name;
+                       $mac_to_name->{$hostname}->{$1} = $name;
                }
 
        } else {
 #              warn "## SKIP [$_]\n";
-               $stat->{ '__' . $hostname }->{$_} += $h{$_} foreach qw( pcpu pmem vsz );
+               $stat->{ '__' . $hostname }->{$_} += $h{$_} foreach qw( pcpu pmem vsz rss
+                       nlwp cputime etimes
+                       maj_flt min_flt
+               );
                
        }
 
 }
 
-warn "# mac_to_name ", XXX( $mac_to_name );
 
-open(my $ip, '-|', $ssh . 'ip -s -o link');
+my $tap = sh('grep -H . /var/run/ganeti/kvm-hypervisor/nic/*/*');
+while(<$tap>) {
+       chomp;
+       my @p = split(/\//,$_);
+#      warn "## tap ", Dumper( \@p ), "$_\n";
+       push @{ $instance_tap->{$hostname}->{ $p[-2] } }, $p[-1];
+
+       my $if = $p[-1];
+       $if =~ s/\d://;
+       $tap_instance->{$hostname}->{$if} = $p[-2];
+}
+
+=for later
+
+my $ip = sh('ip -s -o link');
 while(<$ip>) {
        chomp;
        if ( m/master\s+(\S+).+ether\s+([0-9a-fA-F:]+).+RX:\s+.+\\\s+(\d+).+TX:\s+.+\\\s+(\d+)/ ) {
-               my ( $br, $mac, $rx, $tx ) = ( $1, $2, $3, $4 );
-               if ( my $name = $mac_to_name->{$mac} ) {
-                       $stat->{$name}->{link}->{ $br } = [ $rx, $tx ];
+               my ( $if, $mac, $rx, $tx ) = ( $1, $2, $3, $4 );
+               if ( my $name = $tap_instance->{$hostname}->{$if} ) {
+                       $stat->{$name}->{link}->{ $if } = [ $rx, $tx ];
+                       warn "## ip $name $ip $mac $rx $tx\n";
                } else {
-                       warn "## SKIP MAC $mac [$_]\n" if $DEBUG;
+                       warn "## ip SKIP $if $mac $rx $tx\n"; # XXX if $DEBUG;
                }
        } else {
                warn "## SKIP $_\n" if $DEBUG;
        }
 }
 
-warn "# stat = ", XXX( $stat );
-XXX( @ps_cols );
+=cut
+
+my $dev = sh 'grep : /proc/net/dev';
+while(<$dev>) {
+       chomp;
+       s/^\s+//;
+       my @l = split(/[:\s]+/, $_);
+#warn "XXX $_ -> ",Dumper( \@l );
+       if ( my $instance = $tap_instance->{$hostname}->{ $l[0] } ) {
+                       $stat->{$instance}->{rx} += $l[1];
+                       $stat->{$instance}->{tx} += $l[9];
+       } elsif ( $l[0] =~ m/eth/ ) { # connect hardware eth devices under node
+                       $stat->{ '__' . $hostname }->{rx} += $l[1];
+                       $stat->{ '__' . $hostname }->{tx} += $l[9];
+       }
+}
+
+
+goto next_node if @nodes;
+
+
+
+# dump some useful data structures
+
+sub tab_dump {
+       my ( $name, $hash ) = @_;
+       warn "# $name\n";
+       foreach my $key ( sort keys %$hash ) {
+               warn $key, "\t", $hash->{$key}, "\n";
+       }
+}
+
+
+
+foreach my $node ( sort keys %$mac_to_name ) {
+       tab_dump "$node mac instance",  $mac_to_name->{$node};
+}
+#warn Dumper( $mac_to_name );
+#tab_dump 'instance_tap', $instance_tap;
+#warn Dumper( $instance_tap );
+#warn Dumper( $tap_instance );
+
+
+warn "# stat ", Dumper( $stat ) if $DEBUG;
+
+
+# dump tablable ascii output
+
+#XXX( @ps_cols );
 
-my $lines;
 sub push_line {
        my @l = @_;
        foreach my $i ( 0 .. $#l ) {
@@ -102,7 +193,7 @@ sub push_line {
                $lines->{len}->[$i] ||= $len;
                $lines->{len}->[$i] = $len if $len > $lines->{len}->[$i];
        }
-       push @{ $lines->{line} }, [ @l ];
+       push @{ $lines->{line} }, [ map { ! defined $_ ? '-' : $_ } @l ];
 }
 
 push_line '#name', ps_cols_visible;
@@ -113,10 +204,36 @@ foreach my $name ( sort keys %$stat ) {
        push_line( $name, map { $stat->{$name}->{$_} } ps_cols_visible );
 }
 
-XXX $lines;
+#XXX $lines;
+
+open(my $influx_fh, '>', '/dev/shm/ganeti.influx');
+
+my $last_lines;
+$last_lines = retrieve('/dev/shm/ganeti.last.storable') if -e '/dev/shm/ganeti.last.storable';
 
 my $fmt = join(' ', map { '%' . $_ . 's' } @{ $lines->{len} } ) . "\n";
-warn "# fmt = [$fmt]";
-foreach my $line ( @{ $lines->{line} } ) {
+warn "# fmt = [$fmt]" if $DEBUG;
+foreach my $i ( 0 .. $#{ $lines->{line} } ) {
+       my $line = $lines->{line}->[$i];
        printf $fmt, @$line;
+
+       next unless $last_lines;
+
+       my $last_line = $last_lines->[$i];
+       my @c = ( 'instance', ps_cols_visible );
+       my @influx;
+       foreach my $j ( 0 .. $#$line ) {
+               #push @influx, $c[$j] . '=' . $line->[$j];
+               push @influx, $c[$j] . '='
+                       . ( $j >= 9 && $j != 13 ? $line->[$j] - $last_line->[$j] : $line->[$j] )
+                       . ( $j == 3 || $j >= 6 && $j != 9 ? 'i' : '' );
+       }
+       my $influx = join(',', @influx);
+       $influx =~ s/,user=([^,]+)/ user="$1"/;
+       print $influx_fh "ganeti,$influx\n" unless $influx =~ m/(#|__)/;
+
 }
+
+store $lines->{line}, '/dev/shm/ganeti.last.storable';
+
+#system "curl -i -XPOST 'http://127.0.0.1:8186/write?db=ganeti' --data-binary '@/dev/shm/ganeti.influx'";