1 package FS::part_export::prizm;
3 use vars qw(@ISA %info %options $DEBUG);
5 use FS::Record qw(fields dbh);
8 @ISA = qw(FS::part_export);
11 tie %options, 'Tie::IxHash',
12 'url' => { label => 'Northbound url', default=>'https://localhost:8443/prizm/nbi' },
13 'user' => { label => 'Northbound username', default=>'nbi' },
14 'password' => { label => 'Password', default => '' },
15 'ems' => { label => 'Full EMS', type => 'checkbox' },
16 'always_bam' => { label => 'Always activate/suspend authentication', type => 'checkbox' },
17 'element_name_length' => { label => 'Size of siteName (best left blank)' },
21 Real-time export of <b>svc_broadband</b>, <b>cust_pkg</b>, and <b>cust_main</b>
22 record data to Motorola
23 <a href="http://motorola.canopywireless.com/products/prizm/">Canopy Prizm
24 software</a> via the Northbound interface.<br><br>
26 Freeside will attempt to create an element in an existing network with the
27 values provided in svc_broadband. Of particular interest are
29 <li> mac address - used to identify the element
30 <li> vlan profile - an exact match for a vlan profiles defined in prizm
31 <li> ip address - defines the management ip address of the prizm element
32 <li> latitude - GPS latitude
33 <li> longitude - GPS longitude
34 <li> altitude - GPS altitude
37 In addition freeside attempts to set the service plan name in prizm to the
38 name of the package in which the service resides.
40 The service is associated with a customer in prizm as well, and freeside
41 will create the customer should none already exist with import id matching
42 the freeside customer number. The following fields are set.
45 <li> importId - the freeside customer number
46 <li> customerType - freeside
47 <li> customerName - the name associated with the freeside shipping address
48 <li> address1 - the shipping address
54 <li> workPhone - the daytime phone number
55 <li> homePhone - the night phone number
56 <li> freesideId - the freeside customer number
59 Additionally set on the element are
61 <li> Site Name - The shipping name followed by the service broadband description field
62 <li> Site Location - the shipping address
63 <li> Site Contact - the daytime and night phone numbers
66 Freeside provisions, suspends, and unsuspends elements BAM only unless the
67 'Full EMS' checkbox is checked.<br><br>
69 When freeside provisions an element the siteName is copied internally by
70 prizm in such a manner that it is possible for the value to exceed the size
71 of the column used in the prizm database. Therefore freeside truncates
72 by default this value to 50 characters. It is thought that this
73 column is the account_name column of the element_user_account table. It
74 may be possible to lift this limit by modifying the prizm database and
75 setting a new appropriate value on this export. This is untested and
81 'svc' => 'svc_broadband',
82 'desc' => 'Real-time export to Northbound Interface',
83 'options' => \%options,
89 my ($self,$namespace,$method) = (shift,shift,shift);
91 eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
94 my $prizm = new Net::Prizm (
95 namespace => $namespace,
96 url => $self->option('url'),
97 user => $self->option('user'),
98 password => $self->option('password'),
104 sub queued_prizm_command { # subroutine
105 my( $url, $user, $password, $namespace, $method, @args ) = @_;
107 eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
110 my $prizm = new Net::Prizm (
111 namespace => $namespace,
114 password => $password,
117 $err_or_som = $prizm->$method( @args);
120 unless ref($err_or_som);
127 my( $self, $svc ) = ( shift, shift );
129 my $cust_main = $svc->cust_svc->cust_pkg->cust_main;
131 my $err_or_som = $self->prizm_command('CustomerIfService', 'getCustomers',
133 [$cust_main->custnum],
137 unless ref($err_or_som);
140 if ( defined $cust_main->dbdef_table->column('ship_last') ) {
141 $pre = $cust_main->ship_last ? 'ship_' : '';
143 my $name = $pre ? $cust_main->ship_name : $cust_main->name;
144 my $location = join(" ", map { my $method = "$pre$_"; $cust_main->$method }
145 qw (address1 address2 city state zip)
147 my $contact = join(" ", map { my $method = "$pre$_"; $cust_main->$method }
152 if ($err_or_som->result->[0]) {
153 $pcustomer = $err_or_som->result->[0]->customerId;
155 my $chashref = $cust_main->hashref;
157 importId => $cust_main->custnum,
158 customerName => $name,
159 customerType => 'freeside',
160 address1 => $chashref->{"${pre}address1"},
161 address2 => $chashref->{"${pre}address2"},
162 city => $chashref->{"${pre}city"},
163 state => $chashref->{"${pre}state"},
164 zipCode => $chashref->{"${pre}zip"},
165 workPhone => $chashref->{"${pre}daytime"},
166 homePhone => $chashref->{"${pre}night"},
167 email => @{[$cust_main->invoicing_list_emailonly]}[0],
168 extraFieldNames => [ 'country', 'freesideId',
170 extraFieldValues => [ $chashref->{"${pre}country"}, $cust_main->custnum,
174 $err_or_som = $self->prizm_command('CustomerIfService', 'addCustomer',
177 unless ref($err_or_som);
179 $pcustomer = $err_or_som->result;
181 warn "multiple prizm customers found for $cust_main->custnum"
182 if scalar(@$pcustomer) > 1;
184 # #kinda big question/expensive
185 # $err_or_som = $self->prizm_command('NetworkIfService', 'getPrizmElements',
186 # ['Network Default Gateway Address'],
187 # [$svc->addr_block->ip_gateway],
191 # unless ref($err_or_som);
193 # return "No elements in network" unless exists $err_or_som->result->[0];
196 # for (my $i = 0; $i < $err_or_som->result->[0]->attributeNames; $i++) {
197 # if ($err_or_som->result->[0]->attributeNames->[$i] eq "Network.ID"){
198 # $networkid = $err_or_som->result->[0]->attributeValues->[$i];
203 my $element_name_length = 50;
204 $element_name_length = $1
205 if $self->option('element_name_length') =~ /^\s*(\d+)\s*$/;
206 $err_or_som = $self->prizm_command('NetworkIfService', 'addProvisionedElement',
209 substr($name . " " . $svc->description,
210 0, $element_name_length),
213 sprintf("%032X", $svc->authkey),
214 $svc->cust_svc->cust_pkg->part_pkg->pkg,
216 ($self->option('ems') ? 1 : 0 ),
219 unless ref($err_or_som);
221 my (@names) = ('Management IP',
229 my (@values) = ($svc->ip_addr,
233 $name . " " . $svc->description,
237 $element = $err_or_som->result->elementId;
238 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
246 unless ref($err_or_som);
248 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
255 unless ref($err_or_som);
257 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
259 $svc->cust_svc->cust_pkg->part_pkg->pkg,
264 unless ref($err_or_som);
266 $err_or_som = $self->prizm_command('NetworkIfService',
267 'activateNetworkElements',
270 ( $self->option('ems') ? 1 : 0 ),
274 unless ref($err_or_som);
276 $err_or_som = $self->prizm_command('CustomerIfService',
277 'addElementToCustomer',
285 unless ref($err_or_som);
291 my( $self, $svc ) = ( shift, shift );
293 my $oldAutoCommit = $FS::UID::AutoCommit;
294 local $FS::UID::AutoCommit = 0;
297 my $cust_pkg = $svc->cust_svc->cust_pkg;
302 my $queue = new FS::queue {
303 'svcnum' => $svc->svcnum,
304 'job' => 'FS::part_export::prizm::queued_prizm_command',
306 my $error = $queue->insert(
307 ( map { $self->option($_) }
308 qw( url user password ) ),
310 'removeElementFromCustomer',
318 $dbh->rollback if $oldAutoCommit;
322 push @$depend, $queue->jobnum;
326 $self->queue_statuschange('deleteElement', $depend, $svc, 1);
328 unless (ref($err_or_queue)) {
329 $dbh->rollback if $oldAutoCommit;
330 return $err_or_queue;
333 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
338 sub _export_replace {
339 my( $self, $new, $old ) = ( shift, shift, shift );
341 my $err_or_som = $self->prizm_command('NetworkIfService', 'getPrizmElements',
347 unless ref($err_or_som);
349 return "Can't find prizm element for " . $old->mac_addr
350 unless $err_or_som->result->[0];
352 my %freeside2prizm = ( mac_addr => 'MAC Address',
353 ip_addr => 'Management IP',
354 latitude => 'GPS Latitude',
355 longitude => 'GPS Longitude',
356 altitude => 'GPS Altitude',
357 authkey => 'Authentication Key',
361 my (@names) = map { push @values, $new->$_; $freeside2prizm{$_} }
362 grep { $old->$_ ne $new->$_ }
363 grep { exists($freeside2prizm{$_}) }
364 fields( 'svc_broadband' );
366 if ($old->description ne $new->description) {
367 my $cust_main = $old->cust_svc->cust_pkg->cust_main;
368 my $name = defined($cust_main->dbdef_table->column('ship_last'))
369 ? $cust_main->ship_name
371 push @values, $name . " " . $new->description;
372 push @names, "Site Name";
375 my $element = $err_or_som->result->[0]->elementId;
377 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
385 unless ref($err_or_som);
387 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
393 if $old->vlan_profile ne $new->vlan_profile;
396 unless ref($err_or_som);
402 sub _export_suspend {
403 my( $self, $svc ) = ( shift, shift );
405 my $ems = $self->option('ems') ? 1 : 0;
406 my $err_or_queue = '';
408 my $oldAutoCommit = $FS::UID::AutoCommit;
409 local $FS::UID::AutoCommit = 0;
413 $self->queue_statuschange('suspendNetworkElements', [], $svc, 1, $ems);
414 unless (ref($err_or_queue)) {
415 $dbh->rollback if $oldAutoCommit;
416 return $err_or_queue;
418 push @$depend, $err_or_queue->jobnum;
420 if ($ems && $self->option('always_bam')) {
422 $self->queue_statuschange('suspendNetworkElements', $depend, $svc, 1, 0);
423 unless (ref($err_or_queue)) {
424 $dbh->rollback if $oldAutoCommit;
425 return $err_or_queue;
429 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
434 sub _export_unsuspend {
435 my( $self, $svc ) = ( shift, shift );
437 my $ems = $self->option('ems') ? 1 : 0;
438 my $err_or_queue = '';
440 my $oldAutoCommit = $FS::UID::AutoCommit;
441 local $FS::UID::AutoCommit = 0;
444 if ($ems && $self->option('always_bam')) {
446 $self->queue_statuschange('activateNetworkElements', [], $svc, 1, 0);
447 unless (ref($err_or_queue)) {
448 $dbh->rollback if $oldAutoCommit;
449 return $err_or_queue;
451 push @$depend, $err_or_queue->jobnum;
455 $self->queue_statuschange('activateNetworkElements', $depend, $svc, 1, $ems);
456 unless (ref($err_or_queue)) {
457 $dbh->rollback if $oldAutoCommit;
458 return $err_or_queue;
461 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
467 my( $self, $svc, $arrayref ) = ( shift, shift, shift );
469 push @$arrayref, '<A HREF="http://'. $svc->ip_addr. '">SM</A>';
474 sub queue_statuschange {
475 my( $self, $method, $jobs, $svc, @args ) = @_;
477 # already in a transaction and can't die here
479 my $queue = new FS::queue {
480 'svcnum' => $svc->svcnum,
481 'job' => 'FS::part_export::prizm::statuschange',
483 my $error = $queue->insert(
484 ( map { $self->option($_) }
485 qw( url user password ) ),
491 unless ($error) { # successful insertion
492 foreach my $job ( @$jobs ) {
493 $error ||= $queue->depend_insert($job);
500 sub statuschange { # subroutine
501 my( $url, $user, $password, $method, $mac_addr, @args) = @_;
503 eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
506 my $prizm = new Net::Prizm (
507 namespace => 'NetworkIfService',
510 password => $password,
513 my $err_or_som = $prizm->getPrizmElements( [ 'MAC Address' ],
518 unless ref($err_or_som);
520 die "Can't find prizm element for " . $mac_addr
521 unless $err_or_som->result->[0];
525 if ($method =~ /suspendNetworkElements/ || $method =~ /activateNetworkElements/) {
526 $arg1 = [ $err_or_som->result->[0]->elementId ];
528 $arg1 = $err_or_som->result->[0]->elementId;
530 $err_or_som = $prizm->$method( $arg1, @args );
533 unless ref($err_or_som);