Commit 43cdb619 authored by Leigh Stoller's avatar Leigh Stoller

More work on the reservation utilization stuff.

parent b0f17e8e
......@@ -524,16 +524,11 @@ sub DoList()
#
# Hmm, undef/null is a pain with XMLRPC.
#
if ($details->{'approved'} eq "") {
# Maps to JSON NULL.
$details->{'approved'} = undef;
}
if ($details->{'cancel'} eq "") {
# Maps to JSON NULL.
$details->{'cancel'} = undef;
}
if (!exists($details->{'uuid'}) || $details->{'uuid'} eq "") {
$details->{'uuid'} = NewUUID();
foreach my $key ("approved", "cancel", "deleted") {
if (!exists($details->{$key}) || $details->{$key} eq "") {
$details->{$key} = undef;
}
}
#
......@@ -1148,13 +1143,11 @@ sub DoHistory()
#
# Hmm, undef/null is a pain with XMLRPC.
#
if (!exists($res->{'approved'}) ||
$res->{'approved'} eq "") {
$res->{'approved'} = undef;
}
if (!exists($res->{'cancel'}) ||
$res->{'cancel'} eq "") {
$res->{'cancel'} = undef;
foreach my $key ("approved", "cancel", "deleted") {
if (!exists($res->{$key}) || $res->{$key} eq "") {
$res->{$key} = undef;
}
}
if (!exists($res->{'deleted'}) ||
$res->{'deleted'} eq "") {
......
......@@ -87,7 +87,18 @@ sub CollectReservations($$$)
sub CreateTimeline($@)
{
my ($project, @reservations) = @_;
#
# Remember all the types we care about; when computing the counts
# do not bother with nodes that are not in the set of types reserved.
#
my %typesinuse = ();
foreach my $res (@reservations) {
my $type = $res->type();
$typesinuse{$type} = $type;
}
# We want the earliest/latest reservation for getting project usage();
my $earliest = $reservations[0];
if ($debug) {
......@@ -111,31 +122,43 @@ sub CreateTimeline($@)
};
my @tmp = sort $sortfunc @reservations;
my $latest = $tmp[-1];
my $end = (defined($latest->deleted()) ?
$latest->deleted() : $latest->end());
$end = time() if ($end > time());
if ($debug) {
print "The latest reservation end is " .
POSIX::strftime("%m/%d/20%y %H:%M:%S",
localtime($latest->end())) . " \n";
POSIX::strftime("%m/%d/20%y %H:%M:%S", localtime($end)) . " \n";
}
my $end = $latest->end();
$end = time() if ($end > time());
# Get the usage since the beginning of the earliest reservation.
my $usage = $project->Usage($earliest->start() - (3600 * 24), $end);
my $usage = $project->Usage($earliest->start(), $end);
if ($debug) {
print "There are " . scalar(@$usage) . " usage records\n";
if ($debug > 1 && scalar(@$usage)) {
print Dumper(@$usage);
}
@tmp = ();
foreach my $ref (@$usage) {
foreach my $type (keys(%{$ref->{'types'}})) {
if (exists($typesinuse{$type})) {
push(@tmp, $ref);
last;
}
}
}
if (!scalar(@$usage)) {
print STDERR "There are no usage records to process\n"
if (!scalar(@tmp)) {
print STDERR "There are no usage records left to process\n"
if ($debug);
return ();
}
if ($debug > 1) {
print Dumper(@tmp);
}
# Form a timeline of changes in allocation.
my @timeline = ();
foreach my $ref (@$usage) {
foreach my $ref (@tmp) {
push(@timeline,
{"t" => $ref->{'start'}, "details" => $ref, "op" => "alloc"});
......@@ -147,12 +170,6 @@ sub CreateTimeline($@)
# And sort the new list.
@timeline = sort {$a->{'t'} <=> $b->{'t'}} @timeline;
#
# Remember all the types we care about; when computing the counts
# do not bother with nodes that are not in the set of types reserved.
#
my %typesinuse = ();
#
# Correlate the reservations with the sorted list using the start/end
# of the reservation and the timestamps in the timeline. This tells us
......@@ -171,8 +188,26 @@ sub CreateTimeline($@)
my $resStart = $res->start();
my $resEnd = $res->end();
push(@reslist, $res)
if ($stamp >= $resStart && $stamp <= $resEnd);
if ($stamp >= $resStart && $stamp <= $resEnd) {
push(@reslist, $res);
next;
}
#
# But what if this usage record is for an experiment that started
# prior to the beginning of the reservation, and one of two cases
# is true: 1) The experiment is still running. 2) The end of the
# experiment is during the reservation.
#
# In both these cases the reservation is overlaps with the
# experiment.
#
if ($ref->{'op'} eq "alloc" && $stamp <= $resStart &&
($ref->{'details'}->{'end'} eq "" ||
($ref->{'details'}->{'end'} >= $resStart &&
$ref->{'details'}->{'end'} <= $resEnd))) {
push(@reslist, $res);
print "$res is active for $stamp\n";
}
}
next
if (!@reslist);
......@@ -208,23 +243,6 @@ sub CreateTimeline($@)
}
$ref->{'reserved'} = $reserved;
}
#
# Kill off timeline entries that do not include the types we care
# about (types that are reserved at some point during the timeline).
# This reduces entries the number of entries that do not show an
# interesting change (ie: same as previous entry).
#
@tmp = ();
foreach my $ref (@timeline) {
foreach my $type (keys(%{$ref->{'details'}->{'types'}})) {
if (exists($typesinuse{$type})) {
push(@tmp, $ref);
last;
}
}
}
@timeline = @tmp;
# Hmm, this happens.
return ()
if (!@timeline);
......@@ -234,6 +252,9 @@ sub CreateTimeline($@)
#
my @counts = ComputeCounts($project, \%typesinuse, @timeline);
return @counts
if (1);
#
# We want to add additional entrys for the start of each reservation.
#
......
......@@ -1225,6 +1225,48 @@ sub ProjectReservations($$$;$$) {
return @answer;
}
#
# Fake up an object for a historical reservation entry.
#
sub LookupHistorical($$) {
my ($class, $uuid) = @_;
my $query_result =
DBQueryWarn("select *,UNIX_TIMESTAMP(start) AS s, " .
" UNIX_TIMESTAMP(end) AS e, ".
" UNIX_TIMESTAMP(created) AS c, " .
" UNIX_TIMESTAMP(canceled) AS d, " .
" UNIX_TIMESTAMP(deleted) AS k " .
" from reservation_history ".
"where uuid='$uuid'");
return undef
if (!defined($query_result) || !$query_result->numrows);
my $record = $query_result->fetchrow_hashref();
my $self = {};
$self->{'PID'} = $record->{'pid'};
$self->{'PID_IDX'} = $record->{'pid_idx'};
$self->{'EID'} = undef;
$self->{'START'} = $record->{'s'};
$self->{'END'} = $record->{'e'};
$self->{'CREATED'} = $record->{'c'};
$self->{'DELETED'} = $record->{'k'};
$self->{'CANCEL'} = $record->{'d'};
$self->{'TYPE'} = $record->{'type'};
$self->{'NODES'} = $record->{'nodes'};
$self->{'UID'} = $record->{'uid'};
$self->{'UID_IDX'} = $record->{'uid_idx'};
$self->{'NOTES'} = $record->{'notes'};
$self->{'ADMIN_NOTES'} = $record->{'admin_notes'};
$self->{'APPROVED'} = undef;
$self->{'APPROVER'} = undef;
$self->{'UUID'} = $record->{'uuid'};
bless($self, $class);
return $self;
}
#
# Return a list of historical project reservations. Optional type.
# Type can be a single type or a list reference of types.
......@@ -1254,36 +1296,17 @@ sub HistoricalReservations($$$;$) {
push(@clauses, "pid='$pid'");
}
my $query_result =
DBQueryFatal("select *,UNIX_TIMESTAMP(start) AS s, " .
" UNIX_TIMESTAMP(end) AS e, ".
" UNIX_TIMESTAMP(created) AS c, " .
" UNIX_TIMESTAMP(canceled) AS d, " .
" UNIX_TIMESTAMP(deleted) AS k " .
" from reservation_history ".
"where " . join(" AND ", @clauses) . " " .
"order by start asc");
while (my $record = $query_result->fetchrow_hashref()) {
my $self = {};
$self->{'PID'} = $record->{'pid'};
$self->{'PID_IDX'} = $record->{'pid_idx'};
$self->{'EID'} = undef;
$self->{'START'} = $record->{'s'};
$self->{'END'} = $record->{'e'};
$self->{'CREATED'} = $record->{'c'};
$self->{'DELETED'} = $record->{'k'};
$self->{'CANCEL'} = $record->{'d'};
$self->{'TYPE'} = $record->{'type'};
$self->{'NODES'} = $record->{'nodes'};
$self->{'UID'} = $record->{'uid'};
$self->{'UID_IDX'} = $record->{'uid_idx'};
$self->{'NOTES'} = $record->{'notes'};
$self->{'ADMIN_NOTES'} = $record->{'admin_notes'};
$self->{'APPROVED'} = undef;
$self->{'APPROVER'} = undef;
$self->{'UUID'} = $record->{'uuid'};
bless($self, $class);
push(@answer, $self);
DBQueryWarn("select uuid from reservation_history ".
"where " . join(" AND ", @clauses) . " " .
"order by start asc");
return ()
if (!defined($query_result) || !$query_result->numrows);
while (my ($uuid) = $query_result->fetchrow_array()) {
my $record = Reservation->LookupHistorical($uuid);
push(@answer, $record)
if (defined($record));
}
return @answer;
}
......
......@@ -406,12 +406,46 @@ window.DrawResHistoryGraph = (function ()
var xlabel = false;
var uvalues = [];
var pvalues = [];
var backup =false;
var i = 0;
if (_.has(args, "xaxislabel")) {
xlabel = args.xaxislabel;
}
for (var i = 0; i < history.length; i++) {
// Need start of the reservation to narrow what we show,
// since the timeline is going to include stamps before the
// start of the reservation cause of experiments that span
// the reservation start time.
var start = new Date(details.start).getTime();
console.info("draw start", start);
// Scan past any initial timeline entries that are before the
// start of the reservation.
for (i = 0; i < history.length; i++) {
var record = history[i];
var stamp = parseInt(record.t) * 1000;
if (stamp > start) {
if (i == 0) {
// If this is the first record, then the reservation
// started with zero nodes allocated. Add a zero entry.
uvalues.push({"x" : stamp, "y" : 0});
pvalues.push({"x" : stamp, "y" : 0});
console.info("added zero entry at ", stamp);
}
else {
// We skipped some entries. Flag that we want to
// add the previous entry at beginning of the res.
backup = true;
i--;
console.info("added backup entry at ", stamp, i);
}
break;
}
}
for (; i < history.length; i++) {
var record = history[i];
var stamp = parseInt(record.t) * 1000;
var reserved = record.reserved;
......@@ -422,12 +456,17 @@ window.DrawResHistoryGraph = (function ()
if (Array.isArray(reserved)) {
continue;
}
var pcount = allocated[details.remote_pid][details.type];
// Watch for nothing allocated by the user at this time stamp
var ucount = 0;
if (_.has(allocated, details.remote_uid)) {
ucount = allocated[details.remote_uid][details.type];
}
if (backup) {
stamp = start;
backup = false;
}
uvalues.push({"x" : stamp, "y" : parseInt(ucount)});
pvalues.push({"x" : stamp, "y" : parseInt(pcount)});
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment