the master control program has chosen YOU to serve your system on the game grid
authorivan <ivan>
Fri, 22 Aug 2008 03:01:06 +0000 (03:01 +0000)
committerivan <ivan>
Fri, 22 Aug 2008 03:01:06 +0000 (03:01 +0000)
FS/FS/Conf.pm
FS/FS/Mason.pm
FS/FS/Schema.pm
FS/FS/Tron.pm [new file with mode: 0644]
FS/FS/cust_svc.pm
FS/FS/cust_svc_option.pm [new file with mode: 0644]
FS/MANIFEST
FS/t/cust_svc_option.t [new file with mode: 0644]
bin/tron-scan [new file with mode: 0755]
httemplate/elements/dashboard-toplist.html
httemplate/elements/mcp_lint.html [new file with mode: 0644]

index 728a213..08b299f 100644 (file)
@@ -2357,6 +2357,13 @@ worry that config_items is freeside-specific and icky.
     'type'        => 'text',
   },
 
+  {
+    'key'         => 'mcp_svcpart',
+    'section'     => '',
+    'description' => 'Master Control Program svcpart.  Leave this blank.',
+    'type'        => 'text',
+  },
+
 );
 
 1;
index 219f6b7..77ff668 100644 (file)
@@ -93,6 +93,7 @@ Initializes the Mason environment, loads all Freeside and RT libraries, etc.
   use FS::Misc qw( send_email send_fax states_hash counties state_label );
   use FS::Report::Table::Monthly;
   use FS::TicketSystem;
+  use FS::Tron qw( tron_lint );
 
   use FS::agent;
   use FS::agent_type;
index e461fc1..86cf4a3 100644 (file)
@@ -1003,6 +1003,18 @@ sub tables_hashref {
       'index' => [ ['svcnum'], ['pkgnum'], ['svcpart'] ],
     },
 
+    'cust_svc_option' => {
+      'columns' => [
+        'optionnum',   'serial', '', '', '', '', 
+        'svcnum',      'int', '', '', '', '', 
+        'optionname',  'varchar', '', $char_d, '', '', 
+        'optionvalue', 'text', 'NULL', '', '', '', 
+      ],
+      'primary_key' => 'optionnum',
+      'unique'      => [],
+      'index'       => [ [ 'svcnum' ], [ 'optionname' ] ],
+    },
+
     'part_pkg' => {
       'columns' => [
         'pkgpart',       'serial',    '',   '', '', '', 
diff --git a/FS/FS/Tron.pm b/FS/FS/Tron.pm
new file mode 100644 (file)
index 0000000..3328095
--- /dev/null
@@ -0,0 +1,93 @@
+package FS::Tron;
+# a program to monitor outside systems
+
+use strict;
+use warnings;
+use base 'Exporter';
+use Net::SSH qw( sshopen2 ); #sshopen3 );
+use FS::Record qw( qsearchs );
+use FS::svc_external;
+use FS::cust_svc_option;
+
+our @EXPORT_OK = qw( tron_scan tron_lint);
+
+our %desired = (
+  #lenient for now, so we can fix up important stuff
+  'freeside_version' => qr/^1\.(7\.3|9\.0)/,
+  'debian_version'   => qr/^4/,
+  'apache_mpm'       => qw/^(Prefork|$)/,
+
+  #stuff to add/replace later
+  #'pg_version'       => qr/^8\.[1-9]/,
+  #'apache_version'   => qr/^2/,
+  #'apache_mpm'       => qw/^Prefork/,
+);
+
+sub tron_scan {
+  my $cust_svc = shift;
+
+  my $svc_external;
+  if ( ref($cust_svc) ) {
+    $svc_external = $cust_svc->svc_x;
+  } else {
+    $svc_external = qsearchs('svc_external', { 'svcnum' => $cust_svc } );
+    $cust_svc = $svc_external->cust_svc;
+  }
+
+  #don't scan again if things are okay
+  my $bad = 0;
+  foreach my $option ( keys %desired ) {
+    my $current = $cust_svc->option($option);
+    $bad++ unless $current =~ $desired{$option};
+  }
+  return '' unless $bad;
+
+  #do the scan
+  my %hash = ();
+  my $machine = $svc_external->title; # or better as a cust_svc_option??
+  sshopen2($machine, *READER, *WRITER, '/usr/local/bin/freeside-yori all');
+  while (<READER>) {
+    chomp;
+    my($option, $value) = split(/: ?/);
+    next unless defined($option) && exists($desired{$option});
+    $hash{$option} = $value;
+  }
+  close READER;
+
+  unless ( keys %hash ) {
+    return "error scanning $machine\n";
+  }
+
+  # store the results
+  foreach my $option ( keys %hash ) {
+    my %opthash = ( 'optionname' => $option,
+                    'svcnum'     => $cust_svc->svcnum,
+                  );
+    my $cust_svc_option =  qsearchs('cust_svc_option', \%opthash )
+                          || new FS::cust_svc_option   \%opthash;
+    next if $cust_svc_option->optionvalue eq $hash{$option};
+    $cust_svc_option->optionvalue( $hash{$option} );
+    my $error = $cust_svc_option->optionnum
+                  ? $cust_svc_option->replace
+                  : $cust_svc_option->insert;
+    return $error if $error;
+  }
+  
+  '';
+
+}
+
+sub tron_lint {
+  my $cust_svc = shift;
+
+  my @lint;
+  foreach my $option ( keys %desired ) {
+    my $current = $cust_svc->option($option);
+    push @lint, "$option is $current" unless $current =~ $desired{$option};
+  }
+
+  @lint;
+
+}
+
+1;
index 381b97f..2d6224c 100644 (file)
@@ -3,6 +3,7 @@ package FS::cust_svc;
 use strict;
 use vars qw( @ISA $DEBUG $me $ignore_quantity );
 use Carp;
+#use Scalar::Util qw( blessed );
 use FS::Conf;
 use FS::Record qw( qsearch qsearchs dbh str2time_sql );
 use FS::cust_pkg;
@@ -16,7 +17,7 @@ use FS::cdr;
 #most FS::svc_ classes are autoloaded in svc_x emthod
 use FS::svc_acct;  #this one is used in the cache stuff
 
-@ISA = qw( FS::cust_main_Mixin FS::Record );
+@ISA = qw( FS::cust_main_Mixin FS::option_Common ); #FS::Record );
 
 $DEBUG = 0;
 $me = '[cust_svc]';
@@ -220,7 +221,13 @@ returns the error, otherwise returns false.
 =cut
 
 sub replace {
+#  my $new = shift;
+#
+#  my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+#              ? shift
+#              : $new->replace_old;
   my ( $new, $old ) = ( shift, shift );
+  $old = $new->replace_old unless defined($old);
 
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
@@ -233,8 +240,6 @@ sub replace {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  $old = $new->replace_old unless defined($old);
-  
   if ( $new->svcpart != $old->svcpart ) {
     my $svc_x = $new->svc_x;
     my $new_svc_x = ref($svc_x)->new({$svc_x->hash, svcpart=>$new->svcpart });
@@ -246,6 +251,7 @@ sub replace {
     }
   }
 
+  #my $error = $new->SUPER::replace($old, @_);
   my $error = $new->SUPER::replace($old);
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
diff --git a/FS/FS/cust_svc_option.pm b/FS/FS/cust_svc_option.pm
new file mode 100644 (file)
index 0000000..0a242d5
--- /dev/null
@@ -0,0 +1,136 @@
+package FS::cust_svc_option;
+
+use strict;
+use vars qw( @ISA );
+#use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_svc_option - Object methods for cust_svc_option records
+
+=head1 SYNOPSIS
+
+  use FS::cust_svc_option;
+
+  $record = new FS::cust_svc_option \%hash;
+  $record = new FS::cust_svc_option { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_svc_option object represents an customer service option.
+  FS::cust_svc_option inherits from FS::Record.  The following fields are
+ currently supported:
+
+=over 4
+
+=item optionnum
+
+primary key
+
+=item svcnum
+
+svcnum (see L<FS::cust_svc>)
+
+=item optionname
+
+Option Name
+
+=item optionvalue
+
+Option Value
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new option.  To add the option to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to.  You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_svc_option'; }
+
+=item insert
+
+Adds this record to the database.  If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database.  If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid option.  If there is
+an error, returns the error, otherwise returns false.  Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+  my $self = shift;
+
+  my $error = 
+    $self->ut_numbern('optionnum')
+    || $self->ut_foreign_key('svcnum', 'cust_svc', 'svcnum')
+    || $self->ut_alpha('optionname')
+    || $self->ut_anything('optionvalue')
+  ;
+  return $error if $error;
+
+  $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+The author forgot to customize this manpage.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
index d83bf1d..5dcd696 100644 (file)
@@ -418,3 +418,5 @@ t/pkg_category.t
 FS/phone_avail.pm
 t/phone_avail.t
 FS/Yori.pm
+FS/cust_svc_option.pm
+t/cust_svc_option.t
diff --git a/FS/t/cust_svc_option.t b/FS/t/cust_svc_option.t
new file mode 100644 (file)
index 0000000..eeaa170
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_svc_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/bin/tron-scan b/bin/tron-scan
new file mode 100755 (executable)
index 0000000..914d6d4
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+use FS::UID qw(adminsuidsetup);
+use FS::Conf;
+use FS::Record qw(qsearch);
+use FS::Tron qw(tron_scan tron_lint);
+use FS::cust_svc;
+
+adminsuidsetup shift;
+
+my $conf = new FS::Conf;
+my $mcp_svcpart = $conf->config('mcp_svcpart') or die "no mcp_svcpart";
+
+#tron_scan($_) foreach qsearch('cust_svc', { 'svcpart' => $mcp_svcpart } );
+foreach my $svc ( qsearch('cust_svc', { 'svcpart' => $mcp_svcpart } ) ) {
+  my $error = tron_scan($svc);
+  warn $error if $error;
+
+  my @lint = tron_lint($svc);
+  print $svc->svc_x->title. "\n". join('', map "  $_\n", @lint )
+    if @lint;
+}
+
+1;
index 7ee6f2d..d8cd7f3 100644 (file)
@@ -23,6 +23,9 @@
          <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
            <A HREF="view/cust_main.cgi?<% $custnum %>"><% $cust_main->name %></A>
          </TD>
+          <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+            <% include('/elements/mcp_lint.html', 'cust_main'=>$cust_main) %>
+          </TD>
          <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ALIGN="right">
            <FONT SIZE="-1"><A HREF="<% FS::TicketSystem->href_new_ticket($cust_main, join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list ) ) %>">(new ticket)</A></FONT>
          </TD>
@@ -55,7 +58,7 @@
 %   } elsif ( $line =~ /^\-\-+$/ ) { #divider
 %     
       <TR>
-        <TH CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 3 %>"></TH>
+        <TH CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 4 %>"></TH>
       </TR>
 
 %     next;
 %   } elsif ( $line =~ /^\s*$/ ) {
 
       <TR>
-        <TD CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 3 %>" BGCOLOR="<% $bgcolor %>">&nbsp;</TD>
+        <TD CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 4 %>" BGCOLOR="<% $bgcolor %>">&nbsp;</TD>
       </TR>
 
 %   } elsif ( $line =~ /^\S/ ) { #label line
 
       <TR>
         <TH CLASS="grid" BGCOLOR="#cccccc"><% $line %></TH>
+       <TH CLASS="grid" BGCOLOR="#cccccc">Lint</TH>
        <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
 %       foreach my $priority ( @custom_priorities, '' ) {
           <TH CLASS="grid" BGCOLOR="#cccccc">
@@ -81,7 +85,7 @@
 %   } else { #regular line
 
       <TR>
-        <TD CLASS="grid"  COLSPAN="<% scalar(@custom_priorities) + 3 %>" BGCOLOR="<% $bgcolor %>"><% $line %></TD>
+        <TD CLASS="grid"  COLSPAN="<% scalar(@custom_priorities) + 4 %>" BGCOLOR="<% $bgcolor %>"><% $line %></TD>
       </TR>
 
 %   }
diff --git a/httemplate/elements/mcp_lint.html b/httemplate/elements/mcp_lint.html
new file mode 100644 (file)
index 0000000..826549c
--- /dev/null
@@ -0,0 +1,37 @@
+% foreach my $lint (@lint) {
+  <FONT COLOR="#FF0000"><% $lint %></FONT><BR>
+% }
+
+<%init>
+
+my(%opt) = @_;
+
+my $conf = new FS::Conf;
+
+my @svc = ();
+if ( $opt{svc} ) {
+  @svc = ref($opt{svc}) ? @{ $opt{svc} } : ( $opt{svc} );
+} elsif ( $opt{cust_main} ) {
+  my $custnum = $opt{cust_main}->custnum;
+  @svc = qsearchs({
+           'table'     => 'cust_svc',
+           'addl_from' => ' LEFT JOIN cust_pkg  USING ( pkgnum ) '.
+                          ' LEFT JOIN cust_main USING ( custnum )',
+           'hashref'   => { 'svcpart' => $conf->config('mcp_svcpart') },
+           'extra_sql' => " AND custnum = $custnum ",
+         });
+} else {
+  die 'neither svc nor cust_main options passed to mcp_lint';
+}
+
+my @lint = ();
+foreach my $svc ( @svc ) {
+  my @svc_lint = tron_lint($svc);
+  if ( scalar(@svc) > 1 ) {
+    push @lint, map $svc->title.": $_", @svc_lint;
+  } else {
+    push @lint, @svc_lint;
+  }
+}
+
+</%init>