diff mbox

[Xen-devel,OSSTEST] ms-planner: add a flight summary html report

Message ID 1398677082-30965-1-git-send-email-ian.campbell@citrix.com
State New
Headers show

Commit Message

Ian Campbell April 28, 2014, 9:24 a.m. UTC
I often find myself intested in when a given flight (or the current flight for
a branch) will complete, which with the current resource plan means searching
and cycling through trying to figure out which box ends the latest.

Add an explicit flight status summary, which produces a table for each flight
listing the jobs, there start and end times and the host they run on. The
output is something like a list of these, ordered by flight number (rendered
with links(1), header is bold):

  Flight 26043 [linux-3.4 real] 4 jobs expected to run until 2014-Apr-27 Sun 23:54:19
   build-amd64-pvops                        2014-Apr-27 Sun 20:06:09 2014-Apr-27 Sun 21:21:23 host leaf-beetle
   build-i386-pvops                         2014-Apr-27 Sun 21:20:15 2014-Apr-27 Sun 21:31:27 host grain-weevil
   build-amd64                              2014-Apr-27 Sun 21:21:23 2014-Apr-27 Sun 21:44:22 host leaf-beetle
   build-i386                               2014-Apr-27 Sun 22:18:43 2014-Apr-27 Sun 23:54:19 host field-cricket

I renamed the existing show-html option to show-resource-plan-html for clarity.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
---
It'd be good to sync these reports to e.g. xenbits so other people can see
them. Pressumably this would be reasonably easy with a ssh keypair restricted
in authorized_keys.
---
 ms-planner     | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 ms-queuedaemon | 10 ++++++--
 2 files changed, 82 insertions(+), 3 deletions(-)

Comments

Ian Jackson April 30, 2014, 3:44 p.m. UTC | #1
Ian Campbell writes ("[OSSTEST] ms-planner: add a flight summary html report"):
> I often find myself intested in when a given flight (or the current flight for
> a branch) will complete, which with the current resource plan means searching
> and cycling through trying to figure out which box ends the latest.
> 
> Add an explicit flight status summary, which produces a table for each flight
> listing the jobs, there start and end times and the host they run on. The
> output is something like a list of these, ordered by flight number (rendered
> with links(1), header is bold):
> 
>   Flight 26043 [linux-3.4 real] 4 jobs expected to run until 2014-Apr-27 Sun 23:54:19
>    build-amd64-pvops                        2014-Apr-27 Sun 20:06:09 2014-Apr-27 Sun 21:21:23 host leaf-beetle
>    build-i386-pvops                         2014-Apr-27 Sun 21:20:15 2014-Apr-27 Sun 21:31:27 host grain-weevil
>    build-amd64                              2014-Apr-27 Sun 21:21:23 2014-Apr-27 Sun 21:44:22 host leaf-beetle
>    build-i386                               2014-Apr-27 Sun 22:18:43 2014-Apr-27 Sun 23:54:19 host field-cricket
> 
> I renamed the existing show-html option to show-resource-plan-html for clarity.

Hrm.

I think this is a nice thing to have, but:

> +    while (my ($reso,$evts) = each %{ $plan->{Events} }) {
> +        # [osstest real] job 26010.test-amd64-amd64-xl-win7-amd64
> +        foreach my $evt ( @{$evts} ) {
> +            next unless $evt->{Type} =~ m/^(Start|End)$/;
> +            next unless $evt->{Info} =~ m/^\[(\S+) (\S+)\] job ([0-9]+)\
\.(\S+) (.*)/;

This is rather a layering violation.  Info is for the consumption of
humans.  Perhaps the plan should have an explicit "flight.job" field.
You could compute the "[linux-3.4 real]" part by looking at the common
prefix of all the Infos.

Also, can you wrap everything to 75 or so ?

> +            $flights{$flight}->{Jobs}{$job} = {
> +                Reso => $reso
> +            } unless $flights{$flight}->{Jobs}{$job};

This isn't right.  A job can have more than one resource.  You should
either list each resource as its own line, accumulate a list of
resources.

> +            if ( $evt->{Time} > $flights{$flight}->{End} ) {
> +                $flights{$flight}->{Last} = $evt;
> +                $flights{$flight}->{LastReso} = $reso;

You don't seem to use LastReso and I can't seem to see why you'd want
it.

> +        flight_hdr("Flight $flight [$inf->{Branch} $inf->{Blessing}] ".
> +               (keys %{$inf->{Jobs}})." jobs ".
> +               "expected to run until ".strftime("%Y-%b-%d %a %H:%M:%S"\
, localtime $inf->{End}));

You repeat the strftime rune many times.  You should probably use
show_abs_time.  (Which produces UTC.  I see that ms-planner has a pair
of localtimes in it, which should perhaps be changed.)

> +        foreach my $job (sort { $inf->{Jobs}{$a}->{Start}->{Time} cmp $\
inf->{Jobs}{$b}->{Start}->{Time} } keys %{ $inf->{Jobs} }) {

You repeat the $inf->{Jobs}{$x}->{Start}->{Time} expression.  Make it
an anonymous subref or something and this will become much clearer.

And I think you should probably sort the jobs by end time, not start
time.

> +            my $sevt = $inf->{Jobs}{$job}->{Start};
> +            my $eevt = $inf->{Jobs}{$job}->{End};
> +            print("<tr>\n");
> +            cell($job);
> +            cell(strftime("%Y-%b-%d %a %H:%M:%S", localtime $inf->{Jobs\
}{$job}->{Start}->{Time}));
> +            cell(strftime("%Y-%b-%d %a %H:%M:%S", localtime $inf->{Jobs\
}{$job}->{End}->{Time}));

How about
    foreach my $se (qw(Start End)) {
?

> diff --git a/ms-queuedaemon b/ms-queuedaemon
> index 26d83e2..a5bebd3 100755
> --- a/ms-queuedaemon
> +++ b/ms-queuedaemon
> @@ -224,12 +224,18 @@ proc queuerun-perhaps-step {} {
>  proc report-plan {} {
>      global c
>      if {[catch {
> -        exec ./ms-planner show-html > "$c(WebspaceFile)/resource-plan.html"
> +        exec ./ms-planner show-resource-plan-html > "$c(WebspaceFile)/r\
esource-plan.html"
>      } emsg]} {
> -        log "INTERNAL ERROR showing plan html: $emsg"
> +        log "INTERNAL ERROR showing resource plan html: $emsg"
>      } else {
>          log "report-plan OK"
>      }
> +    if {[catch {
> +        exec ./ms-planner show-flight-summary-html > "$c(WebspaceFile)/\
flight-summary.html"
> +    } emsg]} {
> +        log "INTERNAL ERROR showing flight summary html: $emsg"
> +    } else {
> +        log "report-flight-summary OK"

Repetition.

Thanks,
Ian.
Ian Campbell April 30, 2014, 3:58 p.m. UTC | #2
On Wed, 2014-04-30 at 16:44 +0100, Ian Jackson wrote:

> > +    while (my ($reso,$evts) = each %{ $plan->{Events} }) {
> > +        # [osstest real] job 26010.test-amd64-amd64-xl-win7-amd64
> > +        foreach my $evt ( @{$evts} ) {
> > +            next unless $evt->{Type} =~ m/^(Start|End)$/;
> > +            next unless $evt->{Info} =~ m/^\[(\S+) (\S+)\] job ([0-9]+)\
> \.(\S+) (.*)/;
> 
> This is rather a layering violation.  Info is for the consumption of
> humans.  Perhaps the plan should have an explicit "flight.job" field.

I was a bit more scared of touching the actual planner daemon... But ok.

> You could compute the "[linux-3.4 real]" part by looking at the common
> prefix of all the Infos.
> 
> Also, can you wrap everything to 75 or so ?

Sure.

> > +            $flights{$flight}->{Jobs}{$job} = {
> > +                Reso => $reso
> > +            } unless $flights{$flight}->{Jobs}{$job};
> 
> This isn't right.  A job can have more than one resource.  You should
> either list each resource as its own line, accumulate a list of
> resources.

Oh yes, I'd forgotten this.

> > +            if ( $evt->{Time} > $flights{$flight}->{End} ) {
> > +                $flights{$flight}->{Last} = $evt;
> > +                $flights{$flight}->{LastReso} = $reso;
> 
> You don't seem to use LastReso and I can't seem to see why you'd want
> it.

I think I was just being a completest in case I decided I wanted it.

> 
> > +        flight_hdr("Flight $flight [$inf->{Branch} $inf->{Blessing}] ".
> > +               (keys %{$inf->{Jobs}})." jobs ".
> > +               "expected to run until ".strftime("%Y-%b-%d %a %H:%M:%S"\
> , localtime $inf->{End}));
> 
> You repeat the strftime rune many times.  You should probably use
> show_abs_time.  (Which produces UTC.  I see that ms-planner has a pair
> of localtimes in it, which should perhaps be changed.)

Ack

> > +        foreach my $job (sort { $inf->{Jobs}{$a}->{Start}->{Time} cmp $\
> inf->{Jobs}{$b}->{Start}->{Time} } keys %{ $inf->{Jobs} }) {
> 
> You repeat the $inf->{Jobs}{$x}->{Start}->{Time} expression.  Make it
> an anonymous subref or something and this will become much clearer.

You mean like:
   my $ta = sub { return $inf->{Jobs}{$a}->.... }
   my $tb = sub { return $inf->{Jobs}{$b}->.... }
   foreach ... (sort { $ta() cmp $tb() } keys ... ) { 

Or my $to = sub($) { my $it=shift; return $info-{Jobs}{$it}... } and
$to($a) ?

> And I think you should probably sort the jobs by end time, not start
> time.

OK.

I should note that I realised after I wrote this patch that as jobs
complete others might appear, so you can't necessarily know when the
flight will end, although once the build jobs are done you can generally
get a pretty good idea.

> > +            my $sevt = $inf->{Jobs}{$job}->{Start};
> > +            my $eevt = $inf->{Jobs}{$job}->{End};
> > +            print("<tr>\n");
> > +            cell($job);
> > +            cell(strftime("%Y-%b-%d %a %H:%M:%S", localtime $inf->{Jobs\
> }{$job}->{Start}->{Time}));
> > +            cell(strftime("%Y-%b-%d %a %H:%M:%S", localtime $inf->{Jobs\
> }{$job}->{End}->{Time}));
> 
> How about
>     foreach my $se (qw(Start End)) {
> ?

OK. I never would have come up with that myself.

> > diff --git a/ms-queuedaemon b/ms-queuedaemon
> > index 26d83e2..a5bebd3 100755
> > --- a/ms-queuedaemon
> > +++ b/ms-queuedaemon
> > @@ -224,12 +224,18 @@ proc queuerun-perhaps-step {} {
> >  proc report-plan {} {
> >      global c
> >      if {[catch {
> > -        exec ./ms-planner show-html > "$c(WebspaceFile)/resource-plan.html"
> > +        exec ./ms-planner show-resource-plan-html > "$c(WebspaceFile)/r\
> esource-plan.html"
> >      } emsg]} {
> > -        log "INTERNAL ERROR showing plan html: $emsg"
> > +        log "INTERNAL ERROR showing resource plan html: $emsg"
> >      } else {
> >          log "report-plan OK"
> >      }
> > +    if {[catch {
> > +        exec ./ms-planner show-flight-summary-html > "$c(WebspaceFile)/\
> flight-summary.html"
> > +    } emsg]} {
> > +        log "INTERNAL ERROR showing flight summary html: $emsg"
> > +    } else {
> > +        log "report-flight-summary OK"
> 
> Repetition.

I suppose I can probably figure out enough Tcl to avoid it... I was
being cowardly about making no-trivial mods ;-)

Ian.
Ian Jackson April 30, 2014, 4:21 p.m. UTC | #3
Ian Campbell writes ("Re: [OSSTEST] ms-planner: add a flight summary html report"):
> On Wed, 2014-04-30 at 16:44 +0100, Ian Jackson wrote:
> > This is rather a layering violation.  Info is for the consumption of
> > humans.  Perhaps the plan should have an explicit "flight.job" field.
> 
> I was a bit more scared of touching the actual planner daemon... But ok.

I think you can safely add new fields to the events, although the
existing planner daemon probably won't propagate them.

> > > +            if ( $evt->{Time} > $flights{$flight}->{End} ) {
> > > +                $flights{$flight}->{Last} = $evt;
> > > +                $flights{$flight}->{LastReso} = $reso;
> > 
> > You don't seem to use LastReso and I can't seem to see why you'd want
> > it.
> 
> I think I was just being a completest in case I decided I wanted it.

Right.  I think it's probably going to be tedious to define exactly
what this ought to be so I would leave it out.

> > > +        foreach my $job (sort { $inf->{Jobs}{$a}->{Start}->{Time} cmp $\
> > inf->{Jobs}{$b}->{Start}->{Time} } keys %{ $inf->{Jobs} }) {
> > 
> > You repeat the $inf->{Jobs}{$x}->{Start}->{Time} expression.  Make it
> > an anonymous subref or something and this will become much clearer.
> 
> You mean like:
>    my $ta = sub { return $inf->{Jobs}{$a}->.... }
>    my $tb = sub { return $inf->{Jobs}{$b}->.... }
>    foreach ... (sort { $ta() cmp $tb() } keys ... ) { 
> 
> Or my $to = sub($) { my $it=shift; return $info-{Jobs}{$it}... } and
> $to($a) ?

The latter.  (Or a http://en.wikipedia.org/wiki/Schwartzian_transform
but that seems overkill.)

Hrm, it turns out that some time in the last decade it became possible
to write local lexical subroutines that aren't coderefs.  So try
  sub sortkey { ... }
just before the sort.

> I should note that I realised after I wrote this patch that as jobs
> complete others might appear, so you can't necessarily know when the
> flight will end, although once the build jobs are done you can generally
> get a pretty good idea.

Indeed.  The reader will have to know about that.

> > How about
> >     foreach my $se (qw(Start End)) {
> > ?
> 
> OK. I never would have come up with that myself.

Learn Common Lisp :-).

> > > +    if {[catch {
> > > +        exec ./ms-planner show-flight-summary-html > "$c(WebspaceFile)/\
> > flight-summary.html"
> > > +    } emsg]} {
> > > +        log "INTERNAL ERROR showing flight summary html: $emsg"
> > > +    } else {
> > > +        log "report-flight-summary OK"
> > 
> > Repetition.
> 
> I suppose I can probably figure out enough Tcl to avoid it... I was
> being cowardly about making no-trivial mods ;-)

Heh.  Tcl, like Perl, is a nice language to abstract in.

Thanks,
Ian.
Ian Campbell April 30, 2014, 4:29 p.m. UTC | #4
On Wed, 2014-04-30 at 17:21 +0100, Ian Jackson wrote:
> Ian Campbell writes ("Re: [OSSTEST] ms-planner: add a flight summary html report"):
> > On Wed, 2014-04-30 at 16:44 +0100, Ian Jackson wrote:
> > > This is rather a layering violation.  Info is for the consumption of
> > > humans.  Perhaps the plan should have an explicit "flight.job" field.
> > 
> > I was a bit more scared of touching the actual planner daemon... But ok.
> 
> I think you can safely add new fields to the events, although the
> existing planner daemon probably won't propagate them.

"probably", fantastic ;-)

> > > > +        foreach my $job (sort { $inf->{Jobs}{$a}->{Start}->{Time} cmp $\
> > > inf->{Jobs}{$b}->{Start}->{Time} } keys %{ $inf->{Jobs} }) {
> > > 
> > > You repeat the $inf->{Jobs}{$x}->{Start}->{Time} expression.  Make it
> > > an anonymous subref or something and this will become much clearer.
> > 
> > You mean like:
> >    my $ta = sub { return $inf->{Jobs}{$a}->.... }
> >    my $tb = sub { return $inf->{Jobs}{$b}->.... }
> >    foreach ... (sort { $ta() cmp $tb() } keys ... ) { 
> > 
> > Or my $to = sub($) { my $it=shift; return $info-{Jobs}{$it}... } and
> > $to($a) ?
> 
> The latter.  (Or a http://en.wikipedia.org/wiki/Schwartzian_transform
> but that seems overkill.)

Clever. But yes perhaps overkill...

> Hrm, it turns out that some time in the last decade it became possible
> to write local lexical subroutines that aren't coderefs.  So try
>   sub sortkey { ... }
> just before the sort.

foreach my $foo(sub sortkey{} sort (keys %hash)) {} 

? I don't think that can be what you meant.


> > > How about
> > >     foreach my $se (qw(Start End)) {
> > > ?
> > 
> > OK. I never would have come up with that myself.
> 
> Learn Common Lisp :-).

Now I have two problems. ;-)

Ian.
Ian Jackson May 1, 2014, 11:09 a.m. UTC | #5
Ian Campbell writes ("Re: [OSSTEST] ms-planner: add a flight summary html report"):
> On Wed, 2014-04-30 at 17:21 +0100, Ian Jackson wrote:
> > Hrm, it turns out that some time in the last decade it became possible
> > to write local lexical subroutines that aren't coderefs.  So try
> >   sub sortkey { ... }
> > just before the sort.

These lexical subroutines don't work properly anyway.  I knew there
was a reason I didn't do that.  Forget about that.  What I meant the
first time was something like

   my $jsortkey = sub { $inf->{Jobs}{$_[0]}->{Start}->{Time}; };
   foreach my $job (sort { $jsortkey->($a) cmp $jsortkey->(b) } keys ...

Ian.
diff mbox

Patch

diff --git a/ms-planner b/ms-planner
index f045bbf..4d9787d 100755
--- a/ms-planner
+++ b/ms-planner
@@ -536,7 +536,80 @@  sub showsharetype ($) {
     return $_;
 }
 
-sub cmd_show_html () {
+sub cmd_show_flight_summary_html () {
+    get_current_plan();
+
+    my $earliest= $plan->{Start};
+
+    my %flights;
+    my $jobs = 0;
+    while (my ($reso,$evts) = each %{ $plan->{Events} }) {
+        # [osstest real] job 26010.test-amd64-amd64-xl-win7-amd64
+        foreach my $evt ( @{$evts} ) {
+            next unless $evt->{Type} =~ m/^(Start|End)$/;
+            next unless $evt->{Info} =~ m/^\[(\S+) (\S+)\] job ([0-9]+)\.(\S+) (.*)/;
+            my ($branch,$blessing,$flight,$job,$rest) = ($1,$2,$3,$4,$5);
+            $flights{$flight} = {
+                Branch => $branch,
+                Blessing => $blessing,
+                End => $evt->{Time},
+                Jobs => {},
+                Last => $evt,
+                LastReso => $reso,
+            } unless $flights{$flight};
+            $jobs++;
+            $flights{$flight}->{Jobs}{$job} = {
+                Reso => $reso
+            } unless $flights{$flight}->{Jobs}{$job};
+
+            $flights{$flight}->{Jobs}{$job}->{Start} = $evt if $evt->{Type} eq "Start";
+            $flights{$flight}->{Jobs}{$job}->{End} = $evt if $evt->{Type} eq "End";
+
+            if ( $evt->{Time} > $flights{$flight}->{End} ) {
+                $flights{$flight}->{Last} = $evt;
+                $flights{$flight}->{LastReso} = $reso;
+                $flights{$flight}->{End} = $evt->{Time};
+            }
+        }
+    }
+
+    my @cols = ("Job", "Start", "End", "Host");
+
+    printf("<table>\n<tr>\n");
+    printf(" <th align='left'>%s</th>\n", encode_entities($_)) foreach @cols;
+    printf("</tr>\n");
+
+    sub flight_hdr($) {
+        my $text = encode_entities(shift);
+        printf("<tr><td colspan=%d><b>$text</b></td></tr>", scalar @cols);
+    }
+    sub cell($) {
+        my $text = encode_entities(shift);
+        printf(" <td>$text</td>\n");
+    }
+    foreach my $flight (sort keys %flights) {
+        my $inf = $flights{$flight};
+
+        flight_hdr("Flight $flight [$inf->{Branch} $inf->{Blessing}] ".
+               (keys %{$inf->{Jobs}})." jobs ".
+               "expected to run until ".strftime("%Y-%b-%d %a %H:%M:%S", localtime $inf->{End}));
+
+        foreach my $job (sort { $inf->{Jobs}{$a}->{Start}->{Time} cmp $inf->{Jobs}{$b}->{Start}->{Time} } keys %{ $inf->{Jobs} }) {
+            my $sevt = $inf->{Jobs}{$job}->{Start};
+            my $eevt = $inf->{Jobs}{$job}->{End};
+            print("<tr>\n");
+            cell($job);
+            cell(strftime("%Y-%b-%d %a %H:%M:%S", localtime $inf->{Jobs}{$job}->{Start}->{Time}));
+            cell(strftime("%Y-%b-%d %a %H:%M:%S", localtime $inf->{Jobs}{$job}->{End}->{Time}));
+            cell($inf->{Jobs}{$job}->{Reso});
+            print("</tr>\n");
+        }
+        print("<tr><td>&nbsp</td></tr>\n");
+    }
+    print("</table>\n");
+}
+
+sub cmd_show_resource_plan_html () {
     get_current_plan();
     my $now= time;
 
diff --git a/ms-queuedaemon b/ms-queuedaemon
index 26d83e2..a5bebd3 100755
--- a/ms-queuedaemon
+++ b/ms-queuedaemon
@@ -224,12 +224,18 @@  proc queuerun-perhaps-step {} {
 proc report-plan {} {
     global c
     if {[catch {
-        exec ./ms-planner show-html > "$c(WebspaceFile)/resource-plan.html"
+        exec ./ms-planner show-resource-plan-html > "$c(WebspaceFile)/resource-plan.html"
     } emsg]} {
-        log "INTERNAL ERROR showing plan html: $emsg"
+        log "INTERNAL ERROR showing resource plan html: $emsg"
     } else {
         log "report-plan OK"
     }
+    if {[catch {
+        exec ./ms-planner show-flight-summary-html > "$c(WebspaceFile)/flight-summary.html"
+    } emsg]} {
+        log "INTERNAL ERROR showing flight summary html: $emsg"
+    } else {
+        log "report-flight-summary OK"
 }
 
 proc we-are-thinking {chan} {