managementnode/lib/VCL/Module/Predictive/Level_1.pm (359 lines of code) (raw):

#!/usr/bin/perl -w ############################################################################### # $Id$ ############################################################################### # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### =head1 NAME VCL::Module::Predictive::Level_1 - VCL predictive loading module for "Level 1" algorithm =head1 SYNOPSIS use base qw(VCL::Module::Predictive::Level_1); =head1 DESCRIPTION Needs to be written. =cut ############################################################################### package VCL::Module::Predictive::Level_1; # Specify the lib path using FindBin use FindBin; use lib "$FindBin::Bin/../../.."; # Configure inheritance use base qw(VCL::Module::Predictive); # Specify the version of this module our $VERSION = '2.5.1'; # Specify the version of Perl to use use 5.008000; use strict; use warnings; use diagnostics; use English '-no_match_vars'; use VCL::utils; ############################################################################### =head1 OBJECT METHODS =cut #////////////////////////////////////////////////////////////////////////////// =head2 get_next_image Parameters : None. Must be called as an object method. Returns : Description : =cut sub get_next_image { my $self = shift; if (ref($self) !~ /Level_1/) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method, process exiting"); exit 1; } # Retrieve variables from the DataStructure my $request_id = $self->data->get_request_id(); my $reservation_id = $self->data->get_reservation_id(); my $computer_id = $self->data->get_computer_id(); my $computer_short_name = $self->data->get_computer_short_name(); my $computer_nextimage_id = $self->data->get_computer_nextimage_id(0); my @ret_array; my $notify_prefix = "predictive_reload_Level_1 :"; notify($ERRORS{'OK'}, 0, "$notify_prefix starting predictive_reload_level_1 for $computer_id"); # Check if node is part of block reservation if (is_inblockrequest($computer_id)) { notify($ERRORS{'DEBUG'}, 0, "computer id $computer_id is in blockComputers table"); my @block_ret_array = get_block_request_image_info($computer_id); if (defined($block_ret_array[0]) && $block_ret_array[0]) { push(@ret_array, "reload", @block_ret_array); return @ret_array; } else { notify($ERRORS{'WARNING'}, 0, "computer $computer_id is part of blockComputers, failed to return image info"); } } # If nextimageid set, set to default 0 and return the imageid if (defined($computer_nextimage_id) && $computer_nextimage_id) { #Get computer_nextimage_id info my $select_nextimage = " SELECT DISTINCT imagerevision.imagename AS imagename, imagerevision.id AS imagerevisionid, image.id AS imageid FROM image, computer, imagerevision WHERE imagerevision.imageid = computer.nextimageid AND imagerevision.production = 1 AND computer.nextimageid = image.id AND computer.id = $computer_id AND image.name NOT LIKE 'noimage' "; my @next_selected_rows = database_select($select_nextimage); # Check to make sure at least 1 row were returned if (scalar @next_selected_rows == 0) { notify($ERRORS{'OK'}, 0, "$notify_prefix next image not set for computerid $computer_id"); } elsif (scalar @next_selected_rows > 1) { notify($ERRORS{'WARNING'}, 0, "" . scalar @next_selected_rows . " rows were returned from database select"); } else { notify($ERRORS{'OK'}, 0, "$notify_prefix returning nextimage image=$next_selected_rows[0]{imagename} imageid=$next_selected_rows[0]{imageid}"); my @next_image_ret_array; push (@next_image_ret_array, "reload", $next_selected_rows[0]{imagename}, $next_selected_rows[0]{imageid}, $next_selected_rows[0]{imagerevisionid}); #Clear next_imageid if (!clear_next_image_id($computer_id)) { notify($ERRORS{'WARNING'}, 0, "$notify_prefix failed to clear next_image_id for computerid $computer_id"); } return @next_image_ret_array; } } my $select_statement = " SELECT DISTINCT req.start AS starttime, ir.imagename AS imagename, res.imagerevisionid AS imagerevisionid, res.imageid AS imageid FROM reservation res, request req, image i, state s, imagerevision ir WHERE res.requestid = req.id AND req.stateid = s.id AND i.id = res.imageid AND ir.id = res.imagerevisionid AND res.computerid = $computer_id AND (s.name = \'new\' OR s.name = \'reload\' OR s.name = \'imageprep\') "; # Call the database select subroutine # This will return an array of one or more rows based on the select statement my @selected_rows = database_select($select_statement); # Check to make sure 1 or more rows were returned if (scalar @selected_rows > 0) { # Loop through list of upcoming reservations # Based on the start time load the next one my $now = time(); # It contains a hash for (@selected_rows) { my %reservation_row = %{$_}; my $epoch_start = convert_to_epoch_seconds($reservation_row{starttime}); my $diff = $epoch_start - $now; # If start time is less than 50 minutes from now return this image notify($ERRORS{'OK'}, 0, "$notify_prefix diff= $diff image= $reservation_row{imagename} imageid=$reservation_row{imageid}"); if ($diff < (50 * 60)) { notify($ERRORS{'OK'}, 0, "$notify_prefix future reservation detected diff= $diff image= $reservation_row{imagename} imageid=$reservation_row{imageid}"); push(@ret_array, "reload", $reservation_row{imagename}, $reservation_row{imageid}, $reservation_row{imagerevisionid}); return @ret_array; } } ## end for (@selected_rows) } ## end if (scalar @selected_rows > 0) # No upcoming reservations - determine most popular, unloaded image # determine state of system # get machine type my $select_type = " SELECT type,provisioningid FROM computer WHERE id = $computer_id "; my @data = database_select($select_type); if (scalar @data == 0) { notify($ERRORS{'WARNING'}, 0, "$notify_prefix failed to fetch provisioningid for computer_id $computer_id"); return 0; } my $type = $data[0]{type}; my $provisioningid = $data[0]{provisioningid}; # online machines my $select_online = " SELECT COUNT(id) as cnt FROM computer WHERE stateid IN (2, 3, 6, 8, 11) AND type = '$type' "; @data = database_select($select_online); if (scalar @data == 0) { notify($ERRORS{'WARNING'}, 0, "$notify_prefix failed to run query for computer_id $computer_id\n $select_online"); return 0; } my $online = $data[0]{cnt}; # available machines my $select_available = " SELECT COUNT(id) AS cnt FROM computer WHERE stateid = 2 AND type = '$type' "; @data = database_select($select_available); if (scalar @data == 0) { notify($ERRORS{'WARNING'}, 0, "$notify_prefix failed to run query for computer_id $computer_id\n $select_available"); return 0; } my $avail = $data[0]{cnt}; # check if > X% usage, look at past X days, otherwise, look at past 2 months my $timeframe; my $notavail = ($online - $avail); my $usage = ($notavail / $online); if ($usage > 0.40) { $timeframe = '1 DAY'; } elsif ($usage > 0.35) { $timeframe = '2 DAY'; } elsif ($usage > 0.30) { $timeframe = '3 DAY'; } elsif ($usage > 0.25) { $timeframe = '4 DAY'; } elsif ($usage > 0.20) { $timeframe = '5 DAY'; } elsif ($usage > 0.15) { $timeframe = '10 DAY'; } elsif ($usage > 0.10) { $timeframe = '20 DAY'; } elsif ($usage > 0.05) { $timeframe = '30 DAY'; } else { $timeframe = '2 MONTH'; } notify($ERRORS{'OK'}, 0, "$notify_prefix computer_short_name= $computer_short_name type= $type"); notify($ERRORS{'OK'}, 0, "$notify_prefix avail= $avail notavail= $notavail online= $online timeframe= $timeframe"); # what images map to this computer my $select_mapped_images = " SELECT id FROM resource WHERE resourcetypeid = 12 AND subid = $computer_id "; @data = database_select($select_mapped_images); if (scalar @data == 0) { notify($ERRORS{'WARNING'}, 0, "$notify_prefix failed to run query for computer_id $computer_id\n $select_mapped_images"); return 0; } my $resourceid = $data[0]{id}; my $select_compgrps1 = " SELECT resourcegroupid FROM resourcegroupmembers WHERE resourceid = $resourceid "; @data = database_select($select_compgrps1); if (scalar @data == 0) { notify($ERRORS{'WARNING'}, 0, "$notify_prefix failed to run query for computer_id $computer_id\n $select_compgrps1"); return 0; } my @compgroups; foreach (@data) { my %row = %{$_}; push(@compgroups, $row{resourcegroupid}); } my $inlist = join(',', @compgroups); my $select_imggrps1 = " SELECT resourcegroupid2 FROM resourcemap WHERE resourcetypeid1 = 12 AND resourcegroupid1 IN ($inlist) AND resourcetypeid2 = 13 "; @data = database_select($select_imggrps1); my @imggroups; foreach (@data) { my %row = %{$_}; push(@imggroups, $row{resourcegroupid2}); } my $select_imggrps2 = " SELECT resourcegroupid1 FROM resourcemap WHERE resourcetypeid2 = 12 AND resourcegroupid2 IN ($inlist) AND resourcetypeid1 = 13 "; @data = database_select($select_imggrps2); foreach (@data) { my %row = %{$_}; push(@imggroups, $row{resourcegroupid1}); } if (scalar @imggroups == 0) { notify($ERRORS{'WARNING'}, 0, "$notify_prefix failed to run query for computer_id $computer_id\n $select_imggrps2"); return 0; } $inlist = join(',', @imggroups); my $select_imageids = " SELECT DISTINCT(r.subid) FROM image i, OS o, resource r, resourcegroupmembers rgm, OSinstalltype osit, provisioningOSinstalltype posit WHERE rgm.resourceid = r.id AND r.resourcetypeid = 13 AND rgm.resourcegroupid IN ($inlist) AND r.subid = i.id AND i.deleted = 0 AND i.OSid = o.id AND o.installtype = osit.name AND osit.id = posit.OSinstalltypeid AND posit.provisioningid = $provisioningid "; my @imgids; @data = database_select($select_imageids); if (scalar @data == 0) { notify($ERRORS{'WARNING'}, 0, "$notify_prefix failed to run query for computer_id $computer_id\n $select_imageids"); return 0; } foreach (@data) { my %row = %{$_}; push(@imgids, $row{subid}); } my $numselected_imagids = @imgids; notify($ERRORS{'OK'}, 0, "$notify_prefix $numselected_imagids available images can go on $computer_short_name"); # which of those are loaded $inlist = join(',', @imgids); my $select_loaded = " SELECT DISTINCT(currentimageid), COUNT(currentimageid) AS count FROM computer WHERE currentimageid IN ($inlist) AND stateid = 2 GROUP BY currentimageid HAVING count > 1 "; @data = database_select($select_loaded); my @loaded; foreach (@data) { my %row = %{$_}; push(@loaded, $row{currentimageid}); } my $already_loaded_once = @loaded; notify($ERRORS{'OK'}, 0, "$notify_prefix $already_loaded_once of $numselected_imagids available images loaded at least once"); # which of those are not loaded (find difference of @imagids and @loaded) my (@intersection, @notloaded, $element); @intersection = @notloaded = (); my %count = (); foreach $element (@imgids, @loaded) {$count{$element}++} foreach $element (keys %count) { push @{$count{$element} > 1 ? \@intersection : \@notloaded}, $element; } my $not_loaded = @notloaded; notify($ERRORS{'OK'}, 0, "$notify_prefix $not_loaded of $numselected_imagids total images available for selection"); # get the most popular in $timeframe $inlist = join(',', @notloaded); my $select_imageid = " SELECT COUNT(imageid) AS cnt, imageid FROM log WHERE imageid IN ($inlist) AND start > (NOW() - INTERVAL $timeframe) GROUP BY imageid ORDER BY cnt DESC LIMIT 1 "; @data = database_select($select_imageid); if (scalar @data == 0) { notify($ERRORS{'WARNING'}, 0, "$notify_prefix failed to run query for computer_id $computer_id\n $select_imageid"); return 0; } my $imageid = $data[0]{imageid}; notify($ERRORS{'OK'}, 0, "$notify_prefix imageid= $imageid is most popular image during last $timeframe"); # get extra data about the image my $select_extra = " SELECT i.name, r.id FROM image i, imagerevision r WHERE i.id = $imageid AND r.imageid = $imageid AND r.production = 1 "; @data = database_select($select_extra); if (scalar @data == 0) { notify($ERRORS{'WARNING'}, 0, "$notify_prefix failed to run query for computer_id $computer_id\n $select_extra"); return 0; } notify($ERRORS{'OK'}, 0, "$notify_prefix $computer_id $data[0]{name}, $imageid, $data[0]{id}"); push(@ret_array, "reload", $data[0]{name}, $imageid, $data[0]{id}); return @ret_array; } ## end sub get_next_image_revision #////////////////////////////////////////////////////////////////////////////// 1; __END__ =head1 SEE ALSO L<http://cwiki.apache.org/VCL/> =cut