Commit 5c961517 authored by Mac Newbold's avatar Mac Newbold
Browse files

Merge the newstated branch with the main tree.

Changes to watch out for:

- db calls that change boot info in nodes table are now calls to os_select

- whenever you want to change a node's pxe boot info, or def or next boot
osids or paths, use os_select.

- when you need to wait for a node to reach some point in the boot process
(like ISUP), check the state in the database using the lib calls

- Proxydhcp now sends a BOOTING state for each node that it talks to.

- OSs that don't send ISUP will have one generated for them by stated
either when they ping (if they support ping) or immediately after they get
to BOOTING.

- States now have timeouts. Actions aren't currently carried out, but they
will be soon. If you notice problems here, let me know... we're still
tuning it. (Before all timeouts were set to "none" in the db)

One temporary change:

- While I make our new free node manager daemon (freed), all nodes are
forced into reloading when they're nfreed and the calls to reset the os
are disabled (that will move into freed).
parent 3332148a
......@@ -9,8 +9,19 @@ DISTCLEAN_FILES = @DISTCLEAN_FILES@
include Makeconf
SUBDIRS = lib assign discvr tbsetup db os security pxe tmcd utils www \
tip capture ipod vis sensors @optional_subdirs@
#
# Ordering here matters!
# Things with no dependencies go first:
# assign db lib
# Things that may have dependencies go next:
# @optional_subdirs@ (has event)
# discvr ipod os security sensors
# Then things that only depend on stuff we've done:
# pxe tbsetup tmcd utils www tip capture vis
# Then things that depend on stuff we just did:
#
SUBDIRS = lib db assign @optional_subdirs@ discvr ipod os security sensors \
pxe tbsetup tmcd utils www tip capture ipod vis sensors
all: all-subdirs
......
......@@ -1256,7 +1256,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
sensors/slothd/sdisrunning sensors/slothd/sddeploy \
tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \
tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \
tbsetup/os_load tbsetup/os_setup tbsetup/power \
tbsetup/os_load tbsetup/os_setup tbsetup/os_select tbsetup/power \
tbsetup/node_reboot tbsetup/webnscheck tbsetup/nscheck \
tbsetup/resetvlans tbsetup/rmacct-ctrl tbsetup/rmproj \
tbsetup/sched_reload tbsetup/sched_reserve tbsetup/reload_daemon \
......
......@@ -313,7 +313,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
sensors/slothd/sdisrunning sensors/slothd/sddeploy \
tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \
tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \
tbsetup/os_load tbsetup/os_setup tbsetup/power \
tbsetup/os_load tbsetup/os_setup tbsetup/os_select tbsetup/power \
tbsetup/node_reboot tbsetup/webnscheck tbsetup/nscheck \
tbsetup/resetvlans tbsetup/rmacct-ctrl tbsetup/rmproj \
tbsetup/sched_reload tbsetup/sched_reserve tbsetup/reload_daemon \
......
......@@ -73,18 +73,24 @@ use Exporter;
TB_ASSIGN_TOOFEWNODES TB_OPSPID
TBDB_TBEVENT_NODESTATE TBDB_TBEVENT_NODEOPMODE
TBDB_TBEVENT_ISUP TBDB_TBEVENT_REBOOTING TBDB_TBEVENT_REBOOTED
TBDB_TBEVENT_NORMAL TBDB_TBEVENT_DELAYING TBDB_TBEVENT_UNKNOWNOS
TBDB_TBEVENT_RELOADING
TBDB_TBEVENT_NODESTATE TBDB_TBEVENT_NODEOPMODE TBDB_TBEVENT_CONTROL
TBDB_NODESTATE_ISUP TBDB_NODESTATE_REBOOTING TBDB_NODESTATE_REBOOTED
TBDB_NODESTATE_UNKNOWN
TBDB_NODESTATE_SHUTDOWN TBDB_NODESTATE_BOOTING TBDB_NODESTATE_TBSETUP
TBDB_NODESTATE_RELOADSETUP TBDB_NODESTATE_RELOADING
TBDB_NODESTATE_RELOADDONE TBDB_NODESTATE_UNKNOWN
TBDB_NODEOPMODE_NORMAL TBDB_NODEOPMODE_DELAYING
TBDB_NODEOPMODE_UNKNOWNOS TBDB_NODEOPMODE_RELOADING
TBDB_NODEOPMODE_NORMALv1 TBDB_NODEOPMODE_MINIMAL
TBDB_NODEOPMODE_RELOAD TBDB_NODEOPMODE_DELAY
TBDB_NODEOPMODE_BOOTWHAT
TBDB_NODEOPMODE_UNKNOWN
TBDB_EXPT_WORKDIR
TBSetNodeEventState TBGetNodeEventState
TBSetNodeOpMode TBGetNodeOpMode
TB_OSID_MBKERNEL TB_OSID_PXEBOOT TB_OSID_FRISBEE
TBBootWhat TBNodeStateTimeout
TBDB_TBCONTROL_RESET TBDB_TBCONTROL_RELOADDONE
TBDB_TBCONTROL_TIMEOUT TBDB_NO_STATE_TIMEOUT
TBAdmin TBProjAccessCheck TBNodeAccessCheck TBOSIDAccessCheck
TBImageIDAccessCheck TBExptAccessCheck ExpLeader MarkNodeDown
......@@ -285,6 +291,11 @@ sub TB_OSID_DESTROY() { 3; }
sub TB_OSID_MIN() { TB_OSID_READINFO; }
sub TB_OSID_MAX() { TB_OSID_DESTROY; }
# Magic OSID constants
sub TB_OSID_MBKERNEL() { "_KERNEL_"; } # multiboot kernel OSID
sub TB_OSID_PXEBOOT() { "PXEBOOT"; } # osid for def pxe_boot_path
sub TB_OSID_FRISBEE() { "PXEFRISBEE"; }
# ImageIDs
#
# Clarification:
......@@ -324,36 +335,40 @@ sub TB_OPSPID() { $TBOPSPID; }
#
sub TBDB_TBEVENT_NODESTATE { "TBNODESTATE"; }
sub TBDB_TBEVENT_NODEOPMODE { "TBNODEOPMODE"; }
#
# TBNODESTATE Events
#
sub TBDB_TBEVENT_ISUP() { "ISUP"; }
sub TBDB_TBEVENT_REBOOTED() { "REBOOTED"; }
sub TBDB_TBEVENT_REBOOTING() { "REBOOTING"; }
#
# TBNODEOPMODE Events
#
sub TBDB_TBEVENT_NORMAL() { "NORMAL"; }
sub TBDB_TBEVENT_DELAYING() { "DELAYING"; }
sub TBDB_TBEVENT_UNKNOWNOS() { "UNKNOWNOS"; }
sub TBDB_TBEVENT_RELOADING() { "RELOADING"; }
sub TBDB_TBEVENT_CONTROL { "TBCONTROL"; }
#
# For nodes, we use this set of events.
#
sub TBDB_NODESTATE_ISUP() { TBDB_TBEVENT_ISUP; }
sub TBDB_NODESTATE_REBOOTED() { TBDB_TBEVENT_REBOOTED; }
sub TBDB_NODESTATE_REBOOTING() { TBDB_TBEVENT_REBOOTING; }
sub TBDB_NODESTATE_ISUP() { "ISUP"; }
sub TBDB_NODESTATE_REBOOTED() { "REBOOTED"; }
sub TBDB_NODESTATE_REBOOTING() { "REBOOTING"; }
sub TBDB_NODESTATE_SHUTDOWN() { "SHUTDOWN"; }
sub TBDB_NODESTATE_BOOTING() { "BOOTING"; }
sub TBDB_NODESTATE_TBSETUP() { "TBSETUP"; }
sub TBDB_NODESTATE_RELOADSETUP() { "RELOADSETUP"; }
sub TBDB_NODESTATE_RELOADING() { "RELOADING"; }
sub TBDB_NODESTATE_RELOADDONE() { "RELOADDONE"; }
sub TBDB_NODESTATE_UNKNOWN() { "UNKNOWN"; };
sub TBDB_NODEOPMODE_NORMAL { TBDB_TBEVENT_NORMAL; }
sub TBDB_NODEOPMODE_DELAYING { TBDB_TBEVENT_DELAYING; }
sub TBDB_NODEOPMODE_UNKNOWNOS { TBDB_TBEVENT_UNKNOWNOS; }
sub TBDB_NODEOPMODE_RELOADING { TBDB_TBEVENT_RELOADING; }
sub TBDB_NODEOPMODE_NORMAL { "NORMAL"; }
sub TBDB_NODEOPMODE_DELAYING { "DELAYING"; }
sub TBDB_NODEOPMODE_UNKNOWNOS { "UNKNOWNOS"; }
sub TBDB_NODEOPMODE_RELOADING { "RELOADING"; }
sub TBDB_NODEOPMODE_NORMALv1 { "NORMALv1"; }
sub TBDB_NODEOPMODE_MINIMAL { "MINIMAL"; }
sub TBDB_NODEOPMODE_RELOAD { "RELOAD"; }
sub TBDB_NODEOPMODE_DELAY { "DELAY"; }
sub TBDB_NODEOPMODE_BOOTWHAT { "_BOOTWHAT_"; } # A redirection opmode
sub TBDB_NODEOPMODE_UNKNOWN { "UNKNOWN"; }
sub TBDB_TBCONTROL_RESET { "RESET"; }
sub TBDB_TBCONTROL_RELOADDONE { "RELOADDONE"; }
sub TBDB_TBCONTROL_TIMEOUT { "TIMEOUT"; }
# Constant we use for the timeout field when there is no timeout for a state
sub TBDB_NO_STATE_TIMEOUT { 0; }
#
# Node name we use in the widearea_* tables to represent a generic local node.
# All local nodes are considered to have the same network characteristcs.
......@@ -1162,6 +1177,100 @@ sub OSFeatureSupported($$) {
return 0;
}
#
# Find out what osid a node will boot next time it comes up,
# Usually (but not always) the currently running OS as well.
# May also return TB_OSID_MBKERNEL
#
sub TBBootWhat($;$) {
# WARNING!!!
#
# DO NOT change this function without making corresponding changes to
# the C version of this code in the bootinfo_mysql.c . They MUST
# ALWAYS find exactly the same resulting OSID given the same inputs.
# [The only exception is that here we must take into account
# *pxe_boot_path since that can specify an OS as well.]
#
# Boot priorities go like this:
# NEXT_PXE_BOOT_PATH
# PXE_BOOT_PATH
# NEXT_BOOT_PATH multiboot kernel
# DEF_BOOT_PATH multiboot kernel
# NEXT_BOOT_OSID osid from os_info
# DEF_BOOT_OSID osid from os_info
# if all the above are null, error
my $node = shift || return 0; # error if no node
my $d = shift || 0; # debug flag
my $cmd = "select path from os_info where osid='".TB_OSID_PXEBOOT."'";
my $q = DBQueryFatal($cmd);
my @r = $q->fetchrow_array();
my $defpxepath = $r[0];
$cmd = "select next_pxe_boot_path, pxe_boot_path,
next_boot_path, def_boot_path, next_boot_osid, def_boot_osid
from nodes where node_id = '$node'";
$q = DBQueryFatal($cmd);
if ($q->numrows() < 1) {
warn("TBBootWhat: Ignoring invalid node '$node' (non-existent)\n");
return 0;
}
@r = $q->fetchrow_array();
$nextpxepath=$r[0];
$pxepath=$r[1];
$nextpath=$r[2];
$defpath=$r[3];
$nextosid=$r[4];
$defosid=$r[5];
print "TBBootWhat: $node => (".($nextpxepath?"nextpxe=$nextpxepath, ":"").
( $pxepath ne $defpxepath ? "pxe=$pxepath, ":"") .
"np=$nextpath, dp=$defpath, no=$nextosid, do=$defosid)\n" if $d>1;
# With PXE paths, look up the osid for the path. If none, use
# TB_OSID_MBKERNEL. If it is the default pxe path, just go on like normal.
if (defined($nextpxepath) && $nextpxepath) {
if ($nextpxepath ne $defpxepath) {
print "TBBootWhat: Using next_pxe_boot_path $nextpxepath\n" if $d;
my $cmd = "select osid from os_info where path='$nextpxepath'";
my $q = DBQueryFatal($cmd);
if ($q->numrows() > 0) {
my @r = $q->fetchrow_array();
my $osid = $r[0];
return $osid;
} else {
return TB_OSID_MBKERNEL;
}
} # else go on as normal
} else {
if ($pxepath ne $defpxepath) {
print "TBBootWhat: Using pxe_boot_path $pxepath\n" if $d;
my $cmd = "select osid from os_info where path='$pxepath'";
my $q = DBQueryFatal($cmd);
if ($q->numrows() > 0) {
my @r = $q->fetchrow_array();
my $osid = $r[0];
return $osid;
} else {
return TB_OSID_MBKERNEL;
}
} # else go on as normal
}
if ((defined($nextpath) && $nextpath) ||
(defined($defpath) && $defpath)) {
# No OSID. Return a magic one.
print "TBBootWhat: Using {def,next}_boot_path ${nextpath}$defpath\n"
if $d;
return TB_OSID_MBKERNEL;
}
if (defined($nextosid) && $nextosid) {
print "TBBootWhat: Using next_boot_osid $nextosid\n" if $d;
return $nextosid;
}
if (defined($defosid) && $defosid) {
print "TBBootWhat: Using def_boot_osid $defosid\n" if $d;
return $defosid;
}
warn("***Warning: node '$node': All boot info was null!\n");
return TB_OSID_MBKERNEL;
}
#
# Ah, what a hack! I'm tired of seeing regexs for sharks scattered around
# the code. Anyway, this checks to see if a node is a shelf, and fills
......@@ -1555,6 +1664,50 @@ sub TBGetNodeEventState($$)
return 1;
}
#
# Check if a node has timed out in its current state. If it has, it gets
# stated involved to handle the situation.
#
# usage: TBNodeStateTimeout(char *node)
# Returns 1 if it has timed out and stated was notified
# Returns 0 if it is okay (still within time limits)
#
sub TBNodeStateTimeout($)
{
my ($node) = @_;
my $notimeout = TBDB_NO_STATE_TIMEOUT;
my $query_result =
DBQueryFatal("select now() - state_timestamp > timeout as over, ".
"timeout='$notimeout' as none ".
"from nodes as n left join state_timeouts as st ".
"on n.eventstate=st.state and n.op_mode=st.op_mode ".
"where node_id='$node'");
if ($query_result->numrows == 0) {
warn("*** TBNodeStateTimeout: Couldn't check node '$node'\n");
return 0;
}
my ($over,$none) = $query_result->fetchrow_array();
if ($over && !$none) {
# We're overtime... send an event and return 1
if ($EVENTSYS) {
EventSendFatal(objtype => TBDB_TBEVENT_CONTROL,
objname => $node,
eventtype => TBDB_TBCONTROL_TIMEOUT,
host => $BOSSNODE);
} else {
# Don't know what to do... how are state timeouts handled
# if we don't have stated?
}
return 1;
} else {
# We're good... return 0
return 0;
}
}
#
# Set operational mode for a node.
#
......@@ -1562,6 +1715,8 @@ sub TBGetNodeEventState($$)
# Returns 1 if okay.
# Returns 0 if failed.
#
# DEPRECATED - stated handles these transitions now
#
sub TBSetNodeOpMode($$)
{
my ($node, $mode) = @_;
......@@ -1594,7 +1749,7 @@ sub TBGetNodeOpMode($$)
my ($node, $mode) = @_;
my $query_result =
DBQueryFatal("select eventstate from nodes where node_id='$node'");
DBQueryFatal("select op_mode from nodes where node_id='$node'");
if ($query_result->numrows == 0) {
return 0;
......
......@@ -34,6 +34,7 @@ use libdb;
use libtestbed;
my $consetup = "$TB/libexec/console_setup";
my $osselect = "$TB/bin/os_select";
my $reloadpid = "emulab-ops";
my $pendingeid = "reloadpending";
my $reloadeid = "reloading";
......@@ -211,13 +212,17 @@ foreach my $n (@freed_nodes) {
#
my $result =
DBQueryFatal("select control_net,nt.osid,nt.pxe_boot_path, " .
" n.def_boot_osid,n.def_boot_path, ".
" nt.isvirtnode ".
" from node_types as nt " .
"n.def_boot_osid,n.def_boot_path, ".
"nt.isvirtnode, o.osid ".
"from node_types as nt " .
"left join nodes as n on n.type=nt.type " .
"left join os_info as o on o.path=nt.pxe_boot_path " .
"where node_id='$n'");
my ($control,$osid,$pxe_boot_path,$def_boot_osid,$def_boot_path,$isvirt) =
$result->fetchrow_array();
my ($control, $osid, $pxe_boot_path, $def_boot_osid, $def_boot_path,
$isvirt, $pxe_osid) = $result->fetchrow_array();
if (!defined($pxe_osid) || $pxe_osid eq "") {
$pxe_osid = "-p $pxe_boot_path";
}
#
# See if the OS it was running was marked as mustclean or not. Basically,
......@@ -271,6 +276,13 @@ foreach my $n (@freed_nodes) {
}
}
#
# Reset the OS to the default osid for the node type
#
# XXX disabled while forced reload hack is in place
#system("$osselect -m $pxe_osid $n");
#system("$osselect $osid $n");
#
# Clean up the nodes table so that its in a moderately clean state.
#
......@@ -284,11 +296,9 @@ foreach my $n (@freed_nodes) {
#}
#print "\n";
#}
DBQueryWarn("update nodes set def_boot_osid='$osid',def_boot_cmd_line='',".
"def_boot_path='',startupcmd='',rpms='',deltas='', ".
"tarballs='',pxe_boot_path='$pxe_boot_path', ".
"failureaction='fatal', routertype='none', ".
"next_pxe_boot_path='' where node_id='$n'") || $error++;
DBQueryWarn("update nodes set startupcmd='',rpms='',deltas='', ".
"tarballs='',failureaction='fatal', routertype='none' ".
"where node_id='$n'") || $error++;
#
# Clean out the current_reloads table (a just in case measure).
......@@ -340,7 +350,8 @@ foreach my $n (@freed_nodes) {
DBQueryFatal("select node_id,image_id from scheduled_reloads " .
"where node_id='$n'");
if ($result->numrows()) {
# XXX force reload hack!
if (1 || $result->numrows()) {
print "Moving $n to $reloadpid/$pendingeid.\n";
DBQueryWarn("update reserved set ".
......
......@@ -24,6 +24,7 @@ use lib '@prefix@/lib';
my $BOSSNODE = "@BOSSNODE@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBDBNAME = "@TBDBNAME@";
my $osselect = "@prefix@/bin/os_select";
$| = 1;
......@@ -99,13 +100,6 @@ if (!$handle) { die "Unable to register with event system\n"; }
my $tuple = address_tuple_alloc();
if (!$tuple) { die "Could not allocate an address tuple\n"; }
#
# Unfortunately, we can't subscribe to multiple objtypes - so, we subscribe
# to all events to $BOSSNODE, and throw away notificattions whose objtypes
# do not match the ones we care about.
#
%$tuple = ( host => $BOSSNODE );
if (!event_subscribe($handle,\&handleEvent,$tuple)) {
die "Could not subscribe to events\n";
}
......@@ -114,15 +108,25 @@ if (!event_subscribe($handle,\&handleEvent,$tuple)) {
# Read in the pre-existing node states, and timeout and valid transition
# information from the database
#
my %nodes = readStates();
my %timeouts = getTimeouts();
my %valid = getValid();
my %nodes = readStates();
my %timeouts = getTimeouts();
my %valid = getValid();
my %modeTrans = getModeTrans();
my %triggers = getTriggers();
#
# Gets set if a reload of state from the database should happen.
#
my $do_reload = 0;
#
# Grab some constants into variables
#
my $TBRESET = TBDB_TBCONTROL_RESET;
my $TBRELOADDONE = TBDB_TBCONTROL_RELOADDONE;
my $TBTIMEOUT = TBDB_TBCONTROL_TIMEOUT;
my $TBNOTIMEOUT = TBDB_NO_STATE_TIMEOUT;
#
# Make the daemon reload database state on a sighup - but I'm worried
# about what would happen if we tried to do this mid-loop. So, we'll
......@@ -143,24 +147,21 @@ while (1) {
# Look for nodes that have passed their timeout
#
while (my ($node, $value) = each %nodes) {
my $state = $value->{state};
my $mode = $value->{mode};
my $time = $value->{timestamp};
my $notified = $value->{notified};
my ($timeout,$action);
if ($mode && $state && $timeouts{$mode} &&
$timeouts{$mode}{$state}) {
($timeout, $action) = @{$timeouts{$mode}{$state}};
}
if ((!$notified) && $time && $timeout &&
(($time + $timeout) < $now)) {
#
# TODO: Need to actually do something!
#
notify("Node $node has timed out in state $state" .
", mode $mode\n");
$value->{notified} = 1;
}
my $state = $value->{state};
my $mode = $value->{mode};
my $time = $value->{timestamp};
my $notified = $value->{notified};
my ($timeout,$action);
if ($mode && $state && $timeouts{$mode} &&
$timeouts{$mode}{$state}) {
($timeout, $action) = @{$timeouts{$mode}{$state}};
}
if ((!$notified) && $time && $timeout &&
$timeout!= $TBNOTIMEOUT && (($time + $timeout) < $now)) {
handleCtrlEvent($node,$TBTIMEOUT);
$value->{notified} = 1;
}
}
if ($do_reload || ($iterations > $reload_time)) {
......@@ -186,7 +187,7 @@ sub readStates(;@) {
%oldnodes = ();
}
debug("readStates called\n");
#debug("readStates called\n");
my $result = DBQueryFatal("SELECT node_id, eventstate, " .
"state_timestamp, op_mode, " .
"op_mode_timestamp FROM nodes");
......@@ -202,6 +203,7 @@ sub readStates(;@) {
#
if ($oldnodes{$node_id} && $state && $timestamp &&
($oldnodes{$node_id}{state} eq $state) &&
($oldnodes{$node_id}{mode} eq $mode) &&
($oldnodes{$node_id}{timestamp} == $timestamp)) {
$nodes{$node_id} = $oldnodes{$node_id};
} else {
......@@ -218,7 +220,7 @@ sub readStates(;@) {
# Read timeouts for various states from the database
#
sub getTimeouts() {
debug("getTimeouts called\n");
#debug("getTimeouts called\n");
my $result = DBQueryFatal("SELECT op_mode, state, timeout, action " .
"FROM state_timeouts");
......@@ -233,7 +235,7 @@ sub getTimeouts() {
# Read the list of valid state transitions from the database
#
sub getValid() {
debug("getValid called\n");
#debug("getValid called\n");
my $result = DBQueryFatal("SELECT op_mode, state1, state2 " .
"FROM state_transitions");
......@@ -244,6 +246,43 @@ sub getValid() {
return %valid;
}
#
# Read the list of valid mode transitions from the database
#
sub getModeTrans() {
#debug("getModeTrans called\n");
my $result =
DBQueryFatal("SELECT op_mode1, state1, op_mode2, state2 " .
"FROM mode_transitions order by op_mode1,state1");
my %modeTrans;
while (my ($mode1,$state1, $mode2, $state2) = $result->fetchrow()) {
if (!defined($modeTrans{"$mode1:$state1"})) {
$modeTrans{"$mode1:$state1"}= ["$mode2:$state2"];
} else {
my @l = @{$modeTrans{"$mode1:$state1"}};
push(@l, "$mode2:$state2");
$modeTrans{"$mode1:$state1"}= \@l;
}
}
return %modeTrans;
}
#
# Read the list of states which trigger an action
#
sub getTriggers() {
#debug("getTriggers called\n");
my $result =
DBQueryFatal("SELECT op_mode, state, trigger " .
"FROM state_triggers order by op_mode,state");
my %t;
while (my ($mode,$state, $trig) = $result->fetchrow()) {
$t{"$mode:$state"} = $trig;
}
return %t;
}
#
# Gets called for every event that we recieve
#
......@@ -265,16 +304,18 @@ sub handleEvent($$$) {
SWITCH: for ($objtype) {
#
# Handle both the new TBNODEOPMODE and the old TBCONTROL events,
# for backward compatibility.
#
(/TBNODESTATE/ || /TBCONTROL/) && do {
(/TBNODESTATE/) && do {
stateTransition($objname,$eventtype);
last;
};
(/TBNODEOPMODE/) && do {
opModeTransition($objname,$eventtype);
notify("Use of deprecated event TBNODEOPMODE:\n".
"$objname->$eventtype\n");
last;
};
(/TBCONTROL/) && do {
handleCtrlEvent($objname,$eventtype);
last;
};
......@@ -284,56 +325,268 @@ sub handleEvent($$$) {
sub stateTransition($$) {
my ($node,$newstate) = @_;
my ($node,$newstate) = @_;
# Check for invalid transitions
my ($oldstate, $mode);
# Check for invalid transitions
my ($oldstate, $mode);
if ($nodes{$node}) {
$oldstate = $nodes{$node}{state};
$mode = $nodes{$node}{mode};
} else {
# Try reloading the cache once before we give up on this node
reload();
if ($nodes{$node}) {
$oldstate = $nodes{$node}{state};
$mode = $nodes{$node}{mode};
$oldstate = $nodes{$node}{state};
$mode = $nodes{$node}{mode};
} else {
#
# Try reloading the cache once before we give up on this node
#
reload();
if ($nodes{$node}) {
$oldstate = $nodes{$node}{state};
$mode = $nodes{$node}{mode};
} else {
notify("Got an event for a node ($node) I don't know ".
"about\n");
}
}
if ($oldstate && $mode && $valid{$mode} && $valid{$mode}{$oldstate} &&
!$valid{$mode}{$oldstate}{$newstate}) {
notify("Invalid transition for node $node from $oldstate " .
" to $newstate\n");
notify("Got an event for a node ($node) I don't know about\n");
}
}
if ($oldstate && $mode && $valid{$mode} && $valid{$mode}{$oldstate} &&
!$valid{$mode}{$oldstate}{$newstate}) {
notify("Invalid transition for node $node from $mode/$oldstate " .
"to $newstate\n");
}
my $now = time();
$nodes{$node}{state} = $newstate;
$nodes{$node}{timestamp} = $now;
$nodes{$node}{notified} = 0;
my $now = time();
$nodes{$node}{state} = $newstate;
$nodes{$node}{timestamp} = $now;
$nodes{$node}{notified} = 0;
DBQueryFatal("UPDATE nodes SET eventstate='$newstate', " .
"state_timestamp='$now' WHERE node_id='$node'");
info("$node: $mode/$oldstate => $mode/$newstate\n");
DBQueryFatal("UPDATE nodes SET eventstate='$newstate', " .