find tap device for instance on given node
[gnt-info] / gnt-i
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
4 use autodie;
5
6 my $DEBUG = $ENV{DEBUG} || 0;
7
8 my $hostname = `hostname -s`;
9 chomp $hostname;
10
11 use Data::Dumper;
12 sub XXX { $DEBUG ? warn "XXX ",Dumper( @_ ) : {} };
13
14 my $stat;
15 my $mac_to_name;
16 my $instance_tap;
17 my $lines;
18
19 my @nodes = @ARGV;
20
21 @nodes = map { chomp ; $_ } `gnt-node list -o name --no-header` unless @nodes;
22
23 next_node:
24 my $ssh = '';
25 if ( @nodes ) {
26         $hostname = shift @nodes;
27         $ssh = "ssh $hostname ";
28         $hostname =~ s/\..+$//; # -s
29         warn "## hostname $hostname\n";
30 }
31
32 # if prefixed with _ it will be hiddden from output, _args must be last!
33 my @ps_cols = qw( user pid
34         pcpu pmem
35         vsz
36         rss
37         nlwp
38         cputime etimes 
39         maj_flt min_flt
40         psr
41
42         _args);
43
44 sub ps_cols_all { map { my $t = $_; $t =~ s/^_//; $t } @ps_cols };
45 sub ps_cols_visible { 'node', grep { ! /^_/ } @ps_cols };
46
47 sub DD_hh_mm_ss {
48         my $t = shift;
49         # [[DD-]hh:]mm:ss.
50         my @f = reverse ( 24, 60, 60, 1 );
51         my @p = reverse split(/[-:]/, $t);
52         my $t_sec = 0;
53
54         for ( 0 .. $#p ) {
55                 my $i = $#p - $_;
56 #warn "### $i $p[$i] $f[$i]\n";
57                 $t_sec += $p[$i];
58                 $t_sec *= $f[$i];
59         }
60
61         warn "# DD-hh:mm:ss $t -> $t_sec\n" if $DEBUG;
62         return $t_sec;
63 }
64
65 sub sh {
66         my $cmd = join(' ', @_);
67         $cmd = "$ssh '$cmd'";
68         warn "## $cmd\n";
69         open(my $fh, '-|', $cmd);
70         return $fh;
71 }
72
73 my $ps = sh 'ps --no-headers axwwo ' . join(',', ps_cols_all);
74 while(<$ps>) {
75         chomp;
76         s/^\s*//;
77
78         my %h;
79         @h{@ps_cols} = split(/\s+/, $_, $#ps_cols + 1);
80         $h{cputime} = DD_hh_mm_ss( $h{cputime} );
81
82 #XXX 'h = ', \%h;
83         if ( $h{user} =~ m/gnt/ && $h{_args} =~ m/qemu.*-name\s+(\S+)/ ) {
84
85                 my $name = $1;
86                 $stat->{$name}->{$_} = $h{$_} foreach ps_cols_all;
87
88                 $stat->{$name}->{node} = $hostname;
89
90                 while ( $h{_args} =~ m/mac=([0-9a-fA-F:]+)/g ) {
91                         $mac_to_name->{$hostname}->{$1} = $name;
92                 }
93
94         } else {
95 #               warn "## SKIP [$_]\n";
96                 $stat->{ '__' . $hostname }->{$_} += $h{$_} foreach qw( pcpu pmem vsz rss cputime etimes maj_flt min_flt );
97                 
98         }
99
100 }
101
102
103 my $tap = sh('grep . /var/run/ganeti/kvm-hypervisor/nic/*/*');
104 while(<$tap>) {
105         chomp;
106         warn "## $tap\n";
107         my @p = split(/\//,$_);
108         push @{ $instance_tap->{$hostname}->{ $p[-2] } }, $p[-1];
109 }
110
111 my $ip = sh('ip -s -o link');
112 while(<$ip>) {
113         chomp;
114         if ( m/master\s+(\S+).+ether\s+([0-9a-fA-F:]+).+RX:\s+.+\\\s+(\d+).+TX:\s+.+\\\s+(\d+)/ ) {
115                 my ( $br, $mac, $rx, $tx ) = ( $1, $2, $3, $4 );
116                 if ( my $name = $mac_to_name->{$mac} ) {
117                         $stat->{$name}->{link}->{ $br } = [ $rx, $tx ];
118                 } else {
119                         warn "## SKIP MAC $mac [$_]\n" if $DEBUG;
120                 }
121         } else {
122                 warn "## SKIP $_\n" if $DEBUG;
123         }
124 }
125
126 # dump some useful data structures
127
128 sub tab_dump {
129         my ( $name, $hash ) = @_;
130         warn "# $name\n";
131         foreach my $key ( sort keys %$hash ) {
132                 warn $key, "\t$hostname\t", $hash->{$key}, "\n";
133         }
134 }
135
136 tab_dump 'mac_to_name',  $mac_to_name;
137 warn Dumper( $mac_to_name );
138 tab_dump 'instance_tap', $instance_tap;
139 warn Dumper( $instance_tap );
140
141 goto next_node if @nodes;
142
143
144 warn "# stat ", Dumper( $stat ) if $DEBUG;
145
146
147 # dump tablable ascii output
148
149 XXX( @ps_cols );
150
151 sub push_line {
152         my @l = @_;
153         foreach my $i ( 0 .. $#l ) {
154                 my $len = length($l[$i]) || 0;
155                 $lines->{len}->[$i] ||= $len;
156                 $lines->{len}->[$i] = $len if $len > $lines->{len}->[$i];
157         }
158         push @{ $lines->{line} }, [ map { ! defined $_ ? '-' : $_ } @l ];
159 }
160
161 push_line '#name', ps_cols_visible;
162
163 foreach my $name ( sort keys %$stat ) {
164 #       printf "%6.2f %6.2f %8d %6d %6s %s\n", ( map { $stat->{$name}->{$_} || '' } qw( pcpu pmem vsz pid user ) ), $name;
165 #       print join("\t", $name, map { $stat->{$name}->{$_} } ps_cols_visible ), "\n";
166         push_line( $name, map { $stat->{$name}->{$_} } ps_cols_visible );
167 }
168
169 XXX $lines;
170
171 my $fmt = join(' ', map { '%' . $_ . 's' } @{ $lines->{len} } ) . "\n";
172 warn "# fmt = [$fmt]" if $DEBUG;
173 foreach my $line ( @{ $lines->{line} } ) {
174         printf $fmt, @$line;
175 }
176