Commit 9c5d3308 authored by Kevin Atkinson's avatar Kevin Atkinson

- Added tbreport database schema (added three tables), storage for

  tbreport errors & context.

- Modified fatal() in swapexp, batchexp, and tbprerun, and die_noretry()
  in os_setup to pass hash parameter to tblog functions.

- Added tbreport errror & context information for select errors in
  swapexp, tbswap, assign_wrapper2, snmpit_lib, snmpit, batchexp,
  assign_wrapper, os_setup, parse-ns, & tbprerun.

- Added assign error parser in assign_wrapper2.

- Added parse.tcl error parser in parse-ns.

- Added severity constants for tbreport in libtblog_simple.

- Added tbreport() function & context table mappging for reporting
  discrete error types to libtblog.
parent 5f290b2c
......@@ -825,7 +825,8 @@ while (1) {
$precheck = 1;
my $retval = RunAssign();
if ($retval != 0) {
fatal({type=>'extra', cause=>'user'},
fatal({type=>'extra', cause=>'user', severity=>SEV_ERROR,
error=>['infeasible_resource_assignment']},
"This experiment cannot be instantiated on this ".
"testbed. You have most likely asked for hardware ".
"this testbed does not have, such as nodes of a type ".
......@@ -843,14 +844,18 @@ while (1) {
if ($currentrun >= $maxrun && $retval != 2) {
fatal("Reached run limit. Giving up.");
fatal({type => 'primary', severity => SEV_ERROR,
error => ['reached_assign_run_limit']},
"Reached run limit. Giving up.");
}
if ($retval < 0) {
#
# Failure in assign.
#
fatal("Unretriable error. Giving up.");
fatal({type => 'primary', severity => SEV_ERROR,
error => ['unretriable_assign_error']},
"Unretriable error. Giving up.");
}
print "Waiting 5 seconds and trying again...\n";
......@@ -956,7 +961,9 @@ sub RunAssign ()
kill('TERM', -$pgrp);
waitpid($childpid, 0);
fatal("Cancel flag set; aborting assign run!");
fatal({cause => 'canceled', severity => SEV_IMMEDIATE,
error => ['cancel_flag']},
"Cancel flag set; aborting assign run!");
return -1;
}
# Loop again to reap child above before exit.
......@@ -976,7 +983,9 @@ sub RunAssign ()
# Check cancel flag before continuing.
TBGetCancelFlag($pid, $eid, \$canceled);
if ($canceled) {
fatal("Cancel flag set; aborting assign run!");
fatal({cause => 'canceled', severity => SEV_IMMEDIATE,
error => ['cancel_flag']},
"Cancel flag set; aborting assign run!");
return -1;
}
......@@ -1458,7 +1467,9 @@ foreach my $pnode (keys(%virtnodes)) {
# Check cancel flag before continuing.
TBGetCancelFlag($pid, $eid, \$canceled);
fatal("Cancel flag set; aborting assign run!")
fatal({cause => 'canceled', severity => SEV_IMMEDIATE,
error => ['cancel_flag']},
"Cancel flag set; aborting assign run!")
if ($canceled);
#
......@@ -1644,7 +1655,9 @@ foreach my $pnode (keys(%virtnodes)) {
# Check cancel flag before continuing.
TBGetCancelFlag($pid, $eid, \$canceled);
fatal("Cancel flag set; aborting assign run!")
fatal({cause => 'canceled', severity => SEV_IMMEDIATE,
error => ['cancel_flag']},
"Cancel flag set; aborting assign run!")
if ($canceled);
# Set port range (see below for how we deal with update).
......@@ -3870,7 +3883,8 @@ sub LoadVirtNodes()
if (! ($osid = TBOSID($pid, $osname)) &&
! ($osid = TBOSID(TB_OPSPID, $osname))) {
fatal({cause => 'user'},
fatal({cause => 'user', type => 'primary', severity => SEV_ERROR,
error => ['invalid_os', undef, $osname, $pid]},
"Invalid OS $osname in project $pid!");
}
# Stash this in the virt_nodes data structure.
......@@ -4239,13 +4253,17 @@ sub LoadExperiment()
if (!defined($jail_osid) && defined($jail_osname)) {
if (! ($jail_osid = TBOSID($pid, $jail_osname)) &&
! ($jail_osid = TBOSID(TB_OPSPID, $jail_osname))) {
fatal({cause=>'user'}, "Invalid OS $jail_osname in project $pid!");
fatal({cause => 'user', type => 'primary', severity => SEV_ERROR,
error => ['invalid_os', 'jail', $jail_osname, $pid]},
"Invalid OS $jail_osname in project $pid!");
}
}
if (!defined($delay_osid) && defined($delay_osname)) {
if (! ($delay_osid = TBOSID($pid, $delay_osname)) &&
! ($delay_osid = TBOSID(TB_OPSPID, $delay_osname))) {
fatal({cause=>'user'}, "Invalid OS $delay_osname in project $pid!");
fatal({cause => 'user', type => 'primary', severity => SEV_ERROR,
error => ['invalid_os', 'delay', $delay_osname, $pid]},
"Invalid OS $delay_osname in project $pid!");
}
}
# Keep a desire string we can use to make sure that the node type picked
......@@ -4750,19 +4768,21 @@ sub CreateTopFile()
"(endnode traffic shaping)\n");
$toperrors++;
}
else {
#
# All the OS's have to support linkdelays.
#
foreach my $node ($node0, $node1) {
my $osid = virtnodeosid($node);
if (! $osdoeslinkdelays{$osid}) {
tberror("$node in link $lan is running an OSID ".
"($osid) that does not support linkdelays ".
"(endnode traffic shaping)\n");
$toperrors++;
}
#
# All the OS's have to support linkdelays.
#
foreach my $node ($node0, $node1) {
my $osid = virtnodeosid($node);
if (!defined($osid)) {
tbreport(SEV_ERROR, 'node_lacks_linkdelay_support', $node, $lan, $osid);
} elsif (! $osdoeslinkdelays{$osid}) {
tberror({type => 'primary', severity => SEV_ERROR,
error => ['node_lacks_linkdelay_support', $node, $lan, $osid]},
"$node in link $lan is running an OSID ".
"($osid) that does not support linkdelays ".
"(endnode traffic shaping)\n");
$toperrors++;
}
}
}
......
......@@ -15,6 +15,11 @@ BEGIN {$FAKE_SCRIPTNAME = $ARGV[0];}
use lib "@prefix@/lib";
use libtblog qw(:DEFAULT dblog *SOUT *SERR);
use constant false => 0;
use constant true => 1;
sub parse_error($);
tblog_set_default_cause('temp');
use strict;
......@@ -82,6 +87,7 @@ if ($exitcode) {
if ($mesg =~ s/^warning:\s+//i) {
dblog(TBLOG_WARNING, {sublevel=>$sublevel}, $mesg);
} else {
parse_error($mesg);
dblog(TBLOG_ERR, {sublevel=>$sublevel}, $mesg);
}
}
......@@ -106,10 +112,134 @@ if ($exitcode) {
exit $exitcode;
sub parse_error($) {
my ($mesg) = @_;
return if parse_type_precheck_error($mesg);
return if parse_mapping_precheck_error($mesg);
return if parse_violation_error($mesg);
return if parse_fixed_node_error($mesg);
return;
}
sub parse_type_precheck_error($) {
my ($mesg) = @_;
my ($vtype, $requested, $slots, $max, $round);
if ($mesg =~ /^No (\w+) physical nodes of type (\S+) found \((\d+) requested\)$/) {
($round, $vtype, $requested) = ($1, $2, $3);
$slots = 0;
} elsif ($mesg =~ /^(\d+) nodes of type (\S+) requested, but only (\d+) (\w+) nodes of type \w+ found$/) {
($requested, $vtype, $slots, $round) = ($1, $2, $3, $4);
} elsif ($mesg =~ /^(\d+) nodes of type (\S+) requested, but you are only allowed to use (\d+)$/) {
($requested, $vtype, $max) = ($1, $2, $3);
} else {
return false;
}
tbreport(SEV_WARNING, 'assign_type_precheck',
$vtype, $requested, $slots, $max, $round);
return true;
}
sub parse_mapping_precheck_error($) {
my ($mesg) = @_;
if ($mesg =~ /^No possible mapping for (\S+)\n/) {
my $vnode = $1;
my (undef, @lines) = split("\n", $mesg);
foreach my $line (@lines) {
my ($class, $type, $requested, $count);
if ($line =~ /^No links of type (\S+) found! \((\d+) requested\)$/) {
($type, $requested) = ($1, $2);
$class = 'link';
$count = 0;
} elsif ($line =~ /^Too many links of type (\S+)! \((\d+) requested, (\d+) found\)$/) {
($type, $requested, $count) = ($1, $2, $3);
$class = 'link';
} elsif ($line =~ /^Too much bandwidth on emulated links!$/) {
$class = 'bandwidth';
$count = 1; # Necessary?
} elsif ($line =~ /^No physical nodes have feature (\S+)!$/) {
$type = $1;
$class = 'feature';
$count = 0; # Necessary?
} else {
# Unknown?
next;
}
tbreport(SEV_WARNING, 'assign_mapping_precheck',
$vnode, $class, $type, $requested, $count);
}
return true;
}
return false;
}
sub parse_violation_error($) {
my ($mesg) = @_;
if ($mesg =~ /^Type precheck passed\.\n/) {
my ($unassigned, $pnode_load, $no_connect, $link_users, $bandwidth,
$desires, $vclass, $delay, $trivial_mix, $subnodes, $max_types,
$endpoints);
my (undef, @lines) = split("\n", $mesg);
foreach my $line (@lines) {
if ($line =~ /^ unassigned: +(\d+)$/) { $unassigned = $1 }
elsif ($line =~ /^ pnode_load: +(\d+)$/) { $pnode_load = $1 }
elsif ($line =~ /^ no_connect: +(\d+)$/) { $no_connect = $1 }
elsif ($line =~ /^ link_users: +(\d+)$/) { $link_users = $1 }
elsif ($line =~ /^ bandwidth: +(\d+)$/) { $bandwidth = $1 }
elsif ($line =~ /^ desires: +(\d+)$/) { $desires = $1 }
elsif ($line =~ /^ vclass: +(\d+)$/) { $vclass = $1 }
elsif ($line =~ /^ delay: +(\d+)$/) { $delay = $1 }
elsif ($line =~ /^ trivial mix: +(\d+)$/) { $trivial_mix = $1 }
elsif ($line =~ /^ subnodes: +(\d+)$/) { $subnodes = $1 }
elsif ($line =~ /^ max_types: +(\d+)$/) { $max_types = $1 }
elsif ($line =~ /^ endpoints: +(\d+)$/) { $endpoints = $1 }
}
tbreport(SEV_WARNING, 'assign_violation',
$unassigned, $pnode_load, $no_connect, $link_users, $bandwidth,
$desires, $vclass, $delay, $trivial_mix, $subnodes, $max_types,
$endpoints);
return true;
}
return false;
}
sub parse_fixed_node_error($) {
my ($mesg) = @_;
my ($class, $vnode, $pnode);
if ($mesg =~ /^Fixed node: (\S+) does not exist\.$/) {
$vnode = $1;
$class = 'exist';
} elsif ($mesg =~ /^Fixed node: (\S+) not available\.$/) {
$pnode = $1;
$class = 'available';
} elsif ($mesg =~ /^Unable to find a type for fixed, vtyped, node (\S+)$/) {
$vnode = $1;
$class = 'type';
} elsif ($mesg =~ /^Fixed node: Could not map (\S+) to (\S+)$/) {
($vnode, $pnode) = ($1, $2);
$class = 'map';
} else {
return false;
}
tbreport(SEV_WARNING, 'assign_fixed_node', $class, $vnode, $pnode);
return true;
}
......@@ -56,7 +56,7 @@ sub usage()
sub ParseArgs();
sub CheckCopyArgs();
sub CopyInArchive();
sub fatal($);
sub fatal($;$);
my $optlist = "iE:g:e:p:S:L:a:l:sfwqt:nzc:bx:y:"; # Enough options?
my $batchmode= 1;
......@@ -182,7 +182,8 @@ if (! UserDBInfo($dbuid, \$user_name, \$user_email)) {
# our time. Make sure user sees the error by exiting with 1.
#
if (system("$checkquota $dbuid") != 0) {
tberror({cause => 'user'},
tberror({cause => 'user', type => 'primary', severity => SEV_ERROR,
error => ['over_disk_quota', $CONTROL]},
"You are over your disk quota on $CONTROL; please cleanup!");
exit(1);
}
......@@ -243,7 +244,9 @@ elsif (!defined($tempnsfile)) {
}
elsif (! -f $tempnsfile || ! -r $tempnsfile || -z $tempnsfile) {
# Exit so that user sees the error, not us.
tberror("$tempnsfile does not exist or is not a readable file!");
tberror({type => 'primary', severity => SEV_ERROR,
error => ['bogus_ns_file', $tempnsfile]},
"$tempnsfile does not exist or is not a readable file!");
exit(1);
}
......@@ -304,7 +307,9 @@ $args{'noidleswap_reason'} = $noidleswap_reason;
# Now create the experiment; we get back a perl class instance.
if (! ($experiment = Experiment->Create($pid, $eid, \%args))) {
tbdie("Could not create a new experiment record!");
tbdie({type => 'secondary', severity => SEV_SECONDARY,
error => ['create_experiment_record_failed']},
"Could not create a new experiment record!");
}
#
......@@ -329,7 +334,9 @@ if (!$template_mode || $instance_idx) {
# Obey exit status protocol for web page; User should see this.
$errorstat = 1;
}
fatal("Failed to created experiment directory");
fatal({type => 'secondary', severity => SEV_SECONDARY,
error => ['create_experiment_directory_failed']},
"Failed to created experiment directory");
}
}
else {
......@@ -363,20 +370,26 @@ chdir("$workdir") or
# when doing an experiment branch (fork).
#
if ($template_mode && defined($archive_eid)) {
fatal("Could not create experiment archive!")
fatal({type => 'secondary', severity => SEV_SECONDARY,
error => ['archive_op_failed', 'create', undef, undef]},
"Could not create experiment archive!")
if (libArchive::TBForkExperimentArchive($pid, $eid,
$pid, $archive_eid, undef)
< 0);
}
elsif ($copybranch) {
# Currently, support branching from existing experiment only.
fatal("Could not create experiment archive!")
fatal({type => 'secondary', severity => SEV_SECONDARY,
error => ['archive_op_failed', 'create', undef, undef]},
"Could not create experiment archive!")
if (libArchive::TBForkExperimentArchive($pid, $eid,
$copypid,
$copyeid, $copytag) < 0);
}
elsif (libArchive::TBCreateExperimentArchive($pid, $eid) < 0) {
fatal("Could not create experiment archive!");
fatal({type => 'secondary', severity => SEV_SECONDARY,
error => ['archive_op_failed', 'create', undef, undef]},
"Could not create experiment archive!");
}
#
......@@ -429,7 +442,9 @@ if (!defined($tempnsfile)) {
# Now we can get the NS file!
#
if (system("/bin/cp", "$tempnsfile", "$nsfile")) {
fatal("Could not copy $tempnsfile to $workdir/$nsfile");
fatal({type => 'primary', severity => SEV_ERROR,
error => ['copy_ns_file_failed', $tempnsfile, $nsfile]},
"Could not copy $tempnsfile to $workdir/$nsfile");
}
chmod(0664, "$nsfile");
......@@ -440,7 +455,9 @@ chmod(0664, "$nsfile");
if (system("$parser -n $zeeopt $pid $gid $eid $nsfile") != 0) {
# Obey exit status protocol for web page.
$errorstat = 1;
fatal("NS Parse failed!");
fatal({type => 'secondary', severity => SEV_SECONDARY,
error => ['ns_parse_failed']},
"NS Parse failed!");
}
#
......@@ -572,7 +589,9 @@ $experiment->SetState(EXPTSTATE_PRERUN) == 0
if ($experiment->PreRun($nsfile, ($zeeopt ? "-z" : ""))) {
$errorstat = $? >> 8;
fatal("tbprerun failed!");
fatal({type => 'secondary', severity => SEV_SECONDARY,
error => ['tbprerun_failed']},
"tbprerun failed!");
}
$experiment->SetState(EXPTSTATE_SWAPPED) == 0
......@@ -587,7 +606,9 @@ if (! ($frontend || $batchmode)) {
if ($experiment->Swap("in") != 0) {
$errorstat = $? >> 8;
fatal("tbswap in failed!");
fatal({type => 'secondary', severity => SEV_SECONDARY,
error => ['tbswap_in_failed']},
"tbswap in failed!");
}
$experiment->SetState(EXPTSTATE_ACTIVE) == 0
......@@ -620,7 +641,9 @@ if (! ($frontend || $batchmode)) {
# We append this report in the email message below.
if ($experiment->Report($repfile, "-b") != 0) {
fatal("tbreport failed!");
fatal({type => 'secondary', severity => SEV_SECONDARY,
error => ['tbreport_failed']},
"tbreport failed!");
}
# Latest log is always called the same thing.
......@@ -673,7 +696,9 @@ libArchive::TBExperimentArchiveAddUserFiles($pid, $eid) == 0
#
print "Doing a savepoint on the experiment archive ...\n";
if (libArchive::TBExperimentArchiveSavePoint($pid, $eid, "startexp") < 0) {
fatal("Failed to do a savepoint on the experiment archive!");
fatal({type => 'secondary', severity => SEV_SECONDARY,
error => ['archive_op_failed', 'savepoint', undef, undef]},
"Failed to do a savepoint on the experiment archive!");
}
#
......@@ -683,7 +708,9 @@ if (libArchive::TBExperimentArchiveSavePoint($pid, $eid, "startexp") < 0) {
if ($copybranch) {
print "Doing a commit on the experiment archive ...\n";
libArchive::TBCommitExperimentArchive($pid, $eid, "branch_merge") == 0 or
fatal("Failed to commit experiment archive!");
fatal({type => 'secondary', severity => SEV_SECONDARY,
error => ['archive_op_failed', 'commit', undef, undef]},
"Failed to commit experiment archive!");
}
#
......@@ -916,7 +943,9 @@ sub ParseArgs()
$tempnsfile = $1;
}
else {
tbdie("Bad data returned by realpath: $translated");
tbdie({type => 'primary', severity => SEV_ERROR,
error => ['bad_data', 'realpath', $translated]},
"Bad data returned by realpath: $translated");
}
#
......@@ -931,7 +960,9 @@ sub ParseArgs()
! ($tempnsfile =~ /^\/proj/) &&
! ($tempnsfile =~ /^\/groups/) &&
! ($tempnsfile =~ /^\/users/)) {
tberror("$tempnsfile does not resolve to an allowed directory!");
tberror({type => 'primary', severity => SEV_ERROR,
error => ['disallowed_directory', $tempnsfile]},
"$tempnsfile does not resolve to an allowed directory!");
# Note positive status; so error goes to user not tbops.
exit(1);
}
......@@ -968,7 +999,9 @@ sub ParseArgs()
if (! (($copyarg =~ /^([-\w]+),([-\w]+)(?::[-\w]*)?$/) ||
($copyarg =~ /^(\d+)(?::[-\w]*)?$/))) {
tbdie("Bad data in argument: $copyarg");
tbdie({type => 'primary', severity => SEV_ERROR,
error => ['bad_data', 'argument', $copyarg]},
"Bad data in argument: $copyarg");
}
# This option only makes sense with -c option.
if (defined($options{"b"})) {
......@@ -986,7 +1019,9 @@ sub ParseArgs()
$pid = $1;
}
else {
tbdie("Bad data in argument: $pid.");
tbdie({type => 'primary', severity => SEV_ERROR,
error => ['bad_data', 'argument', $pid]},
"Bad data in argument: $pid.");
}
}
if (defined($options{"e"})) {
......@@ -996,11 +1031,15 @@ sub ParseArgs()
$eid = $1;
}
else {
tbdie("Bad data in argument: $eid.");
tbdie({type => 'primary', severity => SEV_ERROR,
error => ['bad_data', 'argument', $eid]},
"Bad data in argument: $eid.");
}
if (! TBcheck_dbslot($eid, "experiments", "eid",
TBDB_CHECKDBSLOT_WARN|TBDB_CHECKDBSLOT_ERROR)) {
tbdie("Improper experiment name (id)!");
tbdie({type => 'primary', severity => SEV_ERROR,
error => ['bad_data', 'eid', $eid]},
"Improper experiment name (id)!");
}
}
if (defined($options{"g"})) {
......@@ -1010,7 +1049,9 @@ sub ParseArgs()
$gid = $1;
}
else {
tbdie("Bad data in argument: $gid.");
tbdie({type => 'primary', severity => SEV_ERROR,
error => ['bad_data', 'argument', $gid]},
"Bad data in argument: $gid.");
}
}
if (defined($options{"E"})) {
......@@ -1072,7 +1113,9 @@ sub ParseArgs()
$archive_eid = $1;
}
else {
tbdie("Bad data in argument: $archive_eid.");
tbdie({type => 'primary', severity => SEV_ERROR,
error => ['bad_data', 'argument', $archive_eid]},
"Bad data in argument: $archive_eid.");
}
}
......@@ -1083,7 +1126,9 @@ sub ParseArgs()
$instance_idx = $1;
}
else {
tbdie("Bad data in argument: $instance_idx.");
tbdie({type => 'primary', severity => SEV_ERROR,
error => ['bad_data', 'argument', $instance_idx]},
"Bad data in argument: $instance_idx.");
}
}
}
......@@ -1233,11 +1278,13 @@ sub CopyInArchive()
# exit in the library. This is problematic, cause we could be exiting
# cause the mysql server has gone whacky again.
#
sub fatal($)
sub fatal($;$)
{
my $parms = {};
$parms = shift if ref $_[0] eq 'HASH';
my($mesg) = $_[0];
tberror $mesg;
tberror($parms, $mesg);
tbinfo "Cleaning up and exiting with status $errorstat ...";
#
......
......@@ -214,7 +214,9 @@ use Exporter;
tblog_set_cleanup tblog_get_cleanup
copy_hash
TBLOG_EMERG TBLOG_ALERT TBLOG_CRIT TBLOG_ERR
TBLOG_WARNING TBLOG_NOTICE TBLOG_INFO TBLOG_DEBUG);
TBLOG_WARNING TBLOG_NOTICE TBLOG_INFO TBLOG_DEBUG
SEV_DEBUG SEV_NOTICE SEV_WARNING SEV_SECONDARY
SEV_ERROR SEV_ADDITIONAL SEV_IMMEDIATE);
@EXPORT_OK = qw (dblog *SOUT *SERR);
# After package decl.
......@@ -766,9 +768,94 @@ sub dblog_real ( $$@ ) {
*dblog = \&dblog_real;
}
#
# FILLMEIN
#
use constant CONTEXT_MAP => {
'assign_type_precheck' =>
['report_context', 'vc0', 'i0', 'i1', 'i2', 'vc1'],
# vtype, requested, slots, max, round
'assign_mapping_precheck' =>
['report_context', 'vc0', 'vc1', 'vc2', 'i0', 'i1'],
# vnode, class, type, requested, count
'assign_fixed_node' =>
['report_context', 'vc0', 'vc1', 'vc2'],
# class, vnode, pnode
'over_disk_quota' =>
['report_context', 'vc0'],
# control
'update_aborted' =>
['report_context', 'vc0'],
# result
'set_experiment_state_failed' =>
['report_context', 'vc0'],
# state
'archive_op_failed' =>
['report_context', 'vc0', 'vc1', 'vc2'],
# operation, type, dir
'modify_firewall_not_allowed' =>
['report_context', 'vc0', 'vc1'],
# operation, state
'os_node_reset_failed' =>
['report_context', 'vc0'],
# type
'assign_wrapper_failed' =>
['report_context', 'i0'],
# status
'invalidate_bootblock_failed' =>
['report_context', 'vc0'],
# node
'run_command_failed' =>
['report_context', 'vc0', 'vc1'],
# command, node
'node_lacks_linkdelay_support' =>
['report_context', 'vc0', 'vc1', 'vc2'],
# node, lan, osname
'invalid_os' =>
['report_context', 'vc0', 'vc1', 'vc2'],
# type, osname, pname
'copy_ns_file_failed' =>
['report_context', 'vc0', 'vc1'],
# src, dest
'bad_data' =>
['report_context', 'vc0', 'vc1'],
# field, data
'bogus_ns_file' =>
['report_context', 'vc0'],
# path
'disallowed_directory' =>
['report_context', 'vc0'],
# path
'node_boot_failed' =>
['report_context', 'vc0', 'vc1', 'vc2'],
# node, type, osid
'node_load_failed' =>
['report_context', 'vc0', 'vc1', 'vc2'],
# node, type, osid
'file_not_found' =>
['report_context', 'vc0', 'vc1', 'vc2'],
# type, path, node
'invalid_variable' =>
['report_context', 'vc0', 'vc1'],
# type, var
'create_vlan_failed' =>
['report_context', 'vc0'],
# vlan
'get_port_status_failed' =>
['report_context', 'vc0'],
# port
'device_not_in_stack' =>
['report_context', 'vc0'],
# device
'invalid_switch_stack' =>