Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
emulab
emulab-devel
Commits
43cdb619
Commit
43cdb619
authored
Jun 01, 2018
by
Leigh B Stoller
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
More work on the reservation utilization stuff.
parent
b0f17e8e
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
160 additions
and
84 deletions
+160
-84
apt/manage_reservations.in
apt/manage_reservations.in
+10
-17
db/ResUtil.pm.in
db/ResUtil.pm.in
+57
-36
db/Reservation.pm.in
db/Reservation.pm.in
+53
-30
www/aptui/js/resgraphs.js
www/aptui/js/resgraphs.js
+40
-1
No files found.
apt/manage_reservations.in
View file @
43cdb619
...
...
@@ -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
"")
{
...
...
db/ResUtil.pm.in
View file @
43cdb619
...
...
@@ -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
.
#
...
...
db/Reservation.pm.in
View file @
43cdb619
...
...
@@ -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
;
}
...
...
www/aptui/js/resgraphs.js
View file @
43cdb619
...
...
@@ -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
)});
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment