/** * @test */ public function addTimeslot() { $pfTimeslot = new PluginFusioninventoryTimeslot(); $input = array('entities_id' => 0, 'is_recursive' => 0, 'name' => 'unitdefault'); $pfTimeslot->add($input); $cnt = countElementsInTable('glpi_plugin_fusioninventory_timeslots'); $this->assertEquals(1, $cnt, "Timeslot may be added"); $GLPIlog = new GLPIlogs(); $GLPIlog->testSQLlogs(); $GLPIlog->testPHPlogs(); }
/** * Get the list of taskjobstates */ function getTaskjobstatesForAgent($agent_id, $methods = array(), $options = array()) { global $DB; // Check for read only which means we do not change the jobstates state (especially usefull // for the get_agent_jobs.php script). $read_only = false; if (isset($options['read_only'])) { $read_only = $options['read_only']; } $pfTimeslot = new PluginFusioninventoryTimeslot(); $jobstates = array(); //Get the datetime of agent request $now = new Datetime(); // list of jobstates not allowed to run (ie. filtered by schedule and timeslots) $jobstates_to_cancel = array(); $query = implode(" \n", array("SELECT", " task.`id`, task.`name`, task.`is_active`,", " task.`datetime_start`, task.`datetime_end`,", " task.`plugin_fusioninventory_timeslots_id` as timeslot_id,", " job.`id`, job.`name`, job.`method`, job.`actors`,", " run.`itemtype`, run.`items_id`, run.`state`,", " run.`id`, run.`plugin_fusioninventory_agents_id`", "FROM `glpi_plugin_fusioninventory_taskjobstates` run", "LEFT JOIN `glpi_plugin_fusioninventory_taskjobs` job", " ON job.`id` = run.`plugin_fusioninventory_taskjobs_id`", "LEFT JOIN `glpi_plugin_fusioninventory_tasks` task", " ON task.`id` = job.`plugin_fusioninventory_tasks_id`", "WHERE", " job.`method` IN ('" . implode("','", $methods) . "')", " and run.`state` IN ('" . implode("','", array(PluginFusioninventoryTaskjobstate::PREPARED, PluginFusioninventoryTaskjobstate::SERVER_HAS_SENT_DATA, PluginFusioninventoryTaskjobstate::AGENT_HAS_SENT_DATA)) . "')", " AND run.`plugin_fusioninventory_agents_id` = " . $agent_id, "ORDER BY job.`id`")); $query_result = $DB->query($query); $results = array(); if ($query_result) { $results = PluginFusioninventoryToolbox::fetchAssocByTable($query_result); } // Fetch a list of unique actors since the same actor can be assigned to many jobs. $actors = array(); foreach ($results as $result) { $actors_from_job = importArrayFromDB($result['job']['actors']); foreach ($actors_from_job as $actor) { $actor_key = "" . key($actor) . "_" . $actor[key($actor)]; if (!isset($actors[$actor_key])) { $actors[$actor_key] = array(); foreach ($this->getAgentsFromActors(array($actor)) as $agent) { $actors[$actor_key][$agent] = true; } } } } // Merge agents into one list $agents = array(); foreach ($actors as $key => $agents_list) { //Toolbox::logDebug(array("agents_list count" => count($agents_list))); //Toolbox::logDebug(array("agents_list" => $agents_list)); foreach ($agents_list as $id => $val) { if (!isset($agents[$id])) { $agents[$id] = true; } } } $agents = array_keys($agents); //Toolbox::logDebug(array("final agents list count" => count($agents))); //Toolbox::logDebug(array("final agents list" => $agents)); // Get timeslot's entries from this list at the time of the request (ie. get entries according // to the day of the week) $timeslot_entries = array(); $day_of_week = $now->format("N"); $timeslot_ids = array(); foreach ($results as $result) { $timeslot_ids[$result['task']['timeslot_id']] = 1; } $timeslot_entries = $pfTimeslot->getTimeslotEntries(array_keys($timeslot_ids), $day_of_week); $timeslot_cursor = $pfTimeslot->getTimeslotCursor($now); /** * Ensure the agent's jobstates are allowed to run at the time of the agent's request. * The following checks if: * - The tasks associated with those taskjobs are not disabled. * - The task's schedule and timeslots still match the time those jobstates have been * requested. * - The agent is still present in the dynamic actors (eg. Dynamic groups) */ foreach ($results as $result) { $jobstate = new PluginFusioninventoryTaskjobstate(); $jobstate->getFromDB($result['run']['id']); //Cancel the job it has already been sent to the agent but the agent did not replied if ($result['run']['state'] == $jobstate::SERVER_HAS_SENT_DATA or $result['run']['state'] == $jobstate::AGENT_HAS_SENT_DATA) { $jobstates_to_cancel[$jobstate->fields['id']] = array('jobstate' => $jobstate, 'reason' => __("The agent is requesting a configuration that has already been sent to him by the server. It is more likely that the agent is subject to a critical error.", 'fusioninventory'), 'code' => $jobstate::IN_ERROR); continue; } //Cancel the jobstate if the related tasks has been deactivated if ($result['task']['is_active'] == 0) { $jobstates_to_cancel[$jobstate->fields['id']] = array('jobstate' => $jobstate, 'reason' => __('The task has been deactivated after preparation of this job.', 'fusioninventory')); continue; } // Cancel the jobstate if it the schedule doesn't match. if (!is_null($result['task']['datetime_start'])) { $schedule_start = new DateTime($result['task']['datetime_start']); if (!is_null($result['task']['datetime_end'])) { $schedule_end = new DateTime($result['task']['datetime_end']); } else { $schedule_end = $now; } if (!($schedule_start <= $now and $now <= $schedule_end)) { $jobstates_to_cancel[$jobstate->fields['id']] = array('jobstate' => $jobstate, 'reason' => __("This job can not be executed anymore due to the task's schedule.", 'fusioninventory')); continue; } } // Cancel the jobstate if it is requested outside of any timeslot. $timeslot_id = $result['task']['timeslot_id']; // Do nothing if there are no defined timeslots for this jobstate. if ($timeslot_id > 0) { $timeslot_matched = false; // We do nothing if there are no timeslot_entries, meaning this jobstate is not allowed // to be executed at the day of request. if (array_key_exists($timeslot_id, $timeslot_entries)) { foreach ($timeslot_entries[$timeslot_id] as $timeslot_entry) { if ($timeslot_entry['begin'] <= $timeslot_cursor and $timeslot_cursor <= $timeslot_entry['end']) { //The timeslot cursor (ie. time of request) matched a timeslot entry so we can //break the loop here. $timeslot_matched = true; break; } } } // If no timeslot matched, cancel this jobstate. if (!$timeslot_matched) { $jobstates_to_cancel[$jobstate->fields['id']] = array('jobstate' => $jobstate, 'reason' => __("This job can not be executed anymore due to the task's timeslot.", 'fusioninventory')); continue; } } // Make sure the agent is still present in the list of actors that generated // this jobstate. // TODO: If this jobstate needs to be cancelled, it would be worth to point out which actor // is the source of this execution. To do this, we need to track the 'actor_source' in the // jobstate when it's generated by prepareTaskjobs(). //$job_actors = importArrayFromDB($result['job']['actors']); $chrono = microtime(true); if (!in_array($agent_id, $agents)) { $jobstates_to_cancel[$jobstate->fields['id']] = array('jobstate' => $jobstate, 'reason' => __('This agent does not belong anymore in the actors defined in the job.', 'fusioninventory')); continue; } //Toolbox::logDebug( // sprintf( // "Time to check if '%s' is still in actors list: %5.6f", // $_GET['machineid'],(microtime(true) - $chrono) // ) //); //TODO: The following method (actually defined as member of taskjob) needs to be //initialized when getting the jobstate from DB (with a getfromDB hook for example) $jobstate->method = $result['job']['method']; //Add the jobstate to the list since previous checks are good. $jobstates[$jobstate->fields['id']] = $jobstate; } //Remove the list of jobstates previously filtered for removal. foreach ($jobstates_to_cancel as $jobstate) { if (!isset($jobstate['code'])) { $jobstate['code'] = PluginFusioninventoryTaskjobstate::CANCELLED; } switch ($jobstate['code']) { case PluginFusioninventoryTaskjobstate::IN_ERROR: $jobstate['jobstate']->fail($jobstate['reason']); break; default: $jobstate['jobstate']->cancel($jobstate['reason']); break; } } return $jobstates; }
static function cronWakeupAgents($crontask) { global $DB; $wakeupArray = array(); $tasks = array(); //Get the maximum number of agent to wakeup, //as allowed in the general configuration $config = new PluginFusioninventoryConfig(); $maxWakeUp = $config->getValue('wakeup_agent_max'); $counter = 0; $continue = true; //Get all active timeslots $timeslot = new PluginFusioninventoryTimeslot(); $timeslots = $timeslot->getCurrentActiveTimeslots(); if (empty($timeslots)) { $query_timeslot = ''; } else { $query_timeslot = "OR (`plugin_fusioninventory_timeslots_id` IN (" . implode(',', $timeslots) . "))"; } //Get all active task requiring an agent wakeup //Check all tasks without timeslot or task with a current active timeslot $query = "SELECT `id`, `wakeup_agent_counter`, `wakeup_agent_time`, `last_agent_wakeup` \n FROM `glpi_plugin_fusioninventory_tasks` \n WHERE `wakeup_agent_time` > 0 \n AND `wakeup_agent_counter` > 0\n AND `is_active`='1' \n AND ((`plugin_fusioninventory_timeslots_id`='0') \n {$query_timeslot})"; foreach ($DB->request($query) as $task) { if (!is_null($task['wakeup_agent_time'])) { //Do not wake up is last wake up in inferior to the minimum wake up interval $interval = time() - strtotime($task['last_agent_wakeup']); if ($interval < $task['wakeup_agent_time'] * MINUTE_TIMESTAMP) { continue; } } //For each task, get a number of taskjobs at the PREPARED state //(the maximum is defined in wakeup_agent_counter) $query_states = "SELECT `taskjobstates`.`plugin_fusioninventory_agents_id`, \n `tasks`.`id` as `taskID`, \n `tasks`.`wakeup_agent_time`,\n `tasks`.`last_agent_wakeup`\n FROM `glpi_plugin_fusioninventory_taskjobstates` as `taskjobstates`,\n `glpi_plugin_fusioninventory_taskjobs` as `taskjobs`\n LEFT JOIN `glpi_plugin_fusioninventory_tasks` as `tasks` \n ON `tasks`.`id`=`taskjobs`.`plugin_fusioninventory_tasks_id`\n WHERE `tasks`.`id`='" . $task['id'] . "' \n AND `taskjobs`.`id`=`taskjobstates`.`plugin_fusioninventory_taskjobs_id` \n AND `taskjobstates`.`state`='" . PluginFusioninventoryTaskjobstate::PREPARED . "' \n ORDER BY `taskjobstates`.`id` ASC LIMIT " . $task['wakeup_agent_counter']; foreach ($DB->request($query_states) as $state) { $agents_id = $state['plugin_fusioninventory_agents_id']; //Check if agent is already added to the list of agents to wake up if (!isset($wakeupArray[$agents_id])) { //This agent must be woken up $wakeupArray[$agents_id] = $agents_id; $counter++; } //Store task ID if (!in_array($state['taskID'], $tasks)) { $tasks[] = $state['taskID']; } //Do not process more than the maximum number of wakeup allowed in the configuration if ($counter >= $maxWakeUp) { if (PluginFusioninventoryConfig::isExtradebugActive()) { Toolbox::logDebug(__("Maximum number of agent wakeup reached", 'fusioninventory') . ":" . $maxWakeUp); } $continue = false; break; } } //We've reached the maximum number of agents to wake up ! if (!$continue) { break; } } //Number of agents successfully woken up $wokeup = 0; $myTask = new PluginFusioninventoryTask(); if (!empty($tasks)) { //Update last wake up time each task $query_lastrun = "UPDATE `glpi_plugin_fusioninventory_tasks` \n SET `last_agent_wakeup`='" . $_SESSION['glpi_currenttime'] . "' \n WHERE `id` IN (" . implode(",", $tasks) . ")"; $DB->query($query_lastrun); $agent = new PluginFusioninventoryAgent(); //Try to wake up agents one by one foreach ($wakeupArray as $ID => $value) { $agent->getFromDB($ID); if ($agent->wakeUp()) { $wokeup++; } } } $crontask->addVolume($wokeup); return true; }
@co-author @copyright Copyright (c) 2010-2014 FusionInventory team @license AGPL License 3.0 or (at your option) any later version http://www.gnu.org/licenses/agpl-3.0-standalone.html @link http://www.fusioninventory.org/ @link http://forge.fusioninventory.org/projects/fusioninventory-for-glpi/ @since 2014 ------------------------------------------------------------------------ */ include "../../../inc/includes.php"; Session::checkRight('plugin_fusioninventory_task', READ); if (!isset($_GET["id"])) { $_GET["id"] = ""; } $pfTimeslot = new PluginFusioninventoryTimeslot(); //Add a new timeslot if (isset($_POST["add"])) { $pfTimeslot->check(-1, CREATE, $_POST); if ($newID = $pfTimeslot->add($_POST)) { if ($_SESSION['glpibackcreated']) { Html::redirect($pfTimeslot->getFormURL() . "?id=" . $newID); } } Html::back(); // delete a timeslot } else { if (isset($_POST["delete"])) { $pfTimeslot->check($_POST['id'], DELETE); $ok = $pfTimeslot->delete($_POST); $pfTimeslot->redirectToList();