#!/usr/bin/perl -w # # Copyright (c) 2014 University of Utah and the Flux Group. # # {{{GENIPUBLIC-LICENSE # # GENI Public License # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and/or hardware specification (the "Work") to # deal in the Work without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Work, and to permit persons to whom the Work # is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Work. # # THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS # IN THE WORK. # # }}} # use strict; use English; use Getopt::Std; # # Populate the monitoring database. # sub usage() { print "Usage: mondbd [-d] [-i]\n"; exit(1); } my $optlist = "d"; my $debug = 0; # # Configure variables # my $TB = "@prefix@"; my $TBOPS = "@TBOPSEMAIL@"; my $DBNAME = "@TBDBNAME@"; my $DOMAIN = "@OURDOMAIN@"; my $URL = "https://www.$DOMAIN:5001"; my $PGENISUPPORT = @PROTOGENI_SUPPORT@; my $NICKNAME = "@PROTOGENI_NICKNAME@"; my $LOGFILE = "$TB/log/expire_daemon.log"; my $PORTSTATS = "$TB/bin/portstats"; my $INTERVAL = 120; # un-taint path $ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin:/usr/site/bin'; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; # Protos sub fatal($); # # Turn off line buffering on output # $| = 1; if ($UID != 0) { fatal("Must be root to run this script\n"); } # # Exit if not a protogeni site. # if (! $PGENISUPPORT) { exit(0); } my %options = (); if (! getopts($optlist, \%options)) { usage(); } if (defined($options{"d"})) { $debug++; } # Load the Testbed support stuff. use lib "@prefix@/lib"; use libaudit; use libdb; use libtestbed; use emdbi; use emutil; use Experiment; use Interface; use Node; use OSinfo; # Connect to the monitoring DB. my $db = emdbi::NewTBDBHandle( "monitoring" ); my $tbdb = emdbi::NewTBDBHandle( $DBNAME ); if (CheckDaemonRunning("mondbd")) { fatal("Not starting another monitoring daemon!"); } if( !$debug) { # Go to background. if (TBBackGround($LOGFILE)) { exit(0); } } if (MarkDaemonRunning("mondbd")) { fatal("Could not mark daemon as running!"); } # # Setup a signal handler for newsyslog. # sub handler() { my $SAVEEUID = $EUID; $EUID = 0; ReOpenLog($LOGFILE); $EUID = $SAVEEUID; } $SIG{HUP} = \&handler; sub AddInterfaces($$$) { my ($n, $node, $ts) = @_; my @interfaces; Interface->LookupAll( $node, \@interfaces ); foreach my $interface( @interfaces ) { my $i = $interface->iface(); my $addr = $interface->IP(); my $role = $interface->role(); my $speed; my $packets; if( $interface->TypeCapability( "ethernet_defspeed", \$speed ) < 0 ) { $speed = 1000000; } $speed *= 100; $packets = $speed / 80; emdbi::DBQueryWarnN( $db, "INSERT INTO ops_interface SET " . "\$schema='http://www.gpolab.bbn.com/monitoring/schema/20140501/port#', " . "id='${DOMAIN}_interface_$n:$i'," . "selfRef='$URL/info/interface/${DOMAIN}_interface_$n:$i'," . "urn='urn:publicid:IDN+$DOMAIN+interface+$n:$i'," . "ts='$ts'," . "address_type='ipv4'," . "address_address='$addr'," . "properties\$role='$role'," . "properties\$max_bps='$speed'," . "properties\$max_pps='$packets'" ); emdbi::DBQueryWarnN( $db, "INSERT INTO ops_node_interface SET " . "id='${DOMAIN}_interface_$n:$i'," . "node_id='${DOMAIN}_node_$n'," . "urn='urn:publicid:IDN+$DOMAIN+interface+$n:$i'," . "selfRef='$URL/info/interface/${DOMAIN}_interface_$n:$i'" ); } } my %portcounters; while (1) { my $ts = time() . "000000"; my $expire = ( time() - 24 * 60 * 60 ) . "000000"; # Add the local CM. emdbi::DBQueryWarnN( $db, "DELETE FROM ops_aggregate WHERE ts < $expire" ); emdbi::DBQueryWarnN( $db, "INSERT INTO ops_aggregate SET " . "\$schema='http://www.gpolab.bbn.com/monitoring/schema/20140501/aggregate#', " . "id='$NICKNAME'," . "selfRef='$URL/info/aggregate/$NICKNAME'," . "urn='urn:publicid:IDN+$DOMAIN+authority+cm'," . "ts='$ts'," . "measRef='$URL/data/'" ); # Add local XEN nodes and interfaces from the shared pool. emdbi::DBQueryWarnN( $db, "DELETE FROM ops_node WHERE ts < $expire" ); emdbi::DBQueryWarnN( $db, "DELETE FROM ops_interface WHERE ts < $expire" ); emdbi::DBQueryWarnN( $db, "LOCK TABLES ops_node WRITE, " . "ops_interface WRITE, " . "ops_aggregate_resource WRITE, " . "ops_node_interface WRITE" ); emdbi::DBQueryWarnN( $db, "DELETE FROM ops_aggregate_resource" ); emdbi::DBQueryWarnN( $db, "DELETE FROM ops_node_interface" ); # The shared pool experiment isn't named consistently across sites. # Rather than fixing it, let's just grab everything we can find and # hope for the best. my @nodes = ExpNodes( "emulab-ops", "shared-node", 1, 1 ); my @morenodes = ExpNodes( "emulab-ops", "shared-nodes", 1, 1 ); push( @nodes, @morenodes ); foreach my $n ( @nodes ) { my $node = Node->Lookup( $n ); my $os = OSinfo->Lookup( $node->def_boot_osid(), $node->def_boot_osid_vers() ); next unless $os->osname =~ /XEN/; my $mem = $node->memory() * 1024; emdbi::DBQueryWarnN( $db, "INSERT INTO ops_node SET " . "\$schema='http://www.gpolab.bbn.com/monitoring/schema/20140501/node#', " . "id='${DOMAIN}_node_$n'," . "selfRef='$URL/info/node/${DOMAIN}_node_$n'," . "urn='urn:publicid:IDN+$DOMAIN+node+$n'," . "ts='$ts'," . "properties\$mem_total_kb='$mem'," . "properties\$vm_server_type='xen'" ); emdbi::DBQueryWarnN( $db, "INSERT INTO ops_aggregate_resource SET " . "id='${DOMAIN}_node_$n'," . "aggregate_id='$NICKNAME'," . "urn='urn:publicid:IDN+$DOMAIN+node+$n'," . "selfRef='$URL/info/node/${DOMAIN}_node_$n'" ); AddInterfaces( $n, $node, $ts ); } my @fakenodenames = (); my $query_result = emdbi::DBQueryWarnN( $tbdb, "SELECT node_id FROM nodes " . "WHERE type='interconnect'" ); if( $query_result && $query_result->numrows) { my @fakenodes; while( @fakenodes = $query_result->fetchrow_array() ) { my ($n) = @fakenodes; push( @fakenodenames, @fakenodes ); my $node = Node->Lookup( $n ); emdbi::DBQueryWarnN( $db, "INSERT INTO ops_node SET " . "\$schema='http://www.gpolab.bbn.com/monitoring/schema/20140501/node#', " . "id='${DOMAIN}_node_$n'," . "selfRef='$URL/info/node/${DOMAIN}_node_$n'," . "urn='urn:publicid:IDN+$DOMAIN+node+$n'," . "ts='$ts'" ); emdbi::DBQueryWarnN( $db, "INSERT INTO ops_aggregate_resource SET " . "id='${DOMAIN}_node_$n'," . "aggregate_id='$NICKNAME'," . "urn='urn:publicid:IDN+$DOMAIN+node+$n'," . "selfRef='$URL/info/node/${DOMAIN}_node_$n'" ); AddInterfaces( $n, $node, $ts ); } } emdbi::DBQueryWarnN( $db, "UNLOCK TABLES" ); # Add traffic counters for the (fake) switch nodes. Do this after # unlocking the database, because we invoke portstats, which is slow. # We lose atomicity, but since we're only doing appends, it doesn't # really matter. foreach my $n ( @fakenodenames ) { open( P, "$PORTSTATS -s $n:eth0|" ); # ignore 3 lines of headers

;

;

; # Do any fake nodes we care about have more than one interface? Let's # assume not. if(

=~ /(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/ ) { my @counts = ( $2, $3 + $4, $5, $6 + $7 ); if( exists( $portcounters{$n} ) ) { my $oldcounts = $portcounters{$n}; my $rx_b = ( $counts[ 0 ] - $$oldcounts[ 0 ] ) / $INTERVAL; my $rx_p = ( $counts[ 1 ] - $$oldcounts[ 1 ] ) / $INTERVAL; my $tx_b = ( $counts[ 2 ] - $$oldcounts[ 2 ] ) / $INTERVAL; my $tx_p = ( $counts[ 3 ] - $$oldcounts[ 3 ] ) / $INTERVAL; if( ( $rx_b >= 0 ) && ( $rx_p >= 0 ) && ( $rx_b >= 0 ) && ( $rx_p >= 0 ) ) { emdbi::DBQueryWarnN( $db, "INSERT INTO " . "ops_interface_rx_bps SET " . "id='${DOMAIN}_interface_$n:eth0'," . "ts='$ts'," . "v=$rx_b;" ); emdbi::DBQueryWarnN( $db, "INSERT INTO " . "ops_interface_rx_pps SET " . "id='${DOMAIN}_interface_$n:eth0'," . "ts='$ts'," . "v=$rx_p;" ); emdbi::DBQueryWarnN( $db, "INSERT INTO " . "ops_interface_tx_bps SET " . "id='${DOMAIN}_interface_$n:eth0'," . "ts='$ts'," . "v=$tx_b;" ); emdbi::DBQueryWarnN( $db, "INSERT INTO " . "ops_interface_tx_pps SET " . "id='${DOMAIN}_interface_$n:eth0'," . "ts='$ts'," . "v=$tx_p;" ); # We don't count errors. Does anybody care? If they # do, we could insert rows of zeroes. } } $portcounters{$n} = \@counts; } close( P ); } sleep( $INTERVAL ); # FIXME add slice, sliver, user, vlan information } sub fatal($) { my ($msg) = @_; # # Send a message to the testbed list. # SENDMAIL($TBOPS, "ProtoGENI monitoring daemon died", $msg, $TBOPS); MarkDaemonStopped("mondbd"); die("*** $0:\n". " $msg\n"); }