4 use vars qw( @ISA @EXPORT_OK $DEBUG $conf $jobnums);
7 use Storable qw( nfreeze thaw );
8 use FS::UID qw(myconnect);
10 use FS::Record qw( qsearch qsearchs dbh );
16 @ISA = qw(FS::Record);
17 @EXPORT_OK = qw( joblisting );
21 $FS::UID::callback{'FS::queue'} = sub {
29 FS::queue - Object methods for queue records
35 $record = new FS::queue \%hash;
36 $record = new FS::queue { 'column' => 'value' };
38 $error = $record->insert;
40 $error = $new_record->replace($old_record);
42 $error = $record->delete;
44 $error = $record->check;
48 An FS::queue object represents an queued job. FS::queue inherits from
49 FS::Record. The following fields are currently supported:
59 Fully-qualified subroutine name
63 Job status (new, locked, or failed)
67 Freeform text status message
75 Optional link to service (see L<FS::cust_svc>).
79 Optional link to customer (see L<FS::cust_main>).
83 Secure flag, 'Y' indicates that when using encryption, the job needs to be
84 run on a machine with the private key.
96 Creates a new job. To add the job to the database, see L<"insert">.
98 Note that this stores the hash reference, not a distinct copy of the hash it
99 points to. You can ask the object for a copy with the I<hash> method.
103 # the new method can be inherited from FS::Record, if a table method is defined
105 sub table { 'queue'; }
107 =item insert [ ARGUMENT, ARGUMENT... ]
109 Adds this record to the database. If there is an error, returns the error,
110 otherwise returns false.
112 If any arguments are supplied, a queue_arg record for each argument is also
113 created (see L<FS::queue_arg>).
117 #false laziness w/part_export.pm
119 my( $self, @args ) = @_;
121 local $SIG{HUP} = 'IGNORE';
122 local $SIG{INT} = 'IGNORE';
123 local $SIG{QUIT} = 'IGNORE';
124 local $SIG{TERM} = 'IGNORE';
125 local $SIG{TSTP} = 'IGNORE';
126 local $SIG{PIPE} = 'IGNORE';
128 my $oldAutoCommit = $FS::UID::AutoCommit;
129 local $FS::UID::AutoCommit = 0;
138 $self->custnum( $args{'custnum'} ) if $args{'custnum'};
140 my $error = $self->SUPER::insert;
142 $dbh->rollback if $oldAutoCommit;
146 foreach my $arg ( @args ) {
147 my $freeze = ref($arg) ? 'Y' : '';
148 my $queue_arg = new FS::queue_arg ( {
149 'jobnum' => $self->jobnum,
151 'arg' => $freeze ? encode_base64(nfreeze($arg)) : $arg,# always freeze?
153 $error = $queue_arg->insert;
155 $dbh->rollback if $oldAutoCommit;
161 warn "jobnums global is active: $jobnums\n" if $DEBUG;
162 push @$jobnums, $self->jobnum;
165 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
173 Delete this record from the database. Any corresponding queue_arg records are
181 local $SIG{HUP} = 'IGNORE';
182 local $SIG{INT} = 'IGNORE';
183 local $SIG{QUIT} = 'IGNORE';
184 local $SIG{TERM} = 'IGNORE';
185 local $SIG{TSTP} = 'IGNORE';
186 local $SIG{PIPE} = 'IGNORE';
188 my $oldAutoCommit = $FS::UID::AutoCommit;
189 local $FS::UID::AutoCommit = 0;
192 my @del = qsearch( 'queue_arg', { 'jobnum' => $self->jobnum } );
193 push @del, qsearch( 'queue_depend', { 'depend_jobnum' => $self->jobnum } );
195 my $error = $self->SUPER::delete;
197 $dbh->rollback if $oldAutoCommit;
201 foreach my $del ( @del ) {
202 $error = $del->delete;
204 $dbh->rollback if $oldAutoCommit;
209 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
215 =item replace OLD_RECORD
217 Replaces the OLD_RECORD with this one in the database. If there is an error,
218 returns the error, otherwise returns false.
222 # the replace method can be inherited from FS::Record
226 Checks all fields to make sure this is a valid job. If there is
227 an error, returns the error, otherwise returns false. Called by the insert
235 $self->ut_numbern('jobnum')
236 || $self->ut_anything('job')
237 || $self->ut_numbern('_date')
238 || $self->ut_enum('status',['', qw( new locked failed )])
239 || $self->ut_anything('statustext')
240 || $self->ut_numbern('svcnum')
242 return $error if $error;
244 $error = $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum');
245 $self->svcnum('') if $error;
247 $self->status('new') unless $self->status;
248 $self->_date(time) unless $self->_date;
255 Returns a list of the arguments associated with this job.
261 map { $_->frozen ? thaw(decode_base64($_->arg)) : $_->arg }
262 qsearch( 'queue_arg',
263 { 'jobnum' => $self->jobnum },
271 Returns the FS::cust_svc object associated with this job, if any.
277 qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
282 Returns the FS::queue_depend objects associated with this job, if any.
283 (Dependancies that must complete before this job can be run).
289 qsearch('queue_depend', { 'jobnum' => $self->jobnum } );
292 =item depend_insert OTHER_JOBNUM
294 Inserts a dependancy for this job - it will not be run until the other job
295 specified completes. If there is an error, returns the error, otherwise
298 When using job dependancies, you should wrap the insertion of all relevant jobs
299 in a database transaction.
304 my($self, $other_jobnum) = @_;
305 my $queue_depend = new FS::queue_depend ( {
306 'jobnum' => $self->jobnum,
307 'depend_jobnum' => $other_jobnum,
309 $queue_depend->insert;
314 Returns the FS::queue_depend objects that associate other jobs with this job,
315 if any. (The jobs that are waiting for this job to complete before they can
322 qsearch('queue_depend', { 'depend_jobnum' => $self->jobnum } );
325 =item depended_delete
327 Deletes the other queued jobs (FS::queue objects) that are waiting for this
328 job, if any. If there is an error, returns the error, otherwise returns false.
332 sub depended_delete {
336 map { qsearchs('queue', { 'jobnum' => $_->jobnum } ) } $self->queue_depended
338 $error = $job->depended_delete;
339 return $error if $error;
340 $error = $job->delete;
341 return $error if $error
345 =item update_statustext VALUE
347 Updates the statustext value of this job to supplied value, in the database.
348 If there is an error, returns the error, otherwise returns false.
352 use vars qw($_update_statustext_dbh);
353 sub update_statustext {
354 my( $self, $statustext ) = @_;
355 return '' if $statustext eq $self->statustext;
356 warn "updating statustext for $self to $statustext" if $DEBUG;
358 $_update_statustext_dbh ||= myconnect;
360 my $sth = $_update_statustext_dbh->prepare(
361 'UPDATE queue set statustext = ? WHERE jobnum = ?'
362 ) or return $_update_statustext_dbh->errstr;
364 $sth->execute($statustext, $self->jobnum) or return $sth->errstr;
365 $_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
366 $self->statustext($statustext);
369 #my $new = new FS::queue { $self->hash };
370 #$new->statustext($statustext);
371 #my $error = $new->replace($self);
372 #return $error if $error;
373 #$self->statustext($statustext);
383 =item joblisting HASHREF NOACTIONS
388 my($hashref, $noactions) = @_;
394 my @queue = qsearch( 'queue', $hashref );
395 return '' unless scalar(@queue);
397 my $p = FS::CGI::popurl(2);
399 my $html = qq!<FORM ACTION="$p/misc/queue.cgi" METHOD="POST">!.
400 FS::CGI::table(). <<END;
402 <TH COLSPAN=2>Job</TH>
407 $html .= '<TH>Account</TH>' unless $hashref->{svcnum};
410 my $dangerous = $conf->exists('queue_dangerous_controls');
414 foreach my $queue ( sort {
415 $a->getfield('jobnum') <=> $b->getfield('jobnum')
417 my $queue_hashref = $queue->hashref;
418 my $jobnum = $queue->jobnum;
421 if ( $dangerous || $queue->job !~ /^FS::part_export::/ || !$noactions ) {
422 $args = encode_entities( join(' ', $queue->args) );
427 my $date = time2str( "%a %b %e %T %Y", $queue->_date );
428 my $status = $queue->status;
429 $status .= ': '. $queue->statustext if $queue->statustext;
430 my @queue_depend = $queue->queue_depend;
431 $status .= ' (waiting for '.
432 join(', ', map { $_->depend_jobnum } @queue_depend ).
435 my $changable = $dangerous
436 || ( ! $noactions && $status =~ /^failed/ || $status =~ /^locked/ );
439 qq! ( <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A> |!.
440 qq! <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A> )!;
442 my $cust_svc = $queue->cust_svc;
447 <TD>$queue_hashref->{job}</TD>
453 unless ( $hashref->{svcnum} ) {
456 my $table = $cust_svc->part_svc->svcdb;
457 my $label = ( $cust_svc->label )[1];
458 $account = qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
463 $html .= "<TD>$account</TD>";
469 qq!<TD><INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1"></TD>!;
480 $html .= '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
481 '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>';
496 L<FS::Record>, schema.html from the base documentation.