gather.in 8.28 KB
Newer Older
1 2
#!/usr/bin/perl 
#
3
# Copyright (c) 2009-2018 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
23 24 25 26 27 28 29
#
use English;
use Getopt::Std;
use POSIX qw(strftime floor ceil);
use Data::Dumper;
use Carp;

30 31
my $LIMIT = 3000000;

32 33 34 35 36
$Data::Dumper::Indent = 1;

use strict;
use warnings;

37
our $NODE_USAGE_DB;
38
our @NODE_OOS_EXPT;
39 40
require "@prefix@/etc/node_usage.conf";

41 42
my $prep    = 1;
my $results = 1;
43 44
my $debug   = 0;
my $fresh   = 0;
45 46 47 48 49

# Turn off line buffering on output
$| = 1; 

# Load the Testbed support stuff.
50
use lib "@prefix@/lib";
51 52 53
use libdb;
use libtestbed;

54
chdir "@prefix@/data/node_usage";
55

Kevin Atkinson's avatar
Kevin Atkinson committed
56 57
$fresh = 1 unless -e "gather.state.1" && "gather.state.2";

58 59 60 61 62 63 64 65 66 67 68 69 70 71
my %node_type;
my %node_class;

my $qr = DBQueryFatal("select node_id,type from nodes");
while (my ($node_id, $type) = $qr->fetchrow()) {
    $node_type{$node_id} = $type;
}

$qr = DBQueryFatal("select class,type from node_types");
while (my ($class,$type) = $qr->fetchrow()) {
    $node_class{$type} = $class;
}
$node_class{'?'} = '?';

72 73 74 75 76 77 78 79
my %oos = ();
if (@NODE_OOS_EXPT > 0) {
    my $eidclause = join(" or ", map("eid='$_'", @NODE_OOS_EXPT));
    $qr = DBQueryFatal("select idx from experiments where pid='emulab-ops'".
		       " and ($eidclause)");
    while (my ($idx) = $qr->fetchrow()) {
	$oos{$idx} = 1;
    }
80 81
    print "Will skip experiments: ", join(' ', keys %oos), "\n"
	if ($debug);
82 83 84 85 86
}

# XXX find the reloadpending exptid for a hack below
$qr = DBQueryFatal("select idx from experiments where pid='emulab-ops'".
		   " and eid='reloadpending'");
87
my $rpidx = $qr->fetchrow();
88

89 90 91 92 93
our %last_trans;
our %node_state;
our $prev;
our $prev_line;
our $last_history_id = -1;
94 95
our %last_expidx;
our %maybe_free;
96

97 98
my $again = 0;

99 100
if ($prep) {

101 102 103 104 105 106 107
    if ($fresh) {
	DBQueryFatal("create database if not exists $NODE_USAGE_DB");
	DBQueryFatal("drop table if exists $NODE_USAGE_DB.node_history_copy");
	DBQueryFatal("create table $NODE_USAGE_DB.node_history_copy like node_history");
    }

    DBQueryFatal("insert into $NODE_USAGE_DB.node_history_copy select * from node_history where history_id > IFNULL((select max(history_id) from $NODE_USAGE_DB.node_history_copy), 0)");
108 109

    if ($fresh) {
110 111
	DBQueryFatal("drop table if exists $NODE_USAGE_DB.node_trans");
	DBQueryFatal("create table $NODE_USAGE_DB.node_trans (".
112 113
		     "  history_id int unsigned not null primary key,".
		     "  stamp int unsigned not null, ".
114
		     "  node_id char(32) not null, ".
115
		     "  op enum('alloc','free','invalid','oos') not null".
116 117 118 119 120
		     ")");
    }
    
    local %last_trans;
    local %node_state;
121 122
    local %last_expidx;
    local %maybe_free;
123

Kevin Atkinson's avatar
Kevin Atkinson committed
124
    if (!$fresh) {
125
	do "./gather.state.1";
126 127
    }
    
128
    $qr = DBQueryFatal("select history_id,node_id,op,stamp,exptidx from $NODE_USAGE_DB.node_history_copy where history_id > $last_history_id order by history_id limit $LIMIT");
129 130 131 132

    if ($qr->num_rows() == $LIMIT) {
	$again = 1;
    }
133

Kevin Atkinson's avatar
Kevin Atkinson committed
134 135 136
    my $prev_history_id = $last_history_id;

    local $last_history_id = $prev_history_id;
137
    
138
    while (my ($history_id,$node_id,$op,$stamp,$exptidx) = $qr->fetchrow()) {
139 140 141
	my $type = $node_type{$node_id};
	$type = '?' unless defined $type;
	next unless $node_class{$type} eq 'pc';
142 143

	$exptidx = 0 unless defined $exptidx;
144
	my $prev_state = $node_state{$node_id};
145 146 147 148 149 150 151 152
	my $prev_idx = $last_expidx{$node_id};
	if ($op eq "free") {
	    $node_state{$node_id} = "free";
	} elsif ($oos{$exptidx}) {
	    $node_state{$node_id} = "oos";
	} else {
	    $node_state{$node_id} = "alloc";
	}
153 154
	my $invalid;
	if (defined $prev_state) {
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
	    #
	    # XXX We had a rough patch in 7/07-8/07 where the history records
	    # show nodes going from reloadpending directly into another
	    # experiment and back to reloadpending (i.e., there were no
	    # "free" records").
	    #
	    # We paper over those here since they are predictable. If
	    # we get consecutive alloc events where the previous one was
	    # to reloadpending (rather, the experiment ID indicates
	    # reloadpending), we do back and mark the previous event
	    # as a free.
	    #
	    if ($op eq 'alloc' && $prev_state ne 'free') {
		if ($prev_state eq 'alloc' && $prev_idx == $rpidx &&
		    $maybe_free{$node_id}) {
		    my $fid = $maybe_free{$node_id}->[0];
		    my $fts = $maybe_free{$node_id}->[1];
		    $prev_state = 'free';
		    $prev_idx = $fid;
		    DBQueryFatal("replace into $NODE_USAGE_DB.node_trans values ($fid, $fts, '$node_id', 'free')");
		} else {
		    $invalid = "alloc non-free node";
		}
	    } elsif ($op eq 'move') {
		#
		# If moving into reloadpending, remember the entry as
		# we might need to insert it as a free entry later
		#
		$maybe_free{$node_id} = [ $history_id, $stamp ]
		    if ($exptidx == $rpidx);
		# keep track of the current exptidx
		$last_expidx{$node_id} = $exptidx
		    if ($prev_state eq 'alloc');
		#
		# If we were out of service and are being moved out,
		# record this as an allocated event.
		#
		if ($prev_state eq 'oos' and $node_state{$node_id} ne 'oos') {
		    $node_state{$node_id} = 'alloc';
		}
		#
		# If we are a free node and moved into another experiment
		# consider it an allocation or 'oos' event.
		#
		elsif ($prev_state eq 'free') {
		    if ($oos{$exptidx}) {
			$node_state{$node_id} = 'oos';
		    } else {
			$node_state{$node_id} = 'alloc';
		    }
		}
	    } elsif ($op eq 'free') {
		delete $maybe_free{$node_id};
		$invalid = "free already free node"
		    if ($prev_state eq 'free');
	    }
211 212
	}
	if ($invalid) {
213
	    #print STDERR "WARNING: $node_id: $history_id ($stamp) $last_trans{$node_id}: $invalid\n";
214
	    DBQueryFatal("update $NODE_USAGE_DB.node_trans set op = 'invalid' where history_id=$last_trans{$node_id}");
215
	} elsif (!defined $prev_state || $prev_state ne $node_state{$node_id}) {
216
	    DBQueryFatal("replace into $NODE_USAGE_DB.node_trans values ($history_id, $stamp, '$node_id', '$node_state{$node_id}')");
217
	    $last_trans{$node_id} = $history_id;
218
	    $last_expidx{$node_id} = $exptidx;
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
	}
	$last_history_id = $history_id;
    }

    open F, ">gather.state.1";
    print F Data::Dumper->Dump([\%last_trans], ['*last_trans']);
    print F Data::Dumper->Dump([\%node_state], ['*node_state']);
    print F Data::Dumper->Dump([$last_history_id], ['*last_history_id']);
    close F;
}

if ($results) {

    local %node_state;
    local $prev = 0;
    local $prev_line = '';
    
Kevin Atkinson's avatar
Kevin Atkinson committed
236
    if ($fresh) {
237 238
	unlink "node_usage.raw";
    } else {
239
	do "./gather.state.2";
240 241 242 243
    }

    open F, ">>node_usage.raw";

244
    $qr = DBQueryFatal("select history_id,stamp,node_id,op from $NODE_USAGE_DB.node_trans where history_id > $last_history_id order by history_id");
245 246 247 248 249 250 251 252 253 254 255 256
    
    while (my ($history_id,$stamp,$node_id,$op) = $qr->fetchrow()) {
	my $type = $node_type{$node_id};
	$type = '?' unless defined $type;
	$node_state{$node_id} = $op;
	my %tally;
	while (my ($n, $s) = each %node_state) {
	    my $t = $node_type{$n};
	    $t = '?' unless defined $t;
	    $tally{$t}[0]++;
	    $tally{$t}[1]++ if $s eq 'free';
	    $tally{$t}[2]++ if $s eq 'invalid';
257
	    $tally{$t}[3]++ if $s eq 'oos';
258 259 260 261 262
	}
	my $line = "$history_id $stamp ";
	foreach my $t (sort keys %tally) {
	    $tally{$t}[1] = 0 unless defined $tally{$t}[1];
	    $tally{$t}[2] = 0 unless defined $tally{$t}[2];
263 264
	    $tally{$t}[3] = 0 unless defined $tally{$t}[3];
	    $line .= " $t: $tally{$t}[0] $tally{$t}[1] $tally{$t}[2] $tally{$t}[3] ";
265 266 267 268 269 270 271 272 273 274 275 276 277
	}
	$line .= "\n";
	print F $prev_line if $stamp != $prev;
	$prev = $stamp;
	$prev_line = $line;
    }

    open F, ">gather.state.2";
    print F Data::Dumper->Dump([\%node_state], ['*node_state']);
    print F Data::Dumper->Dump([$prev, $prev_line], ['*prev', '*prev_line']);
    close F;
}

278 279 280 281
if ($again) {
    printf STDERR "Too many rows to handle at once, running again...\n";
    exec "@prefix@/libexec/node_usage/gather";
}