RT# 83450 - fixed rateplan export
[freeside.git] / FS / FS / part_export / prizm.pm
index 4383542..9964489 100644 (file)
@@ -1,32 +1,96 @@
 package FS::part_export::prizm;
 
-use vars qw(@ISA %info %options $DEBUG);
+use vars qw(@ISA %info %options $DEBUG $me);
 use Tie::IxHash;
-use FS::Record qw(fields);
+use FS::Record qw(fields dbh);
 use FS::part_export;
 
 @ISA = qw(FS::part_export);
-$DEBUG = 1;
+$DEBUG = 0;
+$me = '[' . __PACKAGE__ . ']';
 
 tie %options, 'Tie::IxHash',
   'url'      => { label => 'Northbound url', default=>'https://localhost:8443/prizm/nbi' },
   'user'     => { label => 'Northbound username', default=>'nbi' },
   'password' => { label => 'Password', default => '' },
   'ems'      => { label => 'Full EMS', type => 'checkbox' },
+  'always_bam' => { label => 'Always activate/suspend authentication', type => 'checkbox' },
+  'element_name_length' => { label => 'Size of siteName (best left blank)' },
 ;
 
+my $notes = <<'EOT';
+Real-time export of <b>svc_broadband</b>, <b>cust_pkg</b>, and <b>cust_main</b>
+record data to Motorola
+<a href="http://motorola.canopywireless.com/products/prizm/">Canopy Prizm
+software</a> via the Northbound interface.<br><br>
+
+Freeside will attempt to create an element in an existing network with the
+values provided in svc_broadband.  Of particular interest are
+<ul>
+  <li> mac address - used to identify the element
+  <li> vlan profile - an exact match for a vlan profiles defined in prizm
+  <li> ip address - defines the management ip address of the prizm element
+  <li> latitude - GPS latitude
+  <li> longitude - GPS longitude
+  <li> altitude - GPS altitude
+</ul>
+
+In addition freeside attempts to set the service plan name in prizm to the
+name of the package in which the service resides.
+
+The service is associated with a customer in prizm as well, and freeside
+will create the customer should none already exist with import id matching
+the freeside customer number.  The following fields are set.
+
+<ul>
+  <li> importId - the freeside customer number
+  <li> customerType - freeside
+  <li> customerName - the name associated with the freeside shipping address
+  <li> address1 - the shipping address
+  <li> address2
+  <li> city
+  <li> state
+  <li> zipCode
+  <li> country
+  <li> workPhone - the daytime phone number
+  <li> homePhone - the night phone number
+  <li> freesideId - the freeside customer number
+</ul>
+
+  Additionally set on the element are
+<ul>
+  <li> Site Name - The shipping name followed by the service broadband description field
+  <li> Site Location - the shipping address
+  <li> Site Contact - the daytime and night phone numbers
+</ul>
+
+Freeside provisions, suspends, and unsuspends elements BAM only unless the
+'Full EMS' checkbox is checked.<br><br>
+
+When freeside provisions an element the siteName is copied internally by
+prizm in such a manner that it is possible for the value to exceed the size
+of the column used in the prizm database.  Therefore freeside truncates
+by default this value to 50 characters.  It is thought that this
+column is the account_name column of the element_user_account table.  It
+may be possible to lift this limit by modifying the prizm database and
+setting a new appropriate value on this export.  This is untested and
+possibly harmful.
+
+EOT
+
 %info = (
-  'svc'      => 'svc_broadband',
-  'desc'     => 'Real-time export to Northbound Interface',
-  'options'  => \%options,
-  'nodomain' => 'Y',
-  'notes'    => 'These are notes.'
+  'svc'        => 'svc_broadband',
+  'desc'       => 'Real-time export to Northbound Interface',
+  'options'    => \%options,
+  'nodomain'   => 'Y',
+  'no_machine' => 1,
+  'notes'      => $notes,
 );
 
 sub prizm_command {
   my ($self,$namespace,$method) = (shift,shift,shift);
 
-  eval "use Net::Prizm qw(CustomerInfo PrizmElement);";
+  eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
   die $@ if $@;
 
   my $prizm = new Net::Prizm (
@@ -42,7 +106,7 @@ sub prizm_command {
 sub queued_prizm_command {  # subroutine
   my( $url, $user, $password, $namespace, $method, @args ) = @_;
 
-  eval "use Net::Prizm qw(CustomerInfo PrizmElement);";
+  eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
   die $@ if $@;
 
   my $prizm = new Net::Prizm (
@@ -63,6 +127,9 @@ sub queued_prizm_command {  # subroutine
 
 sub _export_insert {
   my( $self, $svc ) = ( shift, shift );
+  warn "$me: _export_insert called for export ". $self->exportnum.
+    " on service ". $svc->svcnum. "\n"
+    if $DEBUG;
 
   my $cust_main = $svc->cust_svc->cust_pkg->cust_main;
 
@@ -89,6 +156,7 @@ sub _export_insert {
   my $pcustomer;
   if ($err_or_som->result->[0]) {
     $pcustomer = $err_or_som->result->[0]->customerId;
+    warn "$me: found customer $pcustomer in prizm\n" if $DEBUG;
   }else{
     my $chashref = $cust_main->hashref;
     my $customerinfo = {
@@ -115,6 +183,7 @@ sub _export_insert {
       unless ref($err_or_som);
 
     $pcustomer = $err_or_som->result;
+    warn "$me: added customer $pcustomer to prizm\n" if $DEBUG;
   }
   warn "multiple prizm customers found for $cust_main->custnum"
     if scalar(@$pcustomer) > 1;
@@ -138,19 +207,50 @@ sub _export_insert {
 #    }
 #  }
 
+# here we cope with a problem of prizm failing to insert for reason
+# of duplicate mac addr, but doing so inconsistently... a race in prizm?
+
+  $self->prizm_command( 'CustomerIfService', 'removeElementFromCustomer',
+                        0,
+                        $cust_main->custnum,
+                        0,
+                        $svc->mac_addr,
+                      );
+
+  $err_or_som = $self->prizm_command( 'NetworkIfService', 'getPrizmElements',
+                                      [ 'MAC Address' ],
+                                      [ $svc->mac_addr ],
+                                      [ '=' ],
+                                    );
+  if ( ref($err_or_som) && $err_or_som->result->[0] ) { # ignore errors
+    $self->prizm_command( 'NetworkIfService', 'deleteElement',
+                          $err_or_som->result->[0],
+                          1,
+                        );
+  }
+# end of coping
+
+  my $performance_profile = $svc->performance_profile;
+  $performance_profile ||= $svc->cust_svc->cust_pkg->part_pkg->pkg;
+
+  my $element_name_length = 50;
+  $element_name_length = $1
+    if $self->option('element_name_length') =~ /^\s*(\d+)\s*$/;
   $err_or_som = $self->prizm_command('NetworkIfService', 'addProvisionedElement',
                                       $networkid,
                                       $svc->mac_addr,
-                                      $name, # we fix this below (bug in prizm?)
+                                      substr($name . " " . $svc->description,
+                                             0, $element_name_length),
                                       $location,
                                       $contact,
-                                      sprintf("%032X", $svc->authkey),
-                                      $svc->cust_svc->cust_pkg->part_pkg->pkg,
+                                      sprintf("%032X", $svc->authkey || 0),
+                                      $performance_profile,
                                       $svc->vlan_profile,
-                                      $self->option('ems') == 1,
+                                      ($self->option('ems') ? 1 : 0 ),
                                      );
   return $err_or_som
     unless ref($err_or_som);
+  warn "$me: added provisioned element to prizm\n" if $DEBUG;
 
   my (@names) = ('Management IP',
                  'GPS Latitude',
@@ -178,6 +278,7 @@ sub _export_insert {
                                     );
   return $err_or_som
     unless ref($err_or_som);
+  warn "$me: set element configuration\n" if $DEBUG;
 
   $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
                                      [ $element ],
@@ -187,25 +288,28 @@ sub _export_insert {
                                     );
   return $err_or_som
     unless ref($err_or_som);
+  warn "$me: set element vlan profile\n" if $DEBUG;
 
   $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
                                      [ $element ],
-                                     $svc->cust_svc->cust_pkg->part_pkg->pkg,
+                                     $performance_profile,
                                      0,
                                      1,
                                     );
   return $err_or_som
     unless ref($err_or_som);
+  warn "$me: set element configset (performance profile)\n" if $DEBUG;
 
   $err_or_som = $self->prizm_command('NetworkIfService',
                                      'activateNetworkElements',
                                      [ $element ],
                                      1,
-                                     $self->option('ems') == 1,
+                                     ( $self->option('ems') ? 1 : 0 ),
                                     );
 
   return $err_or_som
     unless ref($err_or_som);
+  warn "$me: activated element\n" if $DEBUG;
 
   $err_or_som = $self->prizm_command('CustomerIfService',
                                      'addElementToCustomer',
@@ -217,6 +321,7 @@ sub _export_insert {
 
   return $err_or_som
     unless ref($err_or_som);
+  warn "$me: added element to customer\n" if $DEBUG;
 
   '';
 }
@@ -224,6 +329,10 @@ sub _export_insert {
 sub _export_delete {
   my( $self, $svc ) = ( shift, shift );
 
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
   my $cust_pkg = $svc->cust_svc->cust_pkg;
 
   my $depend = [];
@@ -233,7 +342,7 @@ sub _export_delete {
       'svcnum' => $svc->svcnum,
       'job'    => 'FS::part_export::prizm::queued_prizm_command',
     };
-    $queue->insert(
+    my $error = $queue->insert(
       ( map { $self->option($_) }
             qw( url user password ) ),
       'CustomerIfService',
@@ -242,10 +351,27 @@ sub _export_delete {
       $cust_pkg->custnum,
       0,
       $svc->mac_addr,
-    ) && push @$depend, $queue->jobnum;
+    );
+
+    if ($error) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+
+    push @$depend, $queue->jobnum;
   }
 
-  $self->queue_statuschange('deleteElement', $depend, $svc, 1);
+  my $err_or_queue =
+    $self->queue_statuschange('deleteElement', $depend, $svc, 1);
+
+  unless (ref($err_or_queue)) {
+    $dbh->rollback if $oldAutoCommit;
+    return $err_or_queue;
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  '';
 }
 
 sub _export_replace {
@@ -308,20 +434,93 @@ sub _export_replace {
   return $err_or_som
     unless ref($err_or_som);
 
+  my $performance_profile = $new->performance_profile;
+  $performance_profile ||= $new->cust_svc->cust_pkg->part_pkg->pkg;
+
+  $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
+                                     [ $element ],
+                                     $performance_profile,
+                                     0,
+                                     1,
+                                    );
+  return $err_or_som
+    unless ref($err_or_som);
+
   '';
 
 }
 
 sub _export_suspend {
   my( $self, $svc ) = ( shift, shift );
-  my $ems = $self->option('ems') == 1;
-  $self->queue_statuschange('suspendNetworkElements', [], $svc, 1, $ems);
+  my $depend = [];
+  my $ems = $self->option('ems') ? 1 : 0;
+  my $err_or_queue = '';
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  $err_or_queue = 
+     $self->queue_statuschange('suspendNetworkElements', [], $svc, 1, $ems);
+  unless (ref($err_or_queue)) {
+    $dbh->rollback if $oldAutoCommit;
+    return $err_or_queue;
+  }
+  push @$depend, $err_or_queue->jobnum;
+
+  if ($ems && $self->option('always_bam')) {
+    $err_or_queue =
+      $self->queue_statuschange('suspendNetworkElements', $depend, $svc, 1, 0);
+    unless (ref($err_or_queue)) {
+      $dbh->rollback if $oldAutoCommit;
+      return $err_or_queue;
+    }
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  '';
 }
 
 sub _export_unsuspend {
   my( $self, $svc ) = ( shift, shift );
-  my $ems = $self->option('ems') == 1;
-  $self->queue_statuschange('activateNetworkElements', [], $svc, 1, $ems);
+  my $depend = [];
+  my $ems = $self->option('ems') ? 1 : 0;
+  my $err_or_queue = '';
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  if ($ems && $self->option('always_bam')) {
+    $err_or_queue =
+      $self->queue_statuschange('activateNetworkElements', [], $svc, 1, 0);
+    unless (ref($err_or_queue)) {
+      $dbh->rollback if $oldAutoCommit;
+      return $err_or_queue;
+    }
+    push @$depend, $err_or_queue->jobnum;
+  }
+
+  $err_or_queue =
+    $self->queue_statuschange('activateNetworkElements', $depend, $svc, 1, $ems);
+  unless (ref($err_or_queue)) {
+    $dbh->rollback if $oldAutoCommit;
+    return $err_or_queue;
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  '';
+}
+
+sub export_links {
+  my( $self, $svc, $arrayref ) = ( shift, shift, shift );
+
+  push @$arrayref,
+    '<A HREF="http://'. $svc->ip_addr. '" target="_blank">SM</A>';
+
+  '';
 }
 
 sub queue_statuschange {
@@ -333,7 +532,7 @@ sub queue_statuschange {
     'svcnum' => $svc->svcnum,
     'job'    => 'FS::part_export::prizm::statuschange',
   };
-  $queue->insert(
+  my $error = $queue->insert(
     ( map { $self->option($_) }
           qw( url user password ) ),
     $method,
@@ -341,18 +540,19 @@ sub queue_statuschange {
     @args,
   );
 
-  if ($queue->jobnum) {                   # successful insertion
+  unless ($error) {                   # successful insertion
     foreach my $job ( @$jobs ) {
-      $queue->depend_insert($job);
+      $error ||= $queue->depend_insert($job);
     }
   }
 
+  $error or $queue;
 }
 
 sub statuschange {  # subroutine
   my( $url, $user, $password, $method, $mac_addr, @args) = @_;
 
-  eval "use Net::Prizm qw(CustomerInfo PrizmElement);";
+  eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
   die $@ if $@;
 
   my $prizm = new Net::Prizm (