Author: rob
Date: Wed Oct 13 12:32:57 2010
New Revision: 24262
URL: https://svn.nixos.org/websvn/nix/?rev=24262&sc=1
Log:
hydra: add some admin for adding/enabling/etc build machines
Added:
hydra/trunk/src/lib/Hydra/Schema/BuildMachineSystemTypes.pm
hydra/trunk/src/lib/Hydra/Schema/BuildMachines.pm
Modified:
hydra/trunk/src/lib/Hydra/Controller/Admin.pm
hydra/trunk/src/lib/Hydra/Controller/Root.pm
hydra/trunk/src/root/admin.tt
hydra/trunk/src/root/common.tt
hydra/trunk/src/root/layout.tt
hydra/trunk/src/root/navbar.tt
hydra/trunk/src/root/status.tt
hydra/trunk/src/sql/hydra.sql
Modified: hydra/trunk/src/lib/Hydra/Controller/Admin.pm
==============================================================================
--- hydra/trunk/src/lib/Hydra/Controller/Admin.pm Wed Oct 13 11:53:58
2010 (r24261)
+++ hydra/trunk/src/lib/Hydra/Controller/Admin.pm Wed Oct 13 12:32:57
2010 (r24262)
@@ -6,15 +6,178 @@
use Hydra::Helper::Nix;
use Hydra::Helper::CatalystUtils;
use Hydra::Helper::AddBuilds;
+use Data::Dump qw(dump);
+
+sub nixMachines {
+ my ($c) = @_;
+ my $result = '';
+
+ foreach my $machine ($c->model("DB::BuildMachines")->all) {
+ if($machine->enabled) {
+ $result = $result . $machine->username . '@'.
$machine->hostname . ' ';
+ foreach my $system ($machine->buildmachinesystemtypes) {
+ $result = $result . $system->system .',';
+ }
+ chop $result;
+ $result = $result . ' '. $machine->ssh_key . ' ' .
$machine->maxconcurrent . ' '. $machine->speedfactor . ' ' . $machine->options
. "\n";
+ }
+ }
+ return $result;
+}
+
+sub saveNixMachines {
+ my ($c) = @_;
+
+ die("File not writable: /etc/nix.machines") if ! -w "/etc/nix.machines" ;
+
+ open (NIXMACHINES, '>/etc/nix.machines') or die("Could not write to
/etc/nix.machines");
+ print NIXMACHINES nixMachines($c);
+ close (NIXMACHINES);
+}
sub admin : Chained('/') PathPart('admin') CaptureArgs(0) {
my ($self, $c) = @_;
requireAdmin($c);
+ $c->stash->{admin} = 1;
}
sub index : Chained('admin') PathPart('') Args(0) {
my ($self, $c) = @_;
+ $c->stash->{machines} = [$c->model('DB::BuildMachines')->search(
+ {},
+ { order_by => "hostname"
+ , '+select' => ["(select bs.stoptime from buildsteps as bs where
bs.machine = (me.username || '\@' || me.hostname) and not bs.stoptime is null
order by bs.stoptime desc limit 1)"]
+ , '+as' => ['idle']
+ })];
+ $c->stash->{steps} = [ $c->model('DB::BuildSteps')->search(
+ { 'me.busy' => 1, 'schedulingInfo.busy' => 1 },
+ { join => [ 'schedulingInfo', 'build' ]
+ , order_by => [ 'machine', 'outpath' ]
+ } ) ];
$c->stash->{template} = 'admin.tt';
+ }
+
+sub machines : Chained('admin') PathPart('machines') Args(0) {
+ my ($self, $c) = @_;
+ $c->stash->{machines} = [$c->model('DB::BuildMachines')->search({},
{order_by => "hostname"})];
+ $c->stash->{systems} = [$c->model('DB::SystemTypes')->search({}, {select
=> ["system"], order_by => "system" })];
+ $c->stash->{nixMachines} = nixMachines($c);
+ $c->stash->{nixMachinesWritable} = (-e "/etc/nix.machines" && -w
"/etc/nix.machines");
+
+ $c->stash->{template} = 'machines.tt';
+ }
+
+sub machine : Chained('admin') PathPart('machine') CaptureArgs(1) {
+ my ($self, $c, $machineName) = @_;
+
+ requireAdmin($c);
+
+ my $machine = $c->model('DB::BuildMachines')->find($machineName)
+ or notFound($c, "Machine $machineName doesn't exist.");
+
+ $c->stash->{machine} = $machine;
+}
+
+sub machine_edit : Chained('machine') PathPart('edit') Args(0) {
+ my ($self, $c) = @_;
+ $c->stash->{template} = 'machine.tt';
+ $c->stash->{systemtypes} = [$c->model('DB::SystemTypes')->search({},
{order_by => "system"})];
+ $c->stash->{edit} = 1;
+}
+
+sub machine_edit_submit : Chained('machine') PathPart('submit') Args(0) {
+ my ($self, $c) = @_;
+ requirePost($c);
+
+ txn_do($c->model('DB')->schema, sub {
+ updateMachine($c, $c->stash->{machine}) ;
+ });
+ saveNixMachines($c);
+ $c->res->redirect("/admin/machines");
+}
+
+sub updateMachine {
+ my ($c, $machine) = @_;
+
+ my $hostname = trim $c->request->params->{"hostname"};
+ my $username = trim $c->request->params->{"username"};
+ my $maxconcurrent = trim $c->request->params->{"maxconcurrent"};
+ my $speedfactor = trim $c->request->params->{"speedfactor"};
+ my $ssh_key = trim $c->request->params->{"ssh_key"};
+ my $systems = $c->request->params->{"systems"} ;
+
+ error($c, "Invalid or empty username.") if $username eq "";
+ error($c, "Max concurrent builds should be an integer > 0.") if
$maxconcurrent eq "" || ! $maxconcurrent =~ m/[0-9]+/;
+ error($c, "Speed factor should be an integer > 0.") if $speedfactor eq ""
|| ! $speedfactor =~ m/[0-9]+/;
+ error($c, "Invalid or empty SSH key.") if $ssh_key eq "";
+
+ $machine->update(
+ { username => $username
+ , maxconcurrent => $maxconcurrent
+ , speedfactor => $speedfactor
+ , ssh_key => $ssh_key
+ });
+ $machine->buildmachinesystemtypes->delete_all;
+ if(ref($systems) eq 'ARRAY') {
+ for my $s (@$systems) {
+ $machine->buildmachinesystemtypes->create({ system => $s}) ;
+ }
+ } else {
+ $machine->buildmachinesystemtypes->create({ system => $systems}) ;
+ }
+}
+
+sub create_machine : Chained('admin') PathPart('create-machine') Args(0) {
+ my ($self, $c) = @_;
+
+ requireAdmin($c);
+
+ $c->stash->{template} = 'machine.tt';
+ $c->stash->{systemtypes} = [$c->model('DB::SystemTypes')->search({},
{order_by => "system"})];
+ $c->stash->{edit} = 1;
+ $c->stash->{create} = 1;
+}
+
+
+sub create_machine_submit : Chained('admin') PathPart('create-machine/submit')
Args(0) {
+ my ($self, $c) = @_;
+
+ requireAdmin($c);
+
+ my $hostname = trim $c->request->params->{"hostname"};
+ error($c, "Invalid or empty hostname.") if $hostname eq "";
+
+ txn_do($c->model('DB')->schema, sub {
+ my $machine = $c->model('DB::BuildMachines')->create(
+ { hostname => $hostname });
+ updateMachine($c, $machine);
+ });
+ $c->res->redirect("/admin/machines");
+}
+
+sub machine_delete : Chained('machine') PathPart('delete') Args(0) {
+ my ($self, $c) = @_;
+ requirePost($c);
+
+ txn_do($c->model('DB')->schema, sub {
+ $c->stash->{machine}->delete;
+ });
+ saveNixMachines($c);
+ $c->res->redirect("/admin/machines");
+}
+
+sub machine_enable : Chained('machine') PathPart('enable') Args(0) {
+ my ($self, $c) = @_;
+ $c->stash->{machine}->update({ enabled => 1});
+ saveNixMachines($c);
+ $c->res->redirect("/admin/machines");
+}
+
+sub machine_disable : Chained('machine') PathPart('disable') Args(0) {
+ my ($self, $c) = @_;
+ $c->stash->{machine}->update({ enabled => 0});
+ saveNixMachines($c);
+ $c->res->redirect("/admin/machines");
}
sub clearfailedcache : Chained('admin') Path('clear-failed-cache') Args(0) {
@@ -31,7 +194,7 @@
print "Clearing evaluation cache\n";
$c->model('DB::JobsetInputHashes')->delete_all;
- $c->res->redirect("/admin");
+ $c->res->redirect("/admin")
}
sub clearvcscache : Chained('admin') Path('clear-vcs-cache') Args(0) {
Modified: hydra/trunk/src/lib/Hydra/Controller/Root.pm
==============================================================================
--- hydra/trunk/src/lib/Hydra/Controller/Root.pm Wed Oct 13 11:53:58
2010 (r24261)
+++ hydra/trunk/src/lib/Hydra/Controller/Root.pm Wed Oct 13 12:32:57
2010 (r24262)
@@ -98,7 +98,7 @@
my ($self, $c) = @_;
$c->stash->{steps} = [ $c->model('DB::BuildSteps')->search(
{ 'me.busy' => 1, 'schedulingInfo.busy' => 1 },
- { join => [ 'schedulingInfo' ]
+ { join => [ 'schedulingInfo', 'build' ]
, order_by => [ 'machine', 'outpath' ]
} ) ];
}
Added: hydra/trunk/src/lib/Hydra/Schema/BuildMachineSystemTypes.pm
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ hydra/trunk/src/lib/Hydra/Schema/BuildMachineSystemTypes.pm Wed Oct 13
12:32:57 2010 (r24262)
@@ -0,0 +1,81 @@
+package Hydra::Schema::BuildMachineSystemTypes;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+
+=head1 NAME
+
+Hydra::Schema::BuildMachineSystemTypes
+
+=cut
+
+__PACKAGE__->table("BuildMachineSystemTypes");
+
+=head1 ACCESSORS
+
+=head2 hostname
+
+ data_type: text
+ default_value: undef
+ is_foreign_key: 1
+ is_nullable: 0
+ size: undef
+
+=head2 system
+
+ data_type: text
+ default_value: undef
+ is_nullable: 0
+ size: undef
+
+=cut
+
+__PACKAGE__->add_columns(
+ "hostname",
+ {
+ data_type => "text",
+ default_value => undef,
+ is_foreign_key => 1,
+ is_nullable => 0,
+ size => undef,
+ },
+ "system",
+ {
+ data_type => "text",
+ default_value => undef,
+ is_nullable => 0,
+ size => undef,
+ },
+);
+__PACKAGE__->set_primary_key("hostname", "system");
+
+=head1 RELATIONS
+
+=head2 hostname
+
+Type: belongs_to
+
+Related object: L<Hydra::Schema::BuildMachines>
+
+=cut
+
+__PACKAGE__->belongs_to(
+ "hostname",
+ "Hydra::Schema::BuildMachines",
+ { hostname => "hostname" },
+ {},
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.05000 @ 2010-10-08 13:47:26
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:F/voQZLNESTotUOWRbg4WA
+
+
+# You can replace this text with custom content, and it will be preserved on
regeneration
+1;
Added: hydra/trunk/src/lib/Hydra/Schema/BuildMachines.pm
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ hydra/trunk/src/lib/Hydra/Schema/BuildMachines.pm Wed Oct 13 12:32:57
2010 (r24262)
@@ -0,0 +1,133 @@
+package Hydra::Schema::BuildMachines;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+
+=head1 NAME
+
+Hydra::Schema::BuildMachines
+
+=cut
+
+__PACKAGE__->table("BuildMachines");
+
+=head1 ACCESSORS
+
+=head2 hostname
+
+ data_type: text
+ default_value: undef
+ is_nullable: 0
+ size: undef
+
+=head2 username
+
+ data_type: text
+ default_value: undef
+ is_nullable: 0
+ size: undef
+
+=head2 ssh_key
+
+ data_type: text
+ default_value: undef
+ is_nullable: 0
+ size: undef
+
+=head2 options
+
+ data_type: text
+ default_value: undef
+ is_nullable: 0
+ size: undef
+
+=head2 maxconcurrent
+
+ data_type: integer
+ default_value: 2
+ is_nullable: 0
+ size: undef
+
+=head2 speedfactor
+
+ data_type: integer
+ default_value: 1
+ is_nullable: 0
+ size: undef
+
+=head2 enabled
+
+ data_type: integer
+ default_value: 1
+ is_nullable: 0
+ size: undef
+
+=cut
+
+__PACKAGE__->add_columns(
+ "hostname",
+ {
+ data_type => "text",
+ default_value => undef,
+ is_nullable => 0,
+ size => undef,
+ },
+ "username",
+ {
+ data_type => "text",
+ default_value => undef,
+ is_nullable => 0,
+ size => undef,
+ },
+ "ssh_key",
+ {
+ data_type => "text",
+ default_value => undef,
+ is_nullable => 0,
+ size => undef,
+ },
+ "options",
+ {
+ data_type => "text",
+ default_value => undef,
+ is_nullable => 0,
+ size => undef,
+ },
+ "maxconcurrent",
+ { data_type => "integer", default_value => 2, is_nullable => 0, size =>
undef },
+ "speedfactor",
+ { data_type => "integer", default_value => 1, is_nullable => 0, size =>
undef },
+ "enabled",
+ { data_type => "integer", default_value => 1, is_nullable => 0, size =>
undef },
+);
+__PACKAGE__->set_primary_key("hostname");
+
+=head1 RELATIONS
+
+=head2 buildmachinesystemtypes
+
+Type: has_many
+
+Related object: L<Hydra::Schema::BuildMachineSystemTypes>
+
+=cut
+
+__PACKAGE__->has_many(
+ "buildmachinesystemtypes",
+ "Hydra::Schema::BuildMachineSystemTypes",
+ { "foreign.hostname" => "self.hostname" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.05000 @ 2010-10-11 12:58:16
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:gje+VA73Hghl7JXp+Fl8pw
+
+
+# You can replace this text with custom content, and it will be preserved on
regeneration
+1;
Modified: hydra/trunk/src/root/admin.tt
==============================================================================
--- hydra/trunk/src/root/admin.tt Wed Oct 13 11:53:58 2010 (r24261)
+++ hydra/trunk/src/root/admin.tt Wed Oct 13 12:32:57 2010 (r24262)
@@ -12,8 +12,45 @@
<li>[% INCLUDE maybeLink uri =
c.uri_for(c.controller('Admin').action_for('clearvcscache')) content = "Clear
VCS caches" confirmmsg = "Are you sure you want to clear the VCS caches?"
%]</li>
</ul>
</li>
- <li>[% INCLUDE maybeLink uri =
c.uri_for(c.controller('Admin').action_for('managenews')) content = "News"
%]</li>
</ul>
+<h2>Status</h2>
+
+[% FOREACH m IN machines %]
+<table style="width: 40em;">
+ <thead>
+ <tr>
+ <th colspan="5">
+ [% IF m.enabled == 1 %]
+ [% INCLUDE maybeLink uri = c.uri_for('/admin/machine'
m.hostname 'disable' ) content='-' %]
+ [% ELSE %]
+ [% INCLUDE maybeLink uri = c.uri_for('/admin/machine'
m.hostname 'enable' ) content='+' %]
+ [% END %]
+ [% m.hostname %] <tt>[% FOREACH ms IN
m.buildmachinesystemtypes %] [% ms.system %][% END %]</tt>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ [% idle = 1 %]
+ [% FOREACH step IN steps %]
+ [% IF step.machine.match('@(.*)').0 == m.hostname %]
+ [% idle = 0 %]
+ <tr>
+ <td><tt>[% INCLUDE renderFullJobName project =
step.build.project.name jobset = step.build.jobset.name job =
step.build.job.name %]</tt></td>
+ <td><tt>[% step.system %]</tt></td>
+ <td><a href="[% c.uri_for('/build' step.build.id) %]">[%
step.build.id %]</a></td>
+ <td><tt>[% step.outpath.match('-(.*)').0 %]</tt></td>
+ <td class='right'>[% INCLUDE renderDuration duration = curTime
- step.starttime %] </td>
+ </tr>
+ [% END %]
+ [% END %]
+ [% IF idle == 1 %]
+ <tr><td colspan="5">Idle since [% INCLUDE renderDuration duration =
curTime - m.get_column('idle') %]</td></tr>
+ [% END %]
+ </tbody>
+</table>
+
+[% END %]
+
[% END %]
Modified: hydra/trunk/src/root/common.tt
==============================================================================
--- hydra/trunk/src/root/common.tt Wed Oct 13 11:53:58 2010 (r24261)
+++ hydra/trunk/src/root/common.tt Wed Oct 13 12:32:57 2010 (r24262)
@@ -334,3 +334,23 @@
[% END %]
+[% BLOCK hydraStatus %]
+<table class="tablesorter">
+ <thead>
+
<tr><th>Machine</th><th>Job</th><th>Type</th><th>Build</th><th>Step</th><th>What</th><th>Since</th></tr>
+ </thead>
+ <tbody>
+ [% FOREACH step IN steps %]
+ <tr>
+ <td><tt>[% IF step.machine; step.machine.match('@(.*)').0; ELSE;
'localhost'; END %]</tt></td>
+ <td><tt>[% INCLUDE renderFullJobName project = step.build.project.name
jobset = step.build.jobset.name job = step.build.job.name %]</tt></td>
+ <td><tt>[% step.system %]</tt></td>
+ <td><a href="[% c.uri_for('/build' step.build.id) %]">[% step.build.id
%]</a></td>
+ <td><a href="[% c.uri_for('/build' step.build.id 'nixlog' step.stepnr)
%]">[% step.stepnr %]</a></td>
+ <td><tt>[% step.outpath.match('-(.*)').0 %]</tt></td>
+ <td class='right'>[% INCLUDE renderDuration duration = curTime -
step.starttime %] </td>
+ </tr>
+ [% END %]
+ </tbody>
+</table>
+[% END %]
Modified: hydra/trunk/src/root/layout.tt
==============================================================================
--- hydra/trunk/src/root/layout.tt Wed Oct 13 11:53:58 2010 (r24261)
+++ hydra/trunk/src/root/layout.tt Wed Oct 13 12:32:57 2010 (r24262)
@@ -16,7 +16,7 @@
<link rel="stylesheet" href="/static/css/nixos-site.css" type="text/css" />
<link rel="stylesheet" href="/static/css/logfile.css" type="text/css" />
<script type="text/javascript"
src="/static/js/jquery/js/jquery-1.4.2.min.js"></script>
- <script type="text/javascript"
src="/static/js/jquery/js/jquery-ui-1.8.4.custom.min.js"></script>
+ <script type="text/javascript"
src="/static/js/jquery/js/jquery-ui-1.8.5.custom.min.js"></script>
<script type="text/javascript"
src="/static/js/tablesorter/jquery.tablesorter.js"></script>
<script type="text/javascript">
Modified: hydra/trunk/src/root/navbar.tt
==============================================================================
--- hydra/trunk/src/root/navbar.tt Wed Oct 13 11:53:58 2010 (r24261)
+++ hydra/trunk/src/root/navbar.tt Wed Oct 13 12:32:57 2010 (r24262)
@@ -24,7 +24,7 @@
</ul>
[% END %]
-[% IF project %]
+[% IF project || admin %]
<div id="left-bar">
@@ -73,6 +73,17 @@
title = "Errors" %]
[% END %]
[% END %]
+
+ [% IF admin %]
+ [% WRAPPER makeSubMenu title="Admin" %]
+ [% INCLUDE makeLink
+ uri = c.uri_for(c.controller('Admin').action_for('machines'))
+ title = "Machines" %]
+ [% INCLUDE makeLink
+ uri = c.uri_for(c.controller('Admin').action_for('managenews'))
+ title = "News" %]
+ [% END %]
+ [% END %]
</div>
[% END %]
Modified: hydra/trunk/src/root/status.tt
==============================================================================
--- hydra/trunk/src/root/status.tt Wed Oct 13 11:53:58 2010 (r24261)
+++ hydra/trunk/src/root/status.tt Wed Oct 13 12:32:57 2010 (r24262)
@@ -3,22 +3,6 @@
<h1>Hydra Status</h1>
-<table class="tablesorter">
- <thead>
-
<tr><th>Machine</th><th>Type</th><th>Build</th><th>Step</th><th>What</th><th>Since</th></tr>
- </thead>
- <tbody>
- [% FOREACH step IN steps %]
- <tr>
- <td><tt>[% IF step.machine; step.machine.match('@(.*)').0; ELSE;
'localhost'; END %]</tt></td>
- <td><tt>[% step.system %]</tt></td>
- <td><a href="[% c.uri_for('/build' step.build.id) %]">[% step.build.id
%]</a></td>
- <td><a href="[% c.uri_for('/build' step.build.id 'nixlog' step.stepnr)
%]">[% step.stepnr %]</a></td>
- <td><tt>[% step.outpath.match('-(.*)').0 %]</tt></td>
- <td class='right'>[% INCLUDE renderDuration duration = curTime -
step.starttime %] </td>
- </tr>
- [% END %]
- </tbody>
-</table>
+[% INCLUDE hydraStatus %]
[% END %]
Modified: hydra/trunk/src/sql/hydra.sql
==============================================================================
--- hydra/trunk/src/sql/hydra.sql Wed Oct 13 11:53:58 2010 (r24261)
+++ hydra/trunk/src/sql/hydra.sql Wed Oct 13 12:32:57 2010 (r24262)
@@ -485,6 +485,22 @@
foreign key (author) references Users(userName) on delete cascade on
update cascade
);
+create table BuildMachines (
+ hostname text primary key NOT NULL,
+ username text NOT NULL,
+ ssh_key text NOT NULL,
+ options text NOT NULL,
+ maxconcurrent integer DEFAULT 2 NOT NULL,
+ speedfactor integer DEFAULT 1 NOT NULL,
+ enabled integer DEFAULT 1 NOT NULL
+);
+
+create table BuildMachineSystemTypes (
+ hostname text NOT NULL,
+ system text NOT NULL,
+ primary key (hostname, system),
+ foreign key (hostname) references BuildMachines(hostname) on delete cascade
+);
-- Some indices.
create index IndexBuildInputsOnBuild on BuildInputs(build);
_______________________________________________
nix-commits mailing list
[email protected]
http://mail.cs.uu.nl/mailman/listinfo/nix-commits