require_once('backgroundprocess.inc.php'); /** * ormStopWatch * encapsulate the behavior of a stop watch that will be stored as an attribute of class AttributeStopWatch * * @copyright Copyright (C) 2010-2012 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ /** * ormStopWatch * encapsulate the behavior of a stop watch that will be stored as an attribute of class AttributeStopWatch * * @package itopORM */ class ormStopWatch { protected $iTimeSpent; // seconds protected $iStarted; // unix time (seconds) protected $iLastStart; // unix time (seconds) protected $iStopped; // unix time (seconds) protected $aThresholds; /** * Constructor */ public function __construct($iTimeSpent = 0, $iStarted = null, $iLastStart = null, $iStopped = null) { $this->iTimeSpent = (int) $iTimeSpent; $this->iStarted = $iStarted; $this->iLastStart = $iLastStart; $this->iStopped = $iStopped; $this->aThresholds = array(); } /** * Necessary for the triggers */ public function __toString() { return (string) $this->iTimeSpent; } public function DefineThreshold($iPercent, $tDeadline = null, $bPassed = false, $bTriggered = false, $iOverrun = null) { $this->aThresholds[$iPercent] = array( 'deadline' => $tDeadline, // unix time (seconds) 'passed' => $bPassed, 'triggered' => $bTriggered, 'overrun' => $iOverrun ); } public function MarkThresholdAsTriggered($iPercent) { $this->aThresholds[$iPercent]['triggered'] = true; } public function GetTimeSpent() { return $this->iTimeSpent; } public function GetStartDate() { return $this->iStarted; } public function GetLastStartDate() { return $this->iLastStart; } public function GetStopDate() { return $this->iStopped; } public function GetThresholdDate($iPercent) { if (array_key_exists($iPercent, $this->aThresholds)) { return $this->aThresholds[$iPercent]['deadline']; } else { return null; } } public function GetOverrun($iPercent) { if (array_key_exists($iPercent, $this->aThresholds)) { return $this->aThresholds[$iPercent]['overrun']; } else { return null; } } public function IsThresholdPassed($iPercent) { if (array_key_exists($iPercent, $this->aThresholds)) { return $this->aThresholds[$iPercent]['passed']; } else { return false; } } public function IsThresholdTriggered($iPercent) { if (array_key_exists($iPercent, $this->aThresholds)) { return $this->aThresholds[$iPercent]['triggered']; } else { return false; } } public function GetAsHTML($oAttDef, $oHostObject = null) { $aProperties = array(); $aProperties['States'] = implode(', ', $oAttDef->GetStates()); if (is_null($this->iLastStart)) { if (is_null($this->iStarted)) { $aProperties['Elapsed'] = 'never started'; } else { $aProperties['Elapsed'] = $this->iTimeSpent.' s'; } } else { $iElapsedTemp = ''; //$this->ComputeDuration($oHostObject, $oAttDef, $this->iLastStart, time()); $aProperties['Elapsed'] = $this->iTimeSpent.' + '.$iElapsedTemp.' s + '; } $aProperties['Started'] = $oAttDef->SecondsToDate($this->iStarted); $aProperties['LastStart'] = $oAttDef->SecondsToDate($this->iLastStart); $aProperties['Stopped'] = $oAttDef->SecondsToDate($this->iStopped); foreach ($this->aThresholds as $iPercent => $aThresholdData) { $sThresholdDesc = $oAttDef->SecondsToDate($aThresholdData['deadline']); if ($aThresholdData['triggered']) { $sThresholdDesc .= " TRIGGERED"; } if ($aThresholdData['overrun']) { $sThresholdDesc .= " Overrun:".(int) $aThresholdData['overrun']." sec."; } $aProperties[$iPercent.'%'] = $sThresholdDesc; } $sRes = ""; $sRes .= ""; foreach ($aProperties as $sProperty => $sValue) { $sRes .= ""; $sCell = str_replace("\n", "
\n", $sValue); $sRes .= ""; $sRes .= ""; } $sRes .= ""; $sRes .= "
$sProperty$sCell
"; return $sRes; } protected function ComputeGoal($oObject, $oAttDef) { $sMetricComputer = $oAttDef->Get('goal_computing'); $oComputer = new $sMetricComputer(); $aCallSpec = array($oComputer, 'ComputeMetric'); if (!is_callable($aCallSpec)) { throw new CoreException("Unknown class/verb '$sMetricComputer/ComputeMetric'"); } $iRet = call_user_func($aCallSpec, $oObject); return $iRet; } protected function ComputeDeadline($oObject, $oAttDef, $iStartTime, $iDurationSec) { $sWorkingTimeComputer = $oAttDef->Get('working_time_computing'); $aCallSpec = array($sWorkingTimeComputer, '__construct'); if (!is_callable($aCallSpec)) { //throw new CoreException("Pas de constructeur pour $sWorkingTimeComputer!"); } $oComputer = new $sWorkingTimeComputer(); $aCallSpec = array($oComputer, 'GetDeadline'); if (!is_callable($aCallSpec)) { throw new CoreException("Unknown class/verb '$sWorkingTimeComputer/GetDeadline'"); } // GetDeadline($oObject, $iDuration, DateTime $oStartDate) $oStartDate = new DateTime('@'.$iStartTime); // setTimestamp not available in PHP 5.2 $oDeadline = call_user_func($aCallSpec, $oObject, $iDurationSec, $oStartDate); $iRet = $oDeadline->format('U'); return $iRet; } protected function ComputeDuration($oObject, $oAttDef, $iStartTime, $iEndTime) { $sWorkingTimeComputer = $oAttDef->Get('working_time_computing'); $oComputer = new $sWorkingTimeComputer(); $aCallSpec = array($oComputer, 'GetOpenDuration'); if (!is_callable($aCallSpec)) { throw new CoreException("Unknown class/verb '$sWorkingTimeComputer/GetOpenDuration'"); } // GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate) $oStartDate = new DateTime('@'.$iStartTime); // setTimestamp not available in PHP 5.2 $oEndDate = new DateTime('@'.$iEndTime); $iRet = call_user_func($aCallSpec, $oObject, $oStartDate, $oEndDate); return $iRet; } public function Reset($oObject, $oAttDef) { $this->iTimeSpent = 0; $this->iStarted = null; $this->iLastStart = null; $this->iStopped = null; foreach ($this->aThresholds as $iPercent => &$aThresholdData) { $aThresholdData['passed'] = false; $aThresholdData['triggered'] = false; $aThresholdData['deadline'] = null; $aThresholdData['overrun'] = null; } } /** * Start or continue * It is the responsibility of the caller to compute the deadlines * (to avoid computing twice for the same result) */ public function Start($oObject, $oAttDef) { if (!is_null($this->iLastStart)) { // Already started return false; } if (is_null($this->iStarted)) { $this->iStarted = time(); } $this->iLastStart = time(); $this->iStopped = null; return true; } /** * Compute or recompute the goal and threshold deadlines */ public function ComputeDeadlines($oObject, $oAttDef) { if (is_null($this->iLastStart)) { // Currently stopped - do nothing return false; } $iDurationGoal = $this->ComputeGoal($oObject, $oAttDef); foreach ($this->aThresholds as $iPercent => &$aThresholdData) { if (is_null($iDurationGoal)) { // No limit: leave null thresholds $aThresholdData['deadline'] = null; } else { $iThresholdDuration = round($iPercent * $iDurationGoal / 100); $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iLastStart, $iThresholdDuration - $this->iTimeSpent); // OR $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iStarted, $iThresholdDuration); } if (is_null($aThresholdData['deadline']) || ($aThresholdData['deadline'] > time())) { // The threshold is in the future, reset $aThresholdData['passed'] = false; $aThresholdData['triggered'] = false; $aThresholdData['overrun'] = null; } else { // The new threshold is in the past $aThresholdData['passed'] = true; // Note: the overrun can be wrong, but the correct algorithm to compute // the overrun of a deadline in the past requires that the ormStopWatch keeps track of all its history!!! } } return true; } /** * Stop counting if not already done */ public function Stop($oObject, $oAttDef) { if (is_null($this->iLastStart)) { // Already stopped return false; } $iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, time()); $this->iTimeSpent = $this->iTimeSpent + $iElapsed; foreach ($this->aThresholds as $iPercent => &$aThresholdData) { if (!is_null($aThresholdData['deadline']) && (time() > $aThresholdData['deadline'])) { if ($aThresholdData['overrun'] > 0) { // Accumulate from last start $aThresholdData['overrun'] += $iElapsed; } else { // First stop after the deadline has been passed $iOverrun = $this->ComputeDuration($oObject, $oAttDef, $aThresholdData['deadline'], time()); $aThresholdData['overrun'] = $iOverrun; } $aThresholdData['passed'] = true; } $aThresholdData['deadline'] = null; } $this->iLastStart = null; $this->iStopped = time(); return true; } } /** * CheckStopWatchThresholds * Implements the automatic actions * * @package itopORM */ class CheckStopWatchThresholds implements iBackgroundProcess { public function GetPeriodicity() { return 10; // seconds } public function Process($iTimeLimit) { foreach (MetaModel::GetClasses() as $sClass) { $aList = array(); foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($oAttDef instanceof AttributeStopWatch) { foreach ($oAttDef->ListThresholds() as $iThreshold => $aThresholdData) { $iPercent = $aThresholdData['percent']; // could be different than the index ! $sExpression = "SELECT $sClass WHERE {$sAttCode}_laststart AND {$sAttCode}_{$iThreshold}_triggered = 0 AND {$sAttCode}_{$iThreshold}_deadline < NOW()"; //echo $sExpression."
\n"; $oFilter = DBObjectSearch::FromOQL($sExpression); $oSet = new DBObjectSet($oFilter); while ((time() < $iTimeLimit) && ($oObj = $oSet->Fetch())) { $sClass = get_class($oObj); $aList[] = $sClass.'::'.$oObj->GetKey().' '.$sAttCode.' '.$iThreshold; //echo $sClass.'::'.$oObj->GetKey().' '.$sAttCode.' '.$iThreshold."\n"; // Execute planned actions // foreach ($aThresholdData['actions'] as $aActionData) { $sVerb = $aActionData['verb']; $aParams = $aActionData['params']; $sParams = implode(', ', $aParams); //echo "Calling: $sVerb($sParams)
\n"; $aCallSpec = array($oObj, $sVerb); call_user_func_array($aCallSpec, $aParams); } // Mark the threshold as "triggered" // $oSW = $oObj->Get($sAttCode); $oSW->MarkThresholdAsTriggered($iThreshold); $oObj->Set($sAttCode, $oSW); if($oObj->IsModified()) { CMDBObject::SetTrackInfo("Automatic - threshold triggered"); $oMyChange = CMDBObject::GetCurrentChange(); $oObj->DBUpdateTracked($oMyChange, true /*skip security*/); } // Activate any existing trigger // $sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL)); $oSet = new DBObjectSet( DBObjectSearch::FromOQL("SELECT TriggerOnThresholdReached AS t WHERE t.target_class IN ('$sClassList') AND stop_watch_code=:stop_watch_code AND threshold_index = :threshold_index"), array(), // order by array('stop_watch_code' => $sAttCode, 'threshold_index' => $iThreshold) ); while ($oTrigger = $oSet->Fetch()) { $oTrigger->DoActivate($oObj->ToArgs('this')); } } } } } } $iProcessed = count($aList); return "Triggered $iProcessed threshold(s)"; } } ?>