#!/bin/perl -w # # System Data Recorder: netrec # # netrec - records TCP, UDP and IP telemetry # from netstat for each zone under Solaris OE # # USAGE: netrec [-nshv] | [interval [count]] # eg, # netrec 60 # print continously every minute # # TCP and UDP stats # # COPYRIGHT: Copyright (c) 2006 Brendan Gregg. # COPYRIGHT: Copyright (c) 2008 Stefan Parvu. # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at Docs/cddl1.txt # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # CDDL HEADER END # # VERSION: 0.63 # # HISTORY # 2007-10-16 first release, ksh based - 0.50 sp # 2008-09-09 new Perl version, RRD format output - 0.60 sp # 2009-02-04 no zone option - 0.63 sp use strict; use Getopt::Std; use Time::Local; # # Command line arguments # usage() if defined $ARGV[0] and $ARGV[0] eq "--help"; getopts('nshv') or usage(); usage() if defined $main::opt_h; revision() if defined $main::opt_v; my $shared = defined $main::opt_s ? $main::opt_s : 0; my $nozones = defined $main::opt_n ? $main::opt_n : 0; # process [[interval [count]] my ($interval, $loop_max); if (defined $ARGV[0]) { $interval = $ARGV[0]; $loop_max = defined $ARGV[1] ? $ARGV[1] : 2**32; usage() if $interval == 0; } else { $interval = 1; $loop_max = 1; } # globals $| = 1; # autoflush my $loop = 0; # current loop number $main::opt_h = 0; $main::opt_v = 0; $main::opt_n = 0; my $currz; # ######### # # MAIN BODY # # ######### # if ($nozones == 0) { $currz = `/usr/bin/zonename`; chomp($currz); } else { $currz = "global"; } while (1) { my (@res,@netdata); if ($nozones == 1 ) { # we have no zones @res = get_netstat(0,"global"); next if ! defined $res[0]; print "$currz:" . time . ":$res[5]:$res[6]:$res[7]:$res[8]:$res[13]:$res[14]" . ":$res[15]:$res[16]:$res[17]:$res[18]:$res[19]:$res[20]" . ":$res[21]:$res[22]:$res[56]:$res[57]:$res[58]:$res[59]" . ":$res[63]:$res[64]:$res[65]:$res[84]:$res[86]\n"; } else { if ( $currz !~ /global/ ) { # call get_netstat for this localzone only @res = get_netstat(0,"$currz"); next if ! defined $res[0]; print "$currz:" . time . ":$res[5]:$res[6]:$res[7]:$res[8]:$res[13]:$res[14]" . ":$res[15]:$res[16]:$res[17]:$res[18]:$res[19]:$res[20]" . ":$res[21]:$res[22]:$res[56]:$res[57]:$res[58]:$res[59]" . ":$res[63]:$res[64]:$res[65]:$res[84]:$res[86]\n"; } my @zones = `/usr/sbin/zoneadm list`; foreach my $z (@zones) { chomp($z); if ( $z =~ /global/ ) { @res = get_netstat(0,"global"); next if ! defined $res[0]; print "$z:" . time . ":$res[5]:$res[6]:$res[7]:$res[8]:$res[13]:$res[14]" . ":$res[15]:$res[16]:$res[17]:$res[18]:$res[19]:$res[20]" . ":$res[21]:$res[22]:$res[56]:$res[57]:$res[58]:$res[59]" . ":$res[63]:$res[64]:$res[65]:$res[84]:$res[86]\n"; # Debug # print "Total elements: $#res\n"; next; } # we need to zlogin each zone @res = get_netstat(1,"$z"); next if ! defined $res[0]; if ( $shared == 0 ) { print "$z:" . time . ":$res[5]:$res[6]:$res[7]:$res[8]:$res[13]:$res[14]" . ":$res[15]:$res[16]:$res[17]:$res[18]:$res[19]:$res[20]" . ":$res[21]:$res[22]:$res[56]:$res[57]:$res[58]:$res[59]" . ":$res[63]:$res[64]:$res[65]:$res[84]:$res[86]\n"; } else { print "$z:" . time . ":$res[17]\n"; } # Debug # print "Total elements: $#res\n"; } } ### Check for end last if ++$loop == $loop_max; ### Interval sleep $interval; } # get_netstat - get netstat stats from each zone # 0 - global zone # 1 - local zone sub get_netstat { my (@net,@temp); if ($_[0] == 0) { open (NETSTAT, "netstat -s |") or die "Can't run netstat: $!\n"; } else { open (NETSTAT, "/usr/sbin/zlogin -S $_[1] netstat -s 2>/dev/null |") or die "Can't run netstat: $!\n"; } while() { chomp($_); next if $_ =~ /^$/; $_ =~ s/^RAWIP|UDP|TCP|IPv4|IPv6//; $_ =~ s/^\s+//; last if $_ =~ /ICMPv4/; push @temp, split /\w+\s*\=\s*/ , $_; # --- debug --- # print "$_\n"; } close (NETSTAT); # --- debug --- # print "@temp"; # remove here the empty lines, and load the final array # but it can be done much easier in one for loop foreach (@temp) { next if $_ =~ /^$/; $_ =~ s/\s+$//; push @net, $_; } # --- debug --- # print "Total elements: $#net\n"; # foreach my $x (@net) { # print "$x\n"; # } return @net; } # usage - print usage and exit. # sub usage { print STDERR <