File: //var/opt/OV/bin/instrumentation/AT.pm
###############################
#
# Version : 11.11.025
#
###############################
package AT;
use strict;
use OvTrace;
use OvCommon;
require Exporter;
use vars qw($VERSION @ISA @EXPORT);
my $catalog = OvCommon::GetCatalog(qw/SystemPerl/);
# set the version for version checking
$VERSION = 0.01;
@ISA = qw(Exporter);
@EXPORT = qw(&WriteTraceLogTest &DoStats &DoThresholds &DoAT);
sub new
{
my $class = shift;
my ( $policy, $console, $rule, $session ) = @_;
my $self = {};
$self->{OVOPOLICY} = $policy;
$self->{OVOCONSOLEMESSAGE} = $console;
$self->{OVORULE} = $rule;
$self->{OVOSESSION} = $session;
$self->{TRACEOBJ}=OvTrace->new($policy,$console,$rule,$session);
bless($self, $class);
return $self;
}
#`opcmsg a=a o=o msg_t=LoadedModule`;
# This subroutine calculates the std. deviation, average, trust factor and confidence factor for the given sample
sub DoStats {
my $self = shift;
my($objData) = @_;
my $nConfidence;
# On Error Resume Next
my $nCount;
my $nValue;
my $objValue;
my $nSumOfSquares;
my $nAvg;
my $nMin;
my $nMax;
my $nAverageAbsoluteDeviation;
my $nSumOfAbsolutes;
$nCount = 0;
my $nItems;
$nItems = $objData->Count();
$nMin = $objData->Item(0)->Value;
$nMax = $objData->Item(0)->Value;
while ($nCount < $nItems) {
$objValue = $objData->Item($nCount);
$nCount = $nCount + 1;
$nValue = $nValue +
$objValue->Value;
if (_compareValues(
$objValue->Value, $nMin, 1)) {
$nMin = $objValue->Value;
}
if (_compareValues(
$objValue->Value, $nMax, 4)) {
$nMax = $objValue->Value;
}
}
# Calculate the standard deviation. Numbers closer to 0 are better
$nAvg = sprintf("%.2f", $nValue / $nCount);
$nCount = 0;
while ($nCount < $nItems) {
$objValue = $objData->Item($nCount);
$nCount = $nCount + 1;
my $nResult;
$nResult =
$objValue->Value - $nAvg;
$nSumOfAbsolutes = $nSumOfAbsolutes + abs($nResult);
$nSumOfSquares = $nSumOfSquares + ($nResult * $nResult);
}
# get the stdDev
my $nStdDev;
my $nTemp;
if ($nItems > 1)
{
$nTemp = $nSumOfSquares / ($nItems - 1);
# no bias
$nAverageAbsoluteDeviation = $nSumOfAbsolutes / $nItems;
# no bias
$nStdDev = sqrt($nTemp);
}
else
{
$nTemp = 0;
$nAverageAbsoluteDeviation = 0;
}
# calculate the trust factor for this data
my $nTrust = 0;
if ($nMax > 0)
{
$nTrust = (1.0 - ($nStdDev / $nMax)) * 100;
}
if ($nTrust < 0) {
$nConfidence = 0;
}
if ($nTrust > 100) {
$nTrust = 0;
}
if ($nItems < 5) {
$nTrust = 0;
}
my $objReturn;
$objReturn = PerfStat->new();
$objReturn->{nAvg} = sprintf("%.4f", $nAvg);
$objReturn->{nPoints} = $nItems;
$objReturn->{nStdDev} = sprintf("%.4f", $nStdDev);
$objReturn->{nMin} = sprintf("%.4f", $nMin);
$objReturn->{nMax} = sprintf("%.4f", $nMax);
$objReturn->{nRange} = sprintf("%.4f", $nMax - $nMin);
$objReturn->{nTrust} = sprintf("%.4f", $nTrust);
$objReturn->{nConfidence} = sprintf("%.4f", $nConfidence);
$objReturn->{nAvgAbsDev} = sprintf("%.4f", $nAverageAbsoluteDeviation);
return $objReturn;
}
#Evaluates threshold values based on stats of baseline data and current sample
sub DoThresholds {
my $self = shift;
my($objCurrentStats, $objHistoryStats, $MessageApplication, $MajorHighSeverity, $MinorHighSeverity, $WarningHighSeverity, $MajorLowSeverity, $MinorLowSeverity, $WarningLowSeverity, $WarningDeviations, $MinorDeviations, $MajorDeviations, $MinimumValue, $MaximumValue, $DebugLevel) = @_;
my $nConfidence;
my $traceObj = $self->{TRACEOBJ};
# max "normal" value (used with stddev later)
my $WarningMax;
# min "normal" value (used with stddev later)
my $WarningMin;
my $MinorMax;
my $MinorMin;
my $MajorMin;
my $MajorMax;
my $nStdDev;
$nStdDev = $objHistoryStats->{nStdDev};
# set up the min and max values for the "normal" baseline.
# Edits 7-14-06
# Change from one-two deviation to configurable deviations in parameters list
# Calculate the Warning Threshold
$WarningMin = $objHistoryStats->{nAvg} - ($nStdDev * $WarningDeviations);
$WarningMin = sprintf("%.4f", $WarningMin);
$WarningMax = $objHistoryStats->{nAvg} + ($nStdDev * $WarningDeviations);
$WarningMax = sprintf("%.4f", $WarningMax);
# Calculate the Minor Threshold
$MinorMin = $objHistoryStats->{nAvg} - ($nStdDev * $MinorDeviations);
$MinorMin = sprintf("%.4f", $MinorMin);
$MinorMax = $objHistoryStats->{nAvg} + ($nStdDev * $MinorDeviations);
$MinorMax = sprintf("%.4f", $MinorMax);
# calculate the major threshold
$MajorMin = $objHistoryStats->{nAvg} - ($nStdDev * $MajorDeviations);
$MajorMin = sprintf("%.4f", $MajorMin);
$MajorMax = $objHistoryStats->{nAvg} + ($nStdDev * $MajorDeviations);
$MajorMax = sprintf("%.4f", $MajorMax);
# enforce the "bounds" for the data.
if ($WarningMin < $MinimumValue) {
$WarningMin = sprintf("%.4f", $MinimumValue);
}
if ($MinorMin < $MinimumValue) {
$MinorMin = sprintf("%.4f", $MinimumValue);
}
if ($MajorMin < $MinimumValue) {
$MajorMin = sprintf("%.4f", $MinimumValue);
}
# erase results higher than the max sample received (in wild stddev calcs)
if ($MaximumValue >= 0) {
if ($WarningMax > $MaximumValue) {
$WarningMax = sprintf("%.4f", $MaximumValue);
}
if ($MinorMax > $MaximumValue) {
$MinorMax = sprintf("%.4f", $MaximumValue);
}
if ($MajorMax > $MaximumValue) {
$MajorMax = sprintf("%.4f", $MaximumValue);
}
}
$self->{OVOSESSION}->Value('WarningMax',$WarningMax);
$self->{OVOSESSION}->Value('MinorMax',$MinorMax);
$self->{OVOSESSION}->Value('MajorMax',$MajorMax);
$self->{OVOSESSION}->Value('WarningMin',$WarningMin);
$self->{OVOSESSION}->Value('MinorMin',$MinorMin);
$self->{OVOSESSION}->Value('MajorMin',$MajorMin);
$self->{OVOSESSION}->Value('HistoryAvg',$objHistoryStats->{nAvg});
$self->{OVOSESSION}->Value('HistoryStdDev',$objHistoryStats->{nStdDev});
$self->{OVOSESSION}->Value('HistoryAbsDev',$objHistoryStats->{nAvgAbsDev});
$self->{OVOSESSION}->Value('HistoryTrust',$objHistoryStats->{nTrust});
$self->{OVOSESSION}->Value('HistorySampleSize',$objHistoryStats->{nPoints});
$self->{OVOSESSION}->Value('HistoryRange',$objHistoryStats->{nRange});
$self->{OVOSESSION}->Value('HistoryMax',$objHistoryStats->{nMax});
$self->{OVOSESSION}->Value('HistoryMin',$objHistoryStats->{nMin});
$self->{OVOSESSION}->Value('CurrentAvg',$objCurrentStats->{nAvg});
$self->{OVOSESSION}->Value('CurrentStdDev',$objCurrentStats->{nStdDev});
$self->{OVOSESSION}->Value('CurrentAbsDev',$objCurrentStats->{nAvgAbsDev});
$self->{OVOSESSION}->Value('CurrentTrust',$objCurrentStats->{nTrust});
$self->{OVOSESSION}->Value('CurrentSampleSize',$objCurrentStats->{nPoints});
$self->{OVOSESSION}->Value('CurrentRange',$objCurrentStats->{nRange});
$self->{OVOSESSION}->Value('CurrentMax',$objCurrentStats->{nMax});
$self->{OVOSESSION}->Value('CurrentMin',$objCurrentStats->{nMin});
my $strTrust;
my ($nHighThresh, $nMedThresh, $nLowThresh);
$nHighThresh = 60;
$nMedThresh = 50;
$nLowThresh = 40;
if ($objHistoryStats->{nTrust} > $nHighThresh) {
$strTrust = 'High';
}
elsif ($objHistoryStats->{nTrust} > $nMedThresh && $objHistoryStats->{nConfidence} <= $nHighThresh) {
$strTrust = 'Medium';
}
elsif ($objHistoryStats->{nTrust} > $nLowThresh && $objHistoryStats->{nConfidence} <= $nMedThresh) {
$strTrust = 'Low';
# don't do anything
return;
}
else {
$strTrust = 'Unreliable';
# don't do anything
return;
}
$self->{OVOSESSION}->Value('HistoryTrust',$strTrust);
#$self->SendBaselineAlert($MessageApplication, $MajorHighSeverity, ' much higher than normal');
# calculate a maximum violation
if ($objCurrentStats->{nAvg} > $MajorMax)
{
$self->SendBaselineAlert($MessageApplication, $MajorHighSeverity, 'much higher than normal');
}
elsif ($objCurrentStats->{nAvg} > $MinorMax ) {
$self->SendBaselineAlert($MessageApplication, $MinorHighSeverity, 'higher than normal');
}
elsif ($objCurrentStats->{nAvg} > $WarningMax) {
$self->SendBaselineAlert($MessageApplication, $WarningHighSeverity, 'higher than normal');
}
# now look for minimum violation
if ($objCurrentStats->{nAvg} < $MajorMin)
{
$self->SendBaselineAlert($MessageApplication, $MajorLowSeverity, 'much lower than normal');
}
elsif ($objCurrentStats->{nAvg} < $MinorMin)
{
$self->SendBaselineAlert($MessageApplication, $MinorLowSeverity, 'lower than normal');
}
elsif ($objCurrentStats->{nAvg} < $WarningMin)
{
$self->SendBaselineAlert($MessageApplication, $WarningLowSeverity, 'lower than normal');
}
# SendConsoleMessage('Baseline Policy', 'Debug', 'DoStats', '', "WMax:$WarningMax \nWMin:$WarningMin \nMiMax:$MinorMax \nMiMin:$MinorMin \nMaMax:$MajorMax \nMaMin:$MajorMin",'normal');
if ($DebugLevel )
{
$traceObj->WriteTraceLog($DebugLevel, "WarningMax:$WarningMax \nWarningMin:$WarningMin \nMinorMax:$MinorMax \nMinorMin:$MinorMin \nMajorMax:$MajorMax \nMajorMin:$MajorMin");
}
}
#Sends alert with appropriate severity to HP OM's active message browser
sub SendBaselineAlert {
my($self, $strApp, $strSev, $strCondition ) = @_;
my $z;
my $MHTN; my $HTN; my $MLTN; my $LTN;
if ( $catalog =~ /Perlen/ )
{
$catalog = "SystemPerlen.1";
$MHTN=" much higher than normal";
$HTN=" higher than normal";
$MLTN=" much lower than normal";
$LTN=" lower than normal";
}
else
{
if ( $catalog =~ /Perlja/ )
{
$catalog = "SystemPerlja.1";
}
elsif ( $catalog =~ /Perlzh/ )
{
$catalog = "SystemPerlzh.1";
}
open FP, $catalog;
while ( <FP> )
{
if (/^29/)
{
my $num;
($num, $z)=split;
$z =~ s/\\x22/\x22/g;$z =~ s/\\x25/\x25/g;$z =~ s/\\x27/\x27/g;$z =~ s/\\\\/\\/g;$z =~ s/\\\$/@%@%@/g;$z =~ s/(\$[\w_!?]+[\[\{][\w_!?\$]+[\]\}])/$1/eeg;$z =~ s/(\$[\w_!?^]+)/$1/eeg;$z =~ s/@%@%@/\$/g;$z =~ s/\\x24/\x24/g;
chomp $z;
$MHTN=$z;
}
elsif (/^30/)
{
my $num;
($num, $z)=split;
$z =~ s/\\x22/\x22/g;$z =~ s/\\x25/\x25/g;$z =~ s/\\x27/\x27/g;$z =~ s/\\\\/\\/g;$z =~ s/\\\$/@%@%@/g;$z =~ s/(\$[\w_!?]+[\[\{][\w_!?\$]+[\]\}])/$1/eeg;$z =~ s/(\$[\w_!?^]+)/$1/eeg;$z =~ s/@%@%@/\$/g;$z =~ s/\\x24/\x24/g;
chomp $z;
$HTN=$z;
}
elsif (/^31/)
{
my $num;
($num, $z)=split;
$z =~ s/\\x22/\x22/g;$z =~ s/\\x25/\x25/g;$z =~ s/\\x27/\x27/g;$z =~ s/\\\\/\\/g;$z =~ s/\\\$/@%@%@/g;$z =~ s/(\$[\w_!?]+[\[\{][\w_!?\$]+[\]\}])/$1/eeg;$z =~ s/(\$[\w_!?^]+)/$1/eeg;$z =~ s/@%@%@/\$/g;$z =~ s/\\x24/\x24/g;
chomp $z;
$MLTN=$z;
}
elsif (/^32/)
{
my $num;
($num, $z)=split;
$z =~ s/\\x22/\x22/g;$z =~ s/\\x25/\x25/g;$z =~ s/\\x27/\x27/g;$z =~ s/\\\\/\\/g;$z =~ s/\\\$/@%@%@/g;$z =~ s/(\$[\w_!?]+[\[\{][\w_!?\$]+[\]\}])/$1/eeg;$z =~ s/(\$[\w_!?^]+)/$1/eeg;$z =~ s/@%@%@/\$/g;$z =~ s/\\x24/\x24/g;
chomp $z;
$LTN=$z;
}
else
{
next;
}
}
close FP;
}
my %cond_hash = (
'much higher than normal' => "$MHTN",
'higher than normal' => "$HTN",
'much lower than normal' => "$MLTN",
'lower than normal' => "$LTN",
);
my %CMA_hash = (
'much higher than normal' => "MuchHigherThanNormal",
'higher than normal' => "HigherThanNormal",
'much lower than normal' => "MuchLowerThanNormal",
'lower than normal' => "LowerThanNormal",
);
my $strConditionText;
my $strConditionWoS;
$strConditionText = $cond_hash{$strCondition};
$strConditionWoS = $CMA_hash{$strCondition};
#$strConditionWoS =~ s/\s+//g; # removes all spaces
my $tempStr;
#convert to lower case
my $tempStr = lc($strSev);
# check to see if user specified NONE for this severity.
if ($tempStr eq 'none')
{
# when none is specified, that means suppress this alert.
$self->{OVORULE}->Status(0);
}
else {
$self->{OVOPOLICY}->MsgSeverity($strSev);
$self->{OVOSESSION}->Value('MessageApp', $strApp);
$self->{OVOSESSION}->Value('MessageCondition',$strConditionText);
$self->{OVOSESSION}->Value('MessageConditionWoS',$strConditionWoS);
$self->{OVORULE}->Status(1);
}
}
#Validates CutOff margin
# CutOff has value x ==> Continue only if current data sample avg is greater than x
# CutOff has value >x ==> Continue only if current data sample avg is less than x
sub EvalCutOff {
my ($cutoff, $avg) = @_;
my ( $cutoffval, $res );
if ( $cutoff =~ /^>/ )
{
$cutoff = $';
if($cutoff =~ /[^0-9]/)
{
$res = 2;
}
else
{
if ( $avg < $cutoff) {
$res = 0;
}
else {
$res = 1;
}
}
}
else
{
if($cutoff =~ /[^0-9]/)
{
$res = 2;
}
else
{
if ( $avg > $cutoff) {
$res = 0;
}
else {
$res = 1;
}
}
}
#`opcmsg a=a o=o msg_t="cutoffval is $cutoffval"`;
return $res;
}
sub GetTodaysDataSample() {
#my $self = shift;
my ($self, $DataObject, $CodaPath, $BaselinePeriod, $instanceName) = @_;
my $objToday ;
# Get Today's data for the current baseline period
$objToday = $self->{OVOPOLICY}->SourceCollection($CodaPath, $BaselinePeriod);
if ( lc($DataObject) ne "global") {
# Add for multi-instance the Include call to FILTER data.
$objToday = $objToday->Include("^$instanceName", "");
}
return $objToday ;
}
sub BuildBaseline() {
#my $self = shift;
my ($self, $DataObject, $CodaPath, $BaselinePeriod, $instanceName, $DebugLevel) = @_;
my ($dCurrent, $dWeek);
my ($strWeek, $bWeek);
my $objWeek;
my $traceObj = $self->{TRACEOBJ};
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);
$dCurrent = time();
my $objBaselineStore;
for( my $week=1; $week < 5; $week++) {
$dWeek = $dCurrent - 604800 * $week;
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($dWeek);
$strWeek = sprintf("%02d/%02d/%d %02d:%02d:%02d", $mday, $mon+=1,$year+=1900,$hour,$min,$sec);
# Get the data for the week ago periods.. (same time last week, week before, etc)
$objWeek = $self->{OVOPOLICY}->SourceCollection($CodaPath, $BaselinePeriod, $strWeek);
if ( lc($DataObject) ne "global") {
# Add for multi-instance the Include call to FILTER data.
$objWeek = $objWeek->Include("^$instanceName","" );
}
my $nCurrent;
my $objValue;
if (defined($objWeek) == 1) {
$bWeek = $objWeek->DataAvailable;
##if ($DebugLevel == 1)
##{
##$self->WriteTraceLog( "Processing week $week samples " . $objWeek->Count() );
##}
if ( ($bWeek) && ($week == 1) )
{
$objBaselineStore = $objWeek;
}
else {
if (defined($objBaselineStore) && ($bWeek == 1) )
{
$nCurrent = 0;
while ($nCurrent < $objWeek->Count())
{
$objValue = $objWeek->Item($nCurrent);
$objBaselineStore->Add($objValue->Time, $objValue->Value);
$nCurrent = $nCurrent + 1;
}
}
else {
if ($bWeek == 1) #So $baselineStore isn't valid, so initialize it.
{
$objBaselineStore = $objWeek;
}
}
}
}
else
{
$bWeek = 0;
if ($DebugLevel )
{
$traceObj->WriteTraceLog( $DebugLevel, "Week $week Data NOT Available " );
}
}
}
$objWeek = undef;
return $objBaselineStore;
}
sub GenerateBaselineAlert {
my $self = shift;
my $arghashref = shift;
my %arghash = %$arghashref;
my $traceObj = $self->{TRACEOBJ};
my ($InstanceSource, $DataObject, $CodaPath, $BaselinePeriod, $DebugLevel, $MessageApplication, $CutOff);
my ($MajorHighSeverity, $MinorHighSeverity, $WarningHighSeverity, $MajorLowSeverity, $MinorLowSeverity);
my ($WarningLowSeverity, $WarningDeviations, $MinorDeviations, $MajorDeviations, $MinimumValue, $MaximumValue);
#`opcmsg a=a o=o msg_t="CodaPath is $CodaPath and BaselinePeriod is $BaselinePeriod"`;
$InstanceSource = $arghash{"InstanceSource"};
$DataObject = $arghash{"DataObject"};
$CodaPath = $arghash{"CodaPath"};
$BaselinePeriod = $arghash{"BaselinePeriod"};
$DebugLevel = $arghash{"DebugLevel"};
$MessageApplication = $arghash{"MessageApplication"};
$MajorHighSeverity = $arghash{"MajorHighSeverity"};
$MinorHighSeverity = $arghash{"MinorHighSeverity"};
$WarningHighSeverity = $arghash{"WarningHighSeverity"};
$MajorLowSeverity = $arghash{"MajorLowSeverity"};
$MinorLowSeverity = $arghash{"MinorLowSeverity"};
$WarningLowSeverity = $arghash{"WarningLowSeverity"};
$WarningDeviations = $arghash{"WarningDeviations"};
$MinorDeviations = $arghash{"MinorDeviations"};
$MajorDeviations = $arghash{"MajorDeviations"};
$MinimumValue = $arghash{"MinimumValue"};
$MaximumValue = $arghash{"MaximumValue"};
$CutOff = $arghash{"CutOff"};
my $instanceName;
if ( lc($DataObject) ne "global") {
# for multi-instance data, this is where we get the instance we're working on right now.
my $policySource;
$policySource = $self->{OVOPOLICY}->Source($InstanceSource);
if (defined $policySource)
{
$instanceName = $policySource->Name();
$self->{OVOSESSION}->Value('instanceName', $instanceName);
if ($DebugLevel )
{
$traceObj->WriteTraceLog( $DebugLevel, 'Instance ' . $policySource->Name);
}
}
}
#Get current sample and validate against CutOff
my $objToday;
$objToday = $self->GetTodaysDataSample($DataObject, $CodaPath, $BaselinePeriod, $instanceName);
my ($bToday, $objTodayStats);
if ($objToday->DataAvailable == 1) {
$bToday = 1;
$objTodayStats = $self->DoStats($objToday);
if ($DebugLevel ) {
$traceObj->WriteTraceLog($DebugLevel, "Today's Data Available. Number of samples is " . $objToday->Count() . "\nCODA DataPath is " . $CodaPath . "\nToday Data Average is " . $objTodayStats->{nAvg} . "\n StdDev is" . $objTodayStats->{nStdDev} );
}
if ( $CutOff ) {
my $StopAT;
$StopAT = EvalCutOff( $CutOff, $objTodayStats->{nAvg} );
if ( $StopAT == 1 ) {
return;
}
if ( $StopAT == 2 ) {
if ($DebugLevel ) {
$traceObj->WriteTraceLog($DebugLevel, 'Invalid Cutoff parameter' );
}
return;
}
}
}
else {
$bToday = 0;
if ($DebugLevel ) {
$traceObj->WriteTraceLog( $DebugLevel, 'Today Data NOT Available ' );
}
}
my $objBaselineStore = $self->BuildBaseline($DataObject, $CodaPath, $BaselinePeriod, $instanceName, $DebugLevel);
# alrighty, we've gotten the data and are proceeding to calculate the baseline
# first, calculate the "quality" of the baseline
if ($bToday == 1) {
# we have data for today, that's a good start
my $objBaselineStats;
# bugfix. Its possible that $objBaselineStore is undefined because of no history. This will catch that case
if (defined($objBaselineStore) == 1)
{
$objBaselineStats = $self->DoStats($objBaselineStore);
if ($DebugLevel ) {
$traceObj->WriteTraceLog($DebugLevel, "Baseline stats - \nnumber of samples " . $objBaselineStore->Count() . "\nBaselineStore Data Average " . $objBaselineStats->{nAvg} . "\nBaselineStore StdDev " . $objBaselineStats->{nStdDev} );
}
$self->DoThresholds($objTodayStats, $objBaselineStats, $MessageApplication, $MajorHighSeverity, $MinorHighSeverity, $WarningHighSeverity, $MajorLowSeverity, $MinorLowSeverity, $WarningLowSeverity, $WarningDeviations, $MinorDeviations, $MajorDeviations, $MinimumValue, $MaximumValue, $DebugLevel);
}
else
{
if ($DebugLevel )
{
$traceObj->WriteTraceLog($DebugLevel, 'Baseline Store is empty (as no history) not processing thresholds' );
}
}
}
else {
# no data for today, cannot compare baseline data as there's nothing to compare
}
# clean up objects
$objToday = undef;
}
sub _isNumeric {
no warnings qw(numeric);
return ($_[0] + 0) eq $_[0];
}
sub _compareValues {
my($lhs, $rhs, $cval) = @_;
my $cmpVal= (_isNumeric($lhs) || _isNumeric($rhs)) ? ($lhs <=> $rhs) : ($lhs cmp $rhs);
# Map (-1, 0, 1) => (1, 2, 4) and do a bit-and
return $cval & (1, 2, 4)[$cmpVal + 1];
}
package PerfStat;
sub new {
my $invocant = shift;
my $class = ref $invocant || $invocant;
return bless {}, $class;
}
1;