#!/usr/bin/perl use warnings; use strict; use autodie; my $DEBUG = $ENV{DEBUG} || 0; my $hostname = `hostname -s`; chomp $hostname; use Data::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 ; $_ } `gnt-node list -o name --no-header` unless @nodes; next_node: my $ssh = ''; 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 rss nlwp cputime etimes maj_flt min_flt psr _args); 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; # [[DD-]hh:]mm:ss. my @f = reverse ( 24, 60, 60, 1 ); my @p = reverse split(/[-:]/, $t); my $t_sec = 0; for ( 0 .. $#p ) { my $i = $#p - $_; #warn "### $i $p[$i] $f[$i]\n"; $t_sec += $p[$i]; $t_sec *= $f[$i]; } # warn "# DD-hh:mm:ss $t -> $t_sec\n" if $DEBUG; return $t_sec; } 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+)/ ) { my $name = $1; $stat->{$name}->{$_} = $h{$_} foreach ps_cols_all; $stat->{$name}->{node} = $hostname; while ( $h{_args} =~ m/mac=([0-9a-fA-F:]+)/g ) { $mac_to_name->{$hostname}->{$1} = $name; } } else { # warn "## SKIP [$_]\n"; $stat->{ '__' . $hostname }->{$_} += $h{$_} foreach qw( pcpu pmem vsz rss nlwp cputime etimes maj_flt min_flt ); } } 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 ( $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 "## ip SKIP $if $mac $rx $tx\n"; # XXX if $DEBUG; } } else { warn "## SKIP $_\n" if $DEBUG; } } =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 ); sub push_line { my @l = @_; foreach my $i ( 0 .. $#l ) { my $len = length($l[$i]) || 0; $lines->{len}->[$i] ||= $len; $lines->{len}->[$i] = $len if $len > $lines->{len}->[$i]; } push @{ $lines->{line} }, [ map { ! defined $_ ? '-' : $_ } @l ]; } push_line '#name', ps_cols_visible; foreach my $name ( sort keys %$stat ) { # printf "%6.2f %6.2f %8d %6d %6s %s\n", ( map { $stat->{$name}->{$_} || '' } qw( pcpu pmem vsz pid user ) ), $name; # print join("\t", $name, map { $stat->{$name}->{$_} } ps_cols_visible ), "\n"; push_line( $name, map { $stat->{$name}->{$_} } ps_cols_visible ); } #XXX $lines; my $fmt = join(' ', map { '%' . $_ . 's' } @{ $lines->{len} } ) . "\n"; warn "# fmt = [$fmt]" if $DEBUG; foreach my $line ( @{ $lines->{line} } ) { printf $fmt, @$line; }