File Coverage

blib/lib/WebService/TestSystem.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             WebService::TestSystem - Web service for implementing a distributed
5             testing system.
6              
7             =head1 SYNOPSIS
8              
9             my $testsys = new WebService::TestSystem;
10              
11             # Getting a list of tests
12             foreach my $test (@{$testsys->get_tests()}) {
13             print "$test->{id} $test->{descriptor}\n";
14             }
15              
16             # Getting a list of hosts
17             foreach my $host (@{$testsys->get_hosts()}) {
18             print "$host->{id} $host->{descriptor}\n";
19             }
20              
21             # Submitting tests
22             my %request;
23             if (! $testsys->validate_test_request(\%request) ) {
24             my %errors = $testsys->get_validation_errors();
25             } else {
26             my $test_request_id = $testsys->request_test(%request);
27             print "Test request #$test_request_id submitted\n";
28             }
29              
30             # System Metrics
31             @metrics = $testsys->metrics_test_run_time(2004, 12);
32             @metrics = $testsys->metrics_requests_per_month(2004, 'all')
33             @metrics = $testsys->metrics_distros_tested_per_month(2004)
34             etc.
35              
36              
37             =head1 DESCRIPTION
38              
39             B presents a programmatic interface (API) for
40             remote interactions with a software testing service. In other words,
41             this provides a set of remote procedure calls (RPCs) for requesting test
42             runs, monitoring systems under test (SUT), and so forth.
43              
44             =head1 FUNCTIONS
45              
46             =cut
47              
48             package WebService::TestSystem;
49             @WebService::TestSystem::ISA = qw(WebService::TicketAuth::DBI);
50              
51 1     1   56232 use strict;
  1         2  
  1         38  
52 1     1   1047 use Config::Simple;
  1         24573  
  1         13  
53 1     1   500 use WebService::TicketAuth::DBI;
  0            
  0            
54             use WebService::TestSystem::Metrics;
55             use WebService::TestSystem::Request;
56             use DBI;
57              
58             # This is the location of the configuration file.
59             # You can update this value here if you wish to move it to a
60             # different location.
61             my $config_file = "/etc/webservice_testsystem/testsystem.conf";
62              
63             use vars qw($VERSION %FIELDS);
64             our $VERSION = '0.06';
65              
66             use base 'WebService::TicketAuth::DBI';
67             use fields qw(
68             stpdb_dbh
69             stpdb_dbi
70             stpdb_user
71             stpdb_pass
72              
73             metrics
74             request
75              
76             _error_msg
77             _debug
78             );
79              
80             =head2 new(%args)
81              
82             Establishes a new WebService::TestSystem instance. This sets up a database
83             connection.
84              
85             =cut
86              
87             sub new {
88             my $class = shift;
89             my WebService::TestSystem $self = fields::new($class);
90              
91             # Load up configuration parameters from config file
92             my %config;
93             my $errormsg = '';
94             if (! Config::Simple->import_from($config_file, \%config)) {
95             $errormsg = "Could not load config file '$config_file': " .
96             Config::Simple->error()."\n";
97             }
98              
99             $self->SUPER::new(%config);
100            
101             foreach my $param (qw(stpdb_dbi stpdb_user stpdb_pass)) {
102             if (defined $config{$param}) {
103             $self->{$param} = $config{$param};
104             }
105             }
106             $self->{_error_msg} .= $errormsg;
107              
108             return $self;
109             }
110              
111             # Internal routine for getting the database handle; if it does not
112             # yet exist, it creates a new one.
113             sub _get_dbh {
114             my $self = shift;
115              
116             $self->{'stpdb_dbh'} =
117             DBI->connect_cached($self->{'stpdb_dbi'},
118             $self->{'stpdb_user'},
119             $self->{'stpdb_pass'}
120             );
121             if (! $self->{'stpdb_dbh'}) {
122             $self->_set_error("Could not connect to '"
123             .$self->{'stpdb_dbi'}
124             ."' as user '"
125             .$self->{'stpdb_user'}
126             ."': $! \n$DBI::errstr\n");
127             }
128             return $self->{'stpdb_dbh'};
129             }
130              
131             # Internal routine for setting the error message
132             sub _set_error {
133             my $self = shift;
134              
135             $self->{'_error_msg'} = shift;
136             }
137              
138             =head2 get_error()
139              
140             Returns the most recent error message. If any of this module's routines
141             return undef, this routine can be called to retrieve a message about
142             what happened. If several errors have occurred, this will only return
143             the most recently encountered one.
144              
145             =cut
146              
147             sub get_error {
148             my $self = shift;
149             return $self->{'_error_msg'};
150             }
151              
152             # Gets the WebService::TestSystem::Metrics object (creating it if needed)
153             sub _metrics {
154             my $self = shift;
155              
156             if (! defined $self->{metrics}) {
157             $self->{metrics} = new WebService::TestSystem::Metrics(app => $self);
158             }
159              
160             return $self->{metrics};
161             }
162              
163             # Gets the WebService::TestSystem::Request object (creating it if needed)
164             sub _request {
165             my $self = shift;
166              
167             if (! defined $self->{_request}) {
168             $self->{_request} = new WebService::TestSystem::Request(app => $self);
169             }
170              
171             return $self->{_request};
172             }
173              
174             ##################
175             # Authentication / Login management
176              
177             # Override for how long to allow tickets to last
178             sub ticket_duration {
179             my $self = shift;
180             my $username = shift;
181              
182             # Give everyone 24 hour logins
183             return 24*60*60;
184             }
185              
186             sub login {
187             my $self = shift;
188              
189             return $self->SUPER::login(@_);
190             }
191              
192              
193             ###################
194             # These just redirect into the _metrics sub-object.
195              
196             sub metrics_requests_per_month {
197             my $self = shift;
198             return $self->_metrics()->metrics_requests_per_month(@_);
199             }
200              
201             sub metrics_test_run_time {
202             my $self = shift;
203             return $self->_metrics()->metrics_test_run_time(@_);
204             }
205              
206             sub metrics_distros_tested_per_month {
207             my $self = shift;
208             return $self->_metrics()->metrics_distros_tested_per_month(@_);
209             }
210              
211             sub metrics_test_request_status_totals {
212             my $self = shift;
213             return $self->_metrics()->metrics_test_request_status_totals(@_);
214             }
215              
216             sub metrics_queue_lengths {
217             my $self = shift;
218             return $self->_metrics()->metrics_queue_lengths(@_);
219             }
220              
221             sub metrics_host_type_test_status_totals {
222             my $self = shift;
223             return $self->_metrics()->metrics_host_type_test_status_totals(@_);
224             }
225              
226             sub metrics_queue_age {
227             my $self = shift;
228             return $self->_metrics()->metrics_queue_age(@_);
229             }
230              
231             sub metrics_patches_queued {
232             my $self = shift;
233             return $self->_metrics()->metrics_patches_queued(@_);
234             }
235              
236             sub metrics_monthly_tests_per_host {
237             my $self = shift;
238             return $self->_metrics()->metrics_monthly_tests_per_host(@_);
239             }
240              
241             ################
242             # Test request and validation
243              
244             =head2 get_validation_errors()
245              
246             Retrieves a hash of error messages from the last call to
247             validate_test_request().
248              
249             =cut
250              
251             sub get_validation_errors {
252             my $self = shift;
253             return $self->_request()->get_validation_errors();
254             }
255              
256             =head2 validate_test_request(\%request)
257              
258             Checks the validity of a given test request. This routine also converts
259             string values into ID's as appropriate, and updates %request in the
260             process.
261              
262             Returns a true value on successful validation, false if there is a
263             validation error, or undef if there is a problem. Validation errors can
264             be retrieved via the get_validation_errors() routine. General error
265             messages can be obtained via the get_error() routine.
266              
267             =cut
268              
269             sub validate_test_request {
270             my $self = shift;
271            
272             return $self->_request()->validate_test_request(@_);
273             }
274              
275             =head2 request_test(\%request)
276              
277             Issues a test request into the system.
278              
279             Returns undef on error, or the test request ID number on success. In
280             case of error, you can retrieve the error message via the get_error()
281             routine.
282              
283             This routine calls validate_test_request() to check inputs prior to
284             submission. If any errors are found, it will return undef, with the
285             error message set to 'test request failed validation'. The errors
286             themselves can be retrieved via the get_validation_errors() routine.
287              
288             =cut
289              
290             sub request_test {
291             my $self = shift;
292             my $request = shift;
293              
294             # Validate the request
295             if (! $self->validate_test_request($request) ) {
296             $self->_set_error("Test request failed validation. "
297             . $self->get_error());
298             return undef;
299             }
300              
301             my $test_request_id = $self->_request()->request_test($request);
302              
303             if (! $test_request_id) {
304             $self->_set_error($self->_request()->get_error());
305             return undef;
306             }
307              
308             return $test_request_id;
309             }
310              
311             sub cancel_test_request {
312             my $self = shift;
313             my $id = shift;
314              
315             # TODO
316              
317             return "Unimplemented";
318             }
319              
320             sub change_test_request {
321             my $self = shift;
322             my $request = shift;
323              
324             # TODO
325              
326             return "Unimplemented";
327             }
328              
329             sub get_test_request {
330             my $self = shift;
331             my $id = shift;
332              
333             # TODO
334              
335             return "Unimplemented";
336             }
337              
338             ################
339             # Eventually, everything after this line should be moved into sub modules
340              
341             =head2 get_tests()
342              
343             Returns a list of tests in the system. Each test object will include
344             several fields:
345              
346             id
347             descriptor
348             description
349             category
350             code_location
351             configuration_notes
352             status
353             environment_default
354             lilo_default
355             repeat_safe
356              
357             =cut
358              
359             sub get_tests {
360             my $self = shift;
361              
362             my $sql = qq|
363             SELECT uid as id, descriptor, description, category, code_location,
364             configuration_notes, status, environment_default, lilo_default,
365             repeat_safe
366             FROM test
367             |;
368             my $dbh = $self->_get_dbh() or return undef;
369             my $sth = $dbh->prepare($sql);
370             $sth->execute;
371              
372             my @tests = ();
373             while (my $test = $sth->fetchrow_hashref) {
374             push @tests, $test;
375             }
376              
377             return \@tests;
378             }
379              
380             =head2 get_hosts()
381              
382             Returns a list of host machines registered in the system.
383              
384             =cut
385              
386             sub get_hosts {
387             my $self = shift;
388              
389             my $sql = qq|
390             SELECT host.uid as id,
391             host.descriptor as host,
392             host_type.descriptor as host_type,
393             host_type.cpu as cpu,
394             host_type.ram_qty as ram_qty,
395             host_type.storage_space as storage_space,
396             host_type.spindle_qty as spindle_qty,
397             host_type.eth100 as eth100,
398             host_type.eth1000 as eth1000,
399             host_state.descriptor as host_state,
400             host_state.available as available,
401             host_state.schedulable as schedulable
402             FROM host, host_type, host_state
403             WHERE host.host_type_uid = host_type.uid
404             AND host.host_state_uid = host_state.uid
405             ORDER BY host.uid
406             |;
407              
408             my $dbh = $self->_get_dbh() or return undef;
409             my $sth = $dbh->prepare($sql);
410             $sth->execute;
411              
412             my @hosts = ();
413             while (my $host = $sth->fetchrow_hashref) {
414             push @hosts, $host;
415             }
416              
417             return \@hosts;
418             }
419              
420             =head2 get_images()
421              
422             This routine returns a list of distro images that are available in
423             the system. Each image record includes its descriptor, id, and status.
424              
425             =cut
426              
427             sub get_images {
428             my $self = shift;
429              
430             my $sql = qq|
431             SELECT uid as id, descriptor, status
432             FROM distro_tag
433             WHERE status='Available'
434             |;
435             my $dbh = $self->_get_dbh() or return undef;
436             my $sth = $dbh->prepare($sql);
437             $sth->execute;
438              
439             my @images = ();
440             while (my $image = $sth->fetchrow_hashref) {
441             push @images, $image;
442             }
443              
444             return \@images;
445             }
446              
447             =head2 get_software_types()
448              
449             Returns a list of software packages available in the system for doing
450             testing against.
451              
452             =cut
453              
454             sub get_software_types {
455             my $self = shift;
456              
457             my $sql = qq|
458             SELECT DISTINCT software_type
459             FROM patch_tag
460             |;
461             my $dbh = $self->_get_dbh() or return undef;
462             my $sth = $dbh->prepare($sql);
463             $sth->execute;
464              
465             my @packages = ();
466             while (my $package = $sth->fetchrow_hashref) {
467             push @packages, $package;
468             }
469              
470             return \@packages;
471             }
472              
473             =head2 get_requests(%args)
474              
475             This routine permits searching against the test requests in the system.
476             Arguments can be provided via the %args hash. Accepted arguments
477             include:
478              
479             limit - the number of records to return
480             order_by - the fieldname to order the records by
481              
482             distro - search condition (supports % wildcards)
483             test - search condition (supports % wildcards)
484             host - search condition (supports % wildcards)
485             host_type - search condition (supports % wildcards)
486             project - search condition (supports % wildcards)
487             priority - search condition (must match exactly)
488             status - search condition (must match exactly)
489             patch_id - search condition (must be a valid patch id number)
490             patch - search condition (must match a valid patch name)
491             created_by - user id number for requestor
492             username - username of requestor
493            
494              
495             Each test request record returned includes the following info:
496              
497             id - the test request's id
498             created_by - user id# of the requestor
499             username - username of the requestor
500             project - project associated with the request
501             status - the state the test request is currently in
502             priority - priority
503              
504             created_date - date it was created
505             started_date - datetime the test run began
506             completion_date - date it was completed
507              
508             distro - distro image name
509             test - test name
510             host - host name
511             host_type - host class
512             patch - patch name
513              
514             distro_tag_id - id# of distro image
515             test_id - id# of test
516             host_id - id# of host
517             host_type_id - id# of host type
518             project_id - id# of project
519             patch_tag_id - id# of patch
520              
521             =cut
522              
523             # TODO: I think this returns one row per patch_tag record...
524             # Perhaps it should return this info as a nested structure?
525             sub get_requests {
526             my ($self, %args) = @_;
527              
528             if ($self->{_debug} >1) {
529             while (my ($key, $value) = each %args) {
530             warn " '$key' = '$value'\n";
531             }
532             }
533              
534             # limit can only be between 0-1000 and must be a number.
535             my $limit = $args{limit} || 20;
536             if ($limit !~ /^\d+$/ || $limit > 1000) {
537             $self->set_error("Invalid limit '$limit'. ".
538             "Must be a number in the range 0-1000.");
539             return undef;
540             } else {
541             delete $args{limit};
542             }
543              
544             # Order field must be alphanumeric
545             my $order_by = $args{order_by} || 'test_request.uid';
546             if ($order_by !~ /^[\.\w]+$/) {
547             $self->_set_error("Invalid order_by field '$order_by'. ".
548             "Must be an alphanumeric field name.");
549             return undef;
550             } else {
551             delete $args{order_by};
552             }
553              
554             # Rest of the arguments can only be alphanumeric values
555             foreach my $key (keys %args) {
556             if ($key !~ m/^\w+$/) {
557             my $err = "Invalid key '$key' specified. ".
558             "Only alphanumeric characters may be used.";
559             warn "Error: $err\n" if ($self->{_debug} > 1);
560             $self->_set_error($err);
561             return undef;
562             } elsif ($args{$key} !~ m/^\w+$/) {
563             my $err = "Invalid value '$args{'$key'}' specified for '$key'. "
564             ."Only alphanumeric characters may be used.";
565             $self->_set_error($err);
566             warn "Error: $err\n" if ($self->{_debug} > 1);
567             return undef;
568             }
569             }
570              
571             my $sql = qq|
572             SELECT
573             test_request.uid AS id,
574             test_request.created_by AS created_by,
575             DATE_FORMAT(test_request.created_date, '%Y-%m-%d') AS created_date,
576             test_request.status AS status,
577             DATE_FORMAT(test_request.completion_date, '%Y-%m-%d') AS completion_date,
578             test_request.test_priority AS priority,
579             test_request.started_date AS started_date,
580              
581             test_request.distro_tag_uid AS distro_tag_id,
582             test_request.test_uid AS test_id,
583             test_request.host_uid AS host_id,
584             test_request.host_type_uid AS host_type_id,
585             test_request.project_uid AS project_id,
586              
587             distro_tag.descriptor AS distro,
588             test.descriptor AS test,
589             host.descriptor AS host,
590             host_type.descriptor AS host_type,
591             EIDETIC.user.descriptor AS username,
592             EIDETIC.project.descriptor AS project,
593              
594             test_request_to_patch_tag.patch_tag_uid AS patch_tag_id,
595             patch_tag.descriptor AS patch
596             FROM
597             test_request,
598             distro_tag,
599             test,
600             host,
601             host_type,
602             test_request_to_patch_tag,
603             patch_tag,
604             EIDETIC.user,
605             EIDETIC.project
606             WHERE 1
607             AND test_request.distro_tag_uid = distro_tag.uid
608             AND test_request.test_uid = test.uid
609             AND (test_request.host_uid = host.uid OR (test_request.host_uid=0 AND host.uid=1))
610             AND test_request.host_type_uid = host_type.uid
611             AND test_request.project_uid = EIDETIC.project.uid
612             AND test_request.uid = test_request_to_patch_tag.test_request_uid
613             AND test_request_to_patch_tag.patch_tag_uid = patch_tag.uid
614             AND test_request.created_by = EIDETIC.user.uid
615             |;
616              
617             if (defined $args{'distro'}) {
618             $sql .= qq| AND distro_tag.descriptor LIKE "$args{'distro'}"\n|;
619             }
620             if (defined $args{'test'}) {
621             $sql .= qq| AND test.descriptor LIKE "$args{'test'}"\n|;
622             }
623             if (defined $args{'host'}) {
624             $sql .= qq| AND host.descriptor LIKE "$args{'host'}"\n|;
625             }
626             if (defined $args{'host_type'}) {
627             $sql .= qq| AND host.descriptor LIKE "$args{'host_type'}"\n|;
628             }
629             if (defined $args{'project'}) {
630             $sql .= qq| AND EIDETIC.project.descriptor LIKE "$args{'project'}"\n|;
631             }
632             if (defined $args{'priority'}) {
633             $sql .= qq| AND test_request.test_priority = $args{'priority'}\n|;
634             }
635             if (defined $args{'status'}) {
636             $sql .= qq| AND test_request.status = "$args{'status'}"\n|;
637             }
638             if (defined $args{'patch_id'}) {
639             if ($args{'patch_id'} !~ m/^\d+$/) {
640             $self->_set_error("Invalid patch ID '$args{'patch_id'}' specified. ".
641             "Must be a positive integer.");
642             return undef;
643             }
644             $sql .= qq| AND test_request_to_patch_tag.patch_tag_uid = $args{'patch_id'}\n|;
645             }
646             if (defined $args{'patch'}) {
647             $sql .= qq| AND patch_tag.descriptor LIKE '$args{'patch'}'|;
648             }
649             if (defined $args{'created_by'}) {
650             if ($args{'created_by'} !~ m/^\d+$/) {
651             $self->_set_error("Invalid created_by ID '$args{'created_by'}'. ".
652             "Must be a positive integer.");
653             return undef;
654             }
655             $sql .= qq| AND test_request.created_by=$args{'created_by'}\n|;
656             }
657             if (defined $args{'username'}) {
658             $sql .= qq| AND EIDETIC.user.descriptor LIKE "$args{'username'}"\n|;
659             }
660              
661             $sql .= qq|ORDER BY $order_by DESC\n|;
662             $sql .= qq|LIMIT $limit\n|;
663              
664             warn "sql = '$sql'\n" if ($self->{_debug} > 2);
665              
666             my $dbh = $self->_get_dbh() or return undef;
667             my $sth = $dbh->prepare($sql);
668             $sth->execute;
669              
670             my @test_requests = ();
671             while (my $tr = $sth->fetchrow_hashref) {
672             push @test_requests, $tr;
673             }
674              
675             return \@test_requests;
676             }
677              
678             =head2 get_request_queue([$host])
679              
680             Returns a list of queued tests for a given host name or id, or all hosts
681             if $host is not defined.
682              
683             =cut
684              
685             sub get_request_queue {
686             my $self = shift;
687             my $host = shift;
688              
689             my $sql = qq|
690             SELECT test_request.uid as id,
691             patch_tag.descriptor as patch,
692             test_request.status,
693             host_type.descriptor as host_type,
694             test_request.created_date
695             FROM test_request, patch_tag, test_request_to_patch_tag, host_type
696             WHERE test_request.status = 'Queued'
697             AND test_request.uid = test_request_to_patch_tag.test_request_uid
698             AND test_request_to_patch_tag.patch_tag_uid = patch_tag.uid
699             AND test_request.host_type_uid = host_type.uid
700             |;
701             if ($host) {
702             if ($host =~ /^\d+$/) {
703             $sql .= " AND host_type.uid = $host\n";
704             } else {
705             $sql .= " AND host_type.descriptor = '$host'\n";
706             }
707             }
708             warn "sql = '$sql'\n" if ($self->{_debug} > 2);
709              
710             my $dbh = $self->_get_dbh() or return undef;
711             my $sth = $dbh->prepare($sql);
712             $sth->execute;
713              
714             my @queue = ();
715             while (my $tr = $sth->fetchrow_hashref) {
716             push @queue, $tr;
717             }
718              
719             return \@queue;
720             }
721              
722              
723             =head2 get_patches($patch_regex[, $limit])
724              
725             Returns a list of patches in the system matching the given regular
726             expression, up to $limit (default 100) items.
727              
728             =cut
729             sub get_patches {
730             my $self = shift;
731             my $patch_regex = shift;
732             my $limit = shift || 100;
733              
734             my $sql = qq|
735             SELECT uid as id, descriptor, software_type, autotest_state
736             FROM patch_tag
737             LIMIT $limit
738             ORDER BY descriptor
739             |;
740             my $dbh = $self->_get_dbh() or return undef;
741             my $sth = $dbh->prepare($sql);
742             $sth->execute;
743              
744             my @patches = ();
745             while (my $patch = $sth->fetchrow_hashref) {
746             push @patches, $patch;
747             }
748              
749             return \@patches;
750             }
751              
752             =head2 add_test($name, \%properties)
753              
754             Adds a new test to the system. This includes all test descriptions,
755             parameter information, default settings, etc.
756              
757             If the user is a maintainer for this test, allows update directly,
758             otherwise sends an email request to the system admins.
759              
760             =cut
761             sub add_test {
762             my $self = shift;
763             my $name = shift;
764             my $properties = shift;
765              
766             # Assumptions:
767             # * Test code has already been inserted into bitkeeper
768             # * Test code has been tagged stp_deploy, etc. as per web directions
769              
770             # Data structure:
771             # test_name
772             # description
773             # lilo_default
774             # code_location
775             # configuration_notes
776             # environment_default
777             # category
778             # status
779             # repeat_safe
780             # test_parameters (array of hashrefs):
781             # + descriptor
782             # + description
783             # + data_type ('string' or 'int')
784             # distros (array of distro id's)
785             # host_types (array of host_type id's)
786             # software_types (array of software strings 'linux', 'postgresql', etc.)
787              
788             # Algorithm:
789             #
790             # * Exit if the test name already exists in the database
791             # * Validate information in $properties
792             # + descriptor must be alphanumeric (no default - error if not provided)
793             # + lilo_default (default '')
794             # + environment_default (default '')
795             # + category must be alphanumeric (default General)
796             # + status must be either 'Available' or 'Unavailable' (default Available)
797             # + repeat_safe must be either 0 or 1 (default 0)
798             # + Validate each test parameter
799             # + Validate distro list - they must exist in the table
800             # + Validate host_type's - they must exist in the table
801             # + Validate software_types - they must be alphanumeric
802             # * Invoke SQL call to insert the information into the test table
803             # + insert it with status='Inserting'
804             # insert into test (
805             # rsf,descriptor,created_by,created_date,description,code_location,category,status,repeat_safe ) VALUES ( 1, 'lhms-regression', 3125, now(),'Linux Hotplug Memory Support Regression Test', 'bk://developer.osdl.org/stp-test/lhms-regression', 'General', 'Available', 0 );
806             # + retrieve the test_uid just inserted
807             # * For each parameter, insert into parameters test_parameter
808             # + test_uid
809             # + descriptor
810             # + description
811             # + data_type ('string' or 'int')
812            
813             # * Add test into test_to_distro_tag
814             # distro_tag_uid, test_uid
815             # insert into test_to_distro_tag ( rsf, distro_tag_uid, test_uid ) VALUES ( 1, 4, 87 );
816             # * Add test into test_to_host_type
817             # host_type_uid, test_uid
818             # insert into test_to_host_type ( host_type_uid, test_uid, rsf ) VALUES ( 81, 87, 1 );
819             # * Add test into test_to_software:
820             # test_uid, software_type, install_priority
821             # > INSERT into test_to_software ( rsf, test_uid, software_type, install_priority) VALUES ( 1, 87, 'linux', 0 );
822             # > INSERT into test_to_software ( rsf, test_uid, software_type, install_priority ) VALUES ( 1, 87, 'sysstat', 0 );
823             # * Update test record and change status to 'Available' or 'Unavailable'
824             # as appropriate
825              
826              
827             # On failure, back out the test insertion
828              
829             return "Not implemented\n";
830             }
831              
832             =head2 get_test($name)
833              
834             Returns properties for the given test (including a URL where the test
835             code can be fetched.
836              
837             =cut
838             sub get_test {
839             my $self = shift;
840             my $name = shift;
841              
842             return "Not implemented\n";
843             }
844              
845             =head2 update_test($name, \%properties)
846              
847             Updates the info about the given test. %properties should contain the
848             list of values to update. Properties to leave alone should be undef.
849              
850             If the user is a maintainer for this test, allows update directly,
851             otherwise sends an email request to the system admins.
852              
853             =cut
854             sub update_test {
855             my $self = shift;
856             my $name = shift;
857             my $properties = shift;
858              
859             return "Not implemented\n";
860             }
861              
862              
863             #### These API routines need implemented
864              
865             =head2 activate_host($host_id)
866              
867             NOT YET IMPLEMENTED
868              
869             Activates the given host, if it is in maintenance mode. This routine
870             can only be called by someone with administrator priv's.
871              
872             =cut
873              
874             sub activate_host {
875             my $self = shift;
876             my $host_id = shift;
877              
878             return "Not implemented\n";
879             }
880              
881             =head2 checkout_host(\%host_criteria, \%notification, \%preparation)
882              
883             NOT YET IMPLEMENTED
884              
885             Requests a machine be 'checked out', as indicated by the %host_criteria
886             hash. This hash supports the following fields:
887              
888             id - a regular expression that will resolve to one or more
889             host id's
890             type - a regular expression that resolves to a valid set of
891             host_type's
892              
893             The above criteria are ANDed together, so only hosts that matches
894             ALL of the criteria will be selected.
895              
896             If more than one host matches the criteria, then the first available
897             system will be checked out. If multiple machines need to be checked
898             out, call this routine that many times.
899              
900             The %notification hash provides instructions regarding how the user
901             should be notified about when the host becomes available. It supports
902             the following fields:
903              
904             email - an email address to send an email to when the system becomes
905             available.
906             on_state_change - if set to true, will notify user of ALL changes,
907             not just availability.
908              
909             If no notification info is provided (the %notification hash is left
910             undefined or empty), then no notification will be performed, and it
911             will be up to the requestor to check back periodically to determine
912             when the machine is available, via get_hosts().
913              
914             When a machine is checked out, it is put on a time-out. After the time
915             has expired, the machine will automatically return to the queue. This
916             way if someone checks out a machine but isn't around to use it when it
917             becomes available, it won't sit idly checked out forever.
918              
919             The %preparation hash allows the user to specify additional custom setup
920             work that should be completed on the machine prior to marking the
921             machine 'available' and notifying the user. This could include waiting
922             for another machine checkout to complete, installing some user-specific
923             tools, initiating some instrumentation, etc.
924              
925             =cut
926              
927             sub checkout_host {
928             my $self = shift;
929             my $host_criteria = shift;
930             my $notification = shift;
931             my $preparation = shift;
932              
933             # Use host_criteria to find matching set of systems
934             # Store request for checking out those systems
935             # TODO: Need separate routine for reviewing checkout requests
936             # Set a time-out for when to return machine to queue
937              
938             return "Not implemented\n";
939             }
940              
941             =head2 change_host_reservation($host_id, $timeout)
942              
943             NOT YET IMPLEMENTED
944              
945             Allows altering the reservation time for a given host. This allows
946             extending your checkout request beyond the default, or even to check
947             a machine back in.
948              
949             $timeout can be a period of time ("120 min"), or a cut-off time (6:00 pm
950             Friday). To check a machine in or cancel the reservation, pass a zero
951             value for $timeout. Invalid timeouts (negative times, non-time strings,
952             dates in the past, etc.) result in an error.
953              
954             Those with admin privs can check out machines for any length of time.
955             Regular users will be limited as to the maximum reservation times
956             they're allowed.
957              
958             =cut
959              
960             sub change_host_reservation {
961             my $self = shift;
962             my $host_id = shift;
963             my $timeout = shift;
964              
965             # TODO
966             return "Not implemented\n";
967             }
968              
969             =head2 add_software_type($type, \%properties)
970              
971             Registers new software for the testing system to track. This will cause
972             the system to periodically check for new releases or snapshots of the
973             code for running tests against. This allows automating the testing
974             process, so that certain tests can be run regularly against the code.
975              
976             The frequency may be limited by the administrator as appropriate to the
977             resource availability.
978              
979             =cut
980              
981             sub add_software_type {
982             my $self = shift;
983             my $type = shift;
984              
985             # TODO
986             return "Not implemented\n";
987             }
988              
989             =head2 update_software_type($type, \%properties)
990              
991             Updates information about the given software type
992              
993             =cut
994              
995             sub update_software {
996             my $self = shift;
997             my $type = shift;
998             my $properties = shift;
999              
1000             # TODO
1001             return "Not implemented\n";
1002             }
1003              
1004             1;
1005              
1006              
1007