Scheduled Tasks and FOG 0.32
-
Many of us have been experiencing issues with fog 0.32 where Scheduled Tasks do not show up in the web GUI. I’ve seen various discussions on these forums about the issue but no definitive fix. Hopefully this will provide a solution.
Before I go any further into this I work for a large university in the UK who rely on group scheduled tasks. We re-image a large number of machines each week based on group schedules including our IT Suites and Seminar rooms. Until a few weeks ago we had been using FOG 0.29 but decided to upgrade to 0.32 to find this bug.
For reference we are running FOG with CentOS 6.5, PHP 5.3.3, MySQL 5.1.73
Though you can schedule single host tasks and group tasks the webGUI will not display the scheduled tasks to allow you to delete them.
[IMG]http://s9.postimg.org/x780ntp2n/Fog_Scheduled_Tasks_web_GUI.png[/IMG]
/fog/management/index.php?node=tasks&sub=schedAfter some poking and testing I discovered the issue came down to an undefined method / function call in one of the core classes under the function “getGroupById”:
The error in /etc/httpd/logs/error_log showed:
[INDENT=1]PHP Fatal error: Call to undefined method Group::addMember() in /var/www/fog/management/lib/FOGCore.class.php on line 212,[/INDENT]
This led me to the forums where the closest fix came from [url]http://fogproject.org/forum/threads/call-to-undefined-method-group-addmember.1008/[/url]. The instructions given were to comment out some of the code which got the GUI working however it stopped group scheduled tasks from running. Host scheduled tasks continued to work.
The instructions from [URL=‘http://fogproject.org/forum/threads/call-to-undefined-method-group-addmember.1008/#post-23191’]Ritchy_Purple[/URL] were as follows (I’ve put them here for reference - do not follow them!):
Modify FOGCore.class.php
line 137
[php]else if ( $group != null )
{
//add copy from below
$task = new ScheduledTask( $host, $group, $timer, $ar[“stTaskType”], $ar[“stID”] );
$task->setShutdownAfterTask( $ar[“stShutDown”] == 1 );
$task->setOther1( $ar[“stOther1”] );
$task->setOther2( $ar[“stOther2”] );
$task->setOther3( $ar[“stOther3”] );
$task->setOther4( $ar[“stOther4”] );
$task->setOther5( $ar[“stOther5”] );
$arTasks[] = $task;
// start Comment
/* if ( $group->getCount() > 0 )
{
$arRm = array();
$hosts = $group->getMembers();
for( $i = 0; $i < count($hosts); $i++ )
{
if ( $hosts[$i] != null )
{
$h = $hosts[$i];
if ( ! ($h->isReadyToImage() && $h->getImage()->getStorageGroup()->getID() == $groupid ) )
{
$arRm[] = $h;
}
}
}//echo ( "Before: " . $group->getCount() );
for( $i = 0; $i < count($arRm); $i++ )
{
$group->removeMember( $arRm[$i] );
}
//echo ( "After: " . $group->getCount() );$task = new ScheduledTask( $host, $group, $timer, $ar[“stTaskType”], $ar[“stID”] );
$task->setShutdownAfterTask( $ar[“stShutDown”] == 1 );
$task->setOther1( $ar[“stOther1”] );
$task->setOther2( $ar[“stOther2”] );
$task->setOther3( $ar[“stOther3”] );
$task->setOther4( $ar[“stOther4”] );
$task->setOther5( $ar[“stOther5”] );
$arTasks[] = $task;
} */
//end comment
//and comment also from below (Line 212 for me) BOLD
$group = null;
while( $ar = mysql_fetch_array( $res ) )
{
if ( $group == null )
{
$group = new Group($ar[“groupID”], $ar[“groupName”], $ar[“groupDesc”] );
}
/* $hid = $ar[“hostid”];
if ( $hid !== null && is_numeric( $hid ) )
{
$host = $this->getHostById( $hid );
if ( $host != null )
$group->addMember( $host );
} */
}
return $group;[/php]While working through the instructions from that thread I setup a test group and a scheduled task to run every minute. Once the web GUI was showing the scheduled tasks I checked the Fog Scheduler log to find that group scheduled tasks no longer worked as it could not find the list of hosts associated with the group. (I have edited the output of the fog scheduler to give more details)
[INDENT=1]tail -f /opt/fog/log/fogscheduler.log[/INDENT]
[INDENT=1][04-17-14 12:29:45 pm] * Task run time: * * * * *[/INDENT]
[INDENT=1][04-17-14 12:29:45 pm] * Found a task that should run…[/INDENT]
[INDENT=1][04-17-14 12:29:45 pm] - Is a group based task.[/INDENT]
[INDENT=1][04-17-14 12:29:45 pm] - Found 0 hosts.[/INDENT]
[INDENT=1][04-17-14 12:29:45 pm] - No valid hosts.[/INDENT]So dissecting the FOGScheduler service at /opt/fog/service/FOGTaskScheduler/FOGTaskScheduler, I found that the getScheduledTasksByStorageGroupID function I’ve just commented a load of code out from is being re-used.
[INDENT=1]91 $tasks = $core->getScheduledTasksByStorageGroupID( $groupid );[/INDENT]
So looking back over the code that was commented out you can see that once the getScheduledTasksByStorageGroupID function has information about the task it looks to see if the task is a group or a single task.
[INDENT=1]Is task is a single host task?[/INDENT]
[INDENT=1]115 If ( $ar[“stIsGroup”] == 0 ){[/INDENT]
[INDENT=1]Get information about host[/INDENT]
[INDENT=1]116 $host=$this->getHostById($ar[“stGroupHostID”]);[/INDENT]
[INDENT=1]117[/INDENT]
[INDENT=1]Is task a group task?[/INDENT]
[INDENT=1]118 }else if ( $ar[“stIsGroup”] == 1 ){// Task[/INDENT]
[INDENT=1]Get information about the group[/INDENT]
[INDENT=1]119 $group = $this->getGroupById($ar[“stGroupHostID”]);[/INDENT]
[INDENT=1]120[/INDENT]
[INDENT=1]121 }[/INDENT]For group tasks on line 119 the function passes to another function getGroupById to get the members of the group. The getGroupById function looks at the DB and gets a list of hosts that are members of the group and then builds an array / list (calling another function Group::addMember( $host ) which caused the web GUI error) with each host as an entry.
The instructions from ritchy_purple were to comment out the part of the getGroupById function that built the array / list of hosts. So commenting out that would be the reason why the Fog Scheduler stopped working for groups!
-
The question now is how to get both the web GUI and group scheduled tasks working? Assuming you have a fresh install of FOG 0.32 and have not made the changes suggested by above: I’ve introduced a new variable to the getScheduledTasksByStorageGroupID and getGroupById functions and a few if statements that allows the script to bypass parts of the code.
Your code might not match the line numbers in mine but should be pretty close.
Before making any changes make sure you make a backup of the file you edit!
cp /var/www/html/fog/management/lib/FOGCore.class.php /var/www/html/fog/management/lib/FOGCore.class.bak
First off lets edit the /var/www/html/fog/management/lib/FOGCore.class.php file:
Looks for:
[php]function getScheduledTasksByStorageGroupID( $groupid, $blIgnoreNonImageReady=false )
{
$arTasks = array();[/php]Edit to become:
[php]function getScheduledTasksByStorageGroupID( $groupid, $blIgnoreNonImageReady=false, $wgui = false )
{
$arTasks = array();[/php]This introduced the $wgui variable in the function parameters, if it is not supplied as a parameter it will default to the value of false.
Find this on lines 110 to 122
[php]if ( $timer != null )
{
$group=null;
$host=null;If ( $ar[“stIsGroup”] == 0 ){
$host=$this->getHostById($ar[“stGroupHostID”]);}else if ( $ar[“stIsGroup”] == 1 ){
$group = $this->getGroupById($ar[“stGroupHostID”]);}
if ( $group != null || $host != null )[/php]Edit it to become:
[php]if ( $timer != null )
{
$group=null;
$host=null;If ( $ar[“stIsGroup”] == 0 ){
$host=$this->getHostById($ar[“stGroupHostID”]);}else if ( $ar[“stIsGroup”] == 1 ){
($wgui == true ? $group = $this->getGroupById($ar[“stGroupHostID”],true) : $group = $this->getGroupById($ar[“stGroupHostID”]));}
if ( $group != null || $host != null )[/php]This edit adds a shorthand if statement. If $wgui is true it will add a new parameter of “true” to the [B]getGroupById [/B]function call.
— Update —
On reflection I’ve overcomplicated the last change. You can also replace the shorthand if statement simply with[INDENT=1]$group = $this->getGroupById($ar[“stGroupHostID”],$wgui);[/INDENT]
-
Find the following on lines 135 to 172:
[php] $arTasks[] = $task;
}
}
else if ( $group != null )
{
if ( $group->getCount() > 0 )
{
$arRm = array();
$hosts = $group->getMembers();
for( $i = 0; $i < count($hosts); $i++ )
{
if ( $hosts[$i] != null )
{
$h = $hosts[$i];
if ( ! ($h->isReadyToImage() && $h->getImage()->getStorageGroup()->getID() == $groupid ) )
{
$arRm[] = $h;
}
}
}//echo ( "Before: " . $group->getCount() ); for( $i = 0; $i < count($arRm); $i++ ) { $group->removeMember( $arRm[$i] ); } //echo ( "After: " . $group->getCount() ); $task = new ScheduledTask( $host, $group, $timer, $ar["stTaskType"], $ar["stID"] ); $task->setShutdownAfterTask( $ar["stShutDown"] == 1 ); $task->setOther1( $ar["stOther1"] ); $task->setOther2( $ar["stOther2"] ); $task->setOther3( $ar["stOther3"] ); $task->setOther4( $ar["stOther4"] ); $task->setOther5( $ar["stOther5"] ); $arTasks[] = $task; } }[/php]
-
Becomes
From line 135 to 185
[php] $arTasks[] = $task;
}
}
else if ( $group != null )
{
if ($wgui == true){
$task = new ScheduledTask( $host, $group, $timer, $ar[“stTaskType”], $ar[“stID”] );
$task->setShutdownAfterTask( $ar[“stShutDown”] == 1 );
$task->setOther1( $ar[“stOther1”] );
$task->setOther2( $ar[“stOther2”] );
$task->setOther3( $ar[“stOther3”] );
$task->setOther4( $ar[“stOther4”] );
$task->setOther5( $ar[“stOther5”] );
$arTasks[] = $task;
}else{
if ( $group->getCount() > 0 )
{
$arRm = array();
$hosts = $group->getMembers();
for( $i = 0; $i < count($hosts); $i++ )
{
if ( $hosts[$i] != null )
{
$h = $hosts[$i];
if ( ! ($h->isReadyToImage() && $h->getImage()->getStorageGroup()->getID() == $groupid ) )
{
$arRm[] = $h;
}
}
}
//echo ( "Before: " . $group->getCount() );
for( $i = 0; $i < count($arRm); $i++ )
{
$group->removeMember( $arRm[$i] );
}
//echo ( "After: " . $group->getCount() );
$task = new ScheduledTask( $host, $group, $timer, $ar[“stTaskType”], $ar[“stID”] );
$task->setShutdownAfterTask( $ar[“stShutDown”] == 1 );
$task->setOther1( $ar[“stOther1”] );
$task->setOther2( $ar[“stOther2”] );
$task->setOther3( $ar[“stOther3”] );
$task->setOther4( $ar[“stOther4”] );
$task->setOther5( $ar[“stOther5”] );
$arTasks[] = $task;
}
}
}[/php]This adds a bypass allowing the original script to execute or if the call is coming from the webGUI the script will be shortened bypassing the calls to get info on the members of the group.
-
Find the following on lines 182 - 184
[php]function getGroupById( $id )
{
if ( $this->db != null && is_numeric( $id ) && $id >= 0 )[/php]Edit it to become:
Lines 195 - 197
[php]function getGroupById( $id, $wgui = false )
{
if ( $this->db != null && is_numeric( $id ) && $id >= 0 )[/php]This adds the parameter of $wgui to the getGroupById function setting the default to false if it is not supplied.
Find the following on lines 208 - 214
[php]if (($hid != null) && (is_numeric($hid)))
{
$host = $this->getHostById( $hid );
if ( $host != null ){
$group->addMember( $host );
}
}[/php]Edit it to become:
Line 221 - 229
[php]if (($hid != null) && (is_numeric($hid)))
{
$host = $this->getHostById( $hid );
if ( $host != null ){
If ($wgui == false){
$group->addMember( $host );
}
}
}[/php]This adds an if statement that adds the option to bypass the function call that caused the original issue with the web GUI.
Save and we will move onto the next file.
Make a backup first
cp /var/www/html/fog/management/includes/tasks.sched.include.php /var/www/html/fog/management/includes/tasks.sched.include.bak
Now edit /var/www/html/fog/management/includes/tasks.sched.include.php
Find lines 37 - 47:
[php]<h2><?php print _(“All Scheduled Tasks”); ?></h2>
<?php$core = new FOGCore($conn);
$tasks = $core->getScheduledTasksByStorageGroupID( “%”, true );if ( $tasks != null && count($tasks) > 0 )
{
echo ( “<center><table cellpadding=“0” cellspacing=“0” border=“0” width=“100%”>” );
echo ( “<tr class=“header”><td>”.(“Run time”).“</td><td>”.(“Task Type”).“</td><td>”.(“Is Group”).“</td><td>”.(“Group/Host Name”).“</td><td>”._(“Kill”).“</td></tr>” );
for( $i = 0; $i < count( $tasks ); $i++ )[/php]Edit to become:
Lines 37 - 47
[php]<h2><?php print _(“All Scheduled Tasks”); ?></h2>
<?php$core = new FOGCore($conn);
$tasks = $core->getScheduledTasksByStorageGroupID( “%”, true, true );if ( $tasks != null && count($tasks) > 0 )
{
echo ( “<center><table cellpadding=“0” cellspacing=“0” border=“0” width=“100%”>” );
echo ( “<tr class=“header”><td>”.(“Run time”).“</td><td>”.(“Task Type”).“</td><td>”.(“Is Group”).“</td><td>”.(“Group/Host Name”).“</td><td>”._(“Kill”).“</td></tr>” );
for( $i = 0; $i < count( $tasks ); $i++ )[/php]Save the file.
So after this has all been edited restart the FOGScheduler service
[INDENT=1]service FOGScheduler restart[/INDENT]
You should find that group scheduler tasks begin to work and the webGUI works.
[INDENT=1]tail -f /opt/fog/log/fogscheduler.log[/INDENT]
[INDENT=1][04-22-14 12:24:03 pm] * Task not due to run[/INDENT]
[INDENT=1][04-22-14 12:24:03 pm] * Task run time: * 1 * * *[/INDENT]
[INDENT=1][04-22-14 12:24:03 pm] * Task not due to run[/INDENT]
[INDENT=1][04-22-14 12:24:03 pm] * Task run time: * * * * *[/INDENT]
[INDENT=1][04-22-14 12:24:03 pm] * Found a task that should run…[/INDENT]
[INDENT=1][04-22-14 12:24:03 pm] - Is a group based task.[/INDENT]
[INDENT=1][04-22-14 12:24:03 pm] - Found 1 hosts[/INDENT]
[INDENT=1][04-22-14 12:24:03 pm] - Host: ********[/INDENT]
[INDENT=1][04-22-14 12:24:03 pm] - Task Started![/INDENT]
[INDENT=1][04-22-14 12:24:03 pm] - Cron style - No cleaning![/INDENT]I hope this helps someone as very little information has been posted about this bug and any reports or questions about it are simply responded with: this is a known bug and will be fixed in the next release.
– Edit – Due to a request I have uploaded my working versions of the files edited. I would strongly advice you to follow through my instructions rather than use my files. However if you do use them please make sure you make a backup of both of your original files before replacing them. Instructions for making backups can be found above.
[url=“/_imported_xf_attachments/0/680_FOGCore.class.php?:”]FOGCore.class.php[/url][url=“/_imported_xf_attachments/0/681_tasks.sched.include.php?:”]tasks.sched.include.php[/url]
-
Can you put your code into [ p h p ] [ / p h p ] blocks without the spaces?
-
[quote=“Tom Elliott, post: 25823, member: 7271”]Can you put your code into [ p h p ] [ / p h p ] blocks without the spaces?[/quote]
Sorry Tom, I’ve updated my post. -
Thank you Neil, it just makes it easier for people to copy paste and such.