1 package FS::part_export::prizm;
3 use vars qw(@ISA %info %options $DEBUG $me);
5 use FS::Record qw(fields dbh);
8 @ISA = qw(FS::part_export);
10 $me = '[' . __PACKAGE__ . ']';
12 tie %options, 'Tie::IxHash',
13 'url' => { label => 'Northbound url', default=>'https://localhost:8443/prizm/nbi' },
14 'user' => { label => 'Northbound username', default=>'nbi' },
15 'password' => { label => 'Password', default => '' },
16 'ems' => { label => 'Full EMS', type => 'checkbox' },
17 'always_bam' => { label => 'Always activate/suspend authentication', type => 'checkbox' },
18 'element_name_length' => { label => 'Size of siteName (best left blank)' },
22 Real-time export of <b>svc_broadband</b>, <b>cust_pkg</b>, and <b>cust_main</b>
23 record data to Motorola
24 <a href="http://motorola.canopywireless.com/products/prizm/">Canopy Prizm
25 software</a> via the Northbound interface.<br><br>
27 Freeside will attempt to create an element in an existing network with the
28 values provided in svc_broadband. Of particular interest are
30 <li> mac address - used to identify the element
31 <li> vlan profile - an exact match for a vlan profiles defined in prizm
32 <li> ip address - defines the management ip address of the prizm element
33 <li> latitude - GPS latitude
34 <li> longitude - GPS longitude
35 <li> altitude - GPS altitude
38 In addition freeside attempts to set the service plan name in prizm to the
39 name of the package in which the service resides.
41 The service is associated with a customer in prizm as well, and freeside
42 will create the customer should none already exist with import id matching
43 the freeside customer number. The following fields are set.
46 <li> importId - the freeside customer number
47 <li> customerType - freeside
48 <li> customerName - the name associated with the freeside shipping address
49 <li> address1 - the shipping address
55 <li> workPhone - the daytime phone number
56 <li> homePhone - the night phone number
57 <li> freesideId - the freeside customer number
60 Additionally set on the element are
62 <li> Site Name - The shipping name followed by the service broadband description field
63 <li> Site Location - the shipping address
64 <li> Site Contact - the daytime and night phone numbers
67 Freeside provisions, suspends, and unsuspends elements BAM only unless the
68 'Full EMS' checkbox is checked.<br><br>
70 When freeside provisions an element the siteName is copied internally by
71 prizm in such a manner that it is possible for the value to exceed the size
72 of the column used in the prizm database. Therefore freeside truncates
73 by default this value to 50 characters. It is thought that this
74 column is the account_name column of the element_user_account table. It
75 may be possible to lift this limit by modifying the prizm database and
76 setting a new appropriate value on this export. This is untested and
82 'svc' => 'svc_broadband',
83 'desc' => 'Real-time export to Northbound Interface',
84 'options' => \%options,
90 my ($self,$namespace,$method) = (shift,shift,shift);
92 eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
95 my $prizm = new Net::Prizm (
96 namespace => $namespace,
97 url => $self->option('url'),
98 user => $self->option('user'),
99 password => $self->option('password'),
105 sub queued_prizm_command { # subroutine
106 my( $url, $user, $password, $namespace, $method, @args ) = @_;
108 eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
111 my $prizm = new Net::Prizm (
112 namespace => $namespace,
115 password => $password,
118 $err_or_som = $prizm->$method( @args);
121 unless ref($err_or_som);
128 my( $self, $svc ) = ( shift, shift );
129 warn "$me: _export_insert called for export ". $self->exportnum.
130 " on service ". $svc->svcnum. "\n"
133 my $cust_main = $svc->cust_svc->cust_pkg->cust_main;
135 my $err_or_som = $self->prizm_command('CustomerIfService', 'getCustomers',
137 [$cust_main->custnum],
141 unless ref($err_or_som);
144 if ( defined $cust_main->dbdef_table->column('ship_last') ) {
145 $pre = $cust_main->ship_last ? 'ship_' : '';
147 my $name = $pre ? $cust_main->ship_name : $cust_main->name;
148 my $location = join(" ", map { my $method = "$pre$_"; $cust_main->$method }
149 qw (address1 address2 city state zip)
151 my $contact = join(" ", map { my $method = "$pre$_"; $cust_main->$method }
156 if ($err_or_som->result->[0]) {
157 $pcustomer = $err_or_som->result->[0]->customerId;
158 warn "$me: found customer $pcustomer in prizm\n" if $DEBUG;
160 my $chashref = $cust_main->hashref;
162 importId => $cust_main->custnum,
163 customerName => $name,
164 customerType => 'freeside',
165 address1 => $chashref->{"${pre}address1"},
166 address2 => $chashref->{"${pre}address2"},
167 city => $chashref->{"${pre}city"},
168 state => $chashref->{"${pre}state"},
169 zipCode => $chashref->{"${pre}zip"},
170 workPhone => $chashref->{"${pre}daytime"},
171 homePhone => $chashref->{"${pre}night"},
172 email => @{[$cust_main->invoicing_list_emailonly]}[0],
173 extraFieldNames => [ 'country', 'freesideId',
175 extraFieldValues => [ $chashref->{"${pre}country"}, $cust_main->custnum,
179 $err_or_som = $self->prizm_command('CustomerIfService', 'addCustomer',
182 unless ref($err_or_som);
184 $pcustomer = $err_or_som->result;
185 warn "$me: added customer $pcustomer to prizm\n" if $DEBUG;
187 warn "multiple prizm customers found for $cust_main->custnum"
188 if scalar(@$pcustomer) > 1;
190 # #kinda big question/expensive
191 # $err_or_som = $self->prizm_command('NetworkIfService', 'getPrizmElements',
192 # ['Network Default Gateway Address'],
193 # [$svc->addr_block->ip_gateway],
197 # unless ref($err_or_som);
199 # return "No elements in network" unless exists $err_or_som->result->[0];
202 # for (my $i = 0; $i < $err_or_som->result->[0]->attributeNames; $i++) {
203 # if ($err_or_som->result->[0]->attributeNames->[$i] eq "Network.ID"){
204 # $networkid = $err_or_som->result->[0]->attributeValues->[$i];
209 # here we cope with a problem of prizm failing to insert for reason
210 # of duplicate mac addr, but doing so inconsistently... a race in prizm?
212 $self->prizm_command( 'CustomerIfService', 'removeElementFromCustomer',
219 $err_or_som = $self->prizm_command( 'NetworkIfService', 'getPrizmElements',
224 if ( ref($err_or_som) && $err_or_som->result->[0] ) { # ignore errors
225 $self->prizm_command( 'NetworkIfService', 'deleteElement',
226 $err_or_som->result->[0],
232 my $performance_profile = $svc->performance_profile;
233 $performance_profile ||= $svc->cust_svc->cust_pkg->part_pkg->pkg;
235 my $element_name_length = 50;
236 $element_name_length = $1
237 if $self->option('element_name_length') =~ /^\s*(\d+)\s*$/;
238 $err_or_som = $self->prizm_command('NetworkIfService', 'addProvisionedElement',
241 substr($name . " " . $svc->description,
242 0, $element_name_length),
245 sprintf("%032X", $svc->authkey || 0),
246 $performance_profile,
248 ($self->option('ems') ? 1 : 0 ),
251 unless ref($err_or_som);
252 warn "$me: added provisioned element to prizm\n" if $DEBUG;
254 my (@names) = ('Management IP',
262 my (@values) = ($svc->ip_addr,
266 $name . " " . $svc->description,
270 $element = $err_or_som->result->elementId;
271 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
279 unless ref($err_or_som);
280 warn "$me: set element configuration\n" if $DEBUG;
282 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
289 unless ref($err_or_som);
290 warn "$me: set element vlan profile\n" if $DEBUG;
292 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
294 $performance_profile,
299 unless ref($err_or_som);
300 warn "$me: set element configset (performance profile)\n" if $DEBUG;
302 $err_or_som = $self->prizm_command('NetworkIfService',
303 'activateNetworkElements',
306 ( $self->option('ems') ? 1 : 0 ),
310 unless ref($err_or_som);
311 warn "$me: activated element\n" if $DEBUG;
313 $err_or_som = $self->prizm_command('CustomerIfService',
314 'addElementToCustomer',
322 unless ref($err_or_som);
323 warn "$me: added element to customer\n" if $DEBUG;
329 my( $self, $svc ) = ( shift, shift );
331 my $oldAutoCommit = $FS::UID::AutoCommit;
332 local $FS::UID::AutoCommit = 0;
335 my $cust_pkg = $svc->cust_svc->cust_pkg;
340 my $queue = new FS::queue {
341 'svcnum' => $svc->svcnum,
342 'job' => 'FS::part_export::prizm::queued_prizm_command',
344 my $error = $queue->insert(
345 ( map { $self->option($_) }
346 qw( url user password ) ),
348 'removeElementFromCustomer',
356 $dbh->rollback if $oldAutoCommit;
360 push @$depend, $queue->jobnum;
364 $self->queue_statuschange('deleteElement', $depend, $svc, 1);
366 unless (ref($err_or_queue)) {
367 $dbh->rollback if $oldAutoCommit;
368 return $err_or_queue;
371 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
376 sub _export_replace {
377 my( $self, $new, $old ) = ( shift, shift, shift );
379 my $err_or_som = $self->prizm_command('NetworkIfService', 'getPrizmElements',
385 unless ref($err_or_som);
387 return "Can't find prizm element for " . $old->mac_addr
388 unless $err_or_som->result->[0];
390 my %freeside2prizm = ( mac_addr => 'MAC Address',
391 ip_addr => 'Management IP',
392 latitude => 'GPS Latitude',
393 longitude => 'GPS Longitude',
394 altitude => 'GPS Altitude',
395 authkey => 'Authentication Key',
399 my (@names) = map { push @values, $new->$_; $freeside2prizm{$_} }
400 grep { $old->$_ ne $new->$_ }
401 grep { exists($freeside2prizm{$_}) }
402 fields( 'svc_broadband' );
404 if ($old->description ne $new->description) {
405 my $cust_main = $old->cust_svc->cust_pkg->cust_main;
406 my $name = defined($cust_main->dbdef_table->column('ship_last'))
407 ? $cust_main->ship_name
409 push @values, $name . " " . $new->description;
410 push @names, "Site Name";
413 my $element = $err_or_som->result->[0]->elementId;
415 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
423 unless ref($err_or_som);
425 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
431 if $old->vlan_profile ne $new->vlan_profile;
434 unless ref($err_or_som);
436 my $performance_profile = $new->performance_profile;
437 $performance_profile ||= $new->cust_svc->cust_pkg->part_pkg->pkg;
439 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
441 $performance_profile,
446 unless ref($err_or_som);
452 sub _export_suspend {
453 my( $self, $svc ) = ( shift, shift );
455 my $ems = $self->option('ems') ? 1 : 0;
456 my $err_or_queue = '';
458 my $oldAutoCommit = $FS::UID::AutoCommit;
459 local $FS::UID::AutoCommit = 0;
463 $self->queue_statuschange('suspendNetworkElements', [], $svc, 1, $ems);
464 unless (ref($err_or_queue)) {
465 $dbh->rollback if $oldAutoCommit;
466 return $err_or_queue;
468 push @$depend, $err_or_queue->jobnum;
470 if ($ems && $self->option('always_bam')) {
472 $self->queue_statuschange('suspendNetworkElements', $depend, $svc, 1, 0);
473 unless (ref($err_or_queue)) {
474 $dbh->rollback if $oldAutoCommit;
475 return $err_or_queue;
479 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
484 sub _export_unsuspend {
485 my( $self, $svc ) = ( shift, shift );
487 my $ems = $self->option('ems') ? 1 : 0;
488 my $err_or_queue = '';
490 my $oldAutoCommit = $FS::UID::AutoCommit;
491 local $FS::UID::AutoCommit = 0;
494 if ($ems && $self->option('always_bam')) {
496 $self->queue_statuschange('activateNetworkElements', [], $svc, 1, 0);
497 unless (ref($err_or_queue)) {
498 $dbh->rollback if $oldAutoCommit;
499 return $err_or_queue;
501 push @$depend, $err_or_queue->jobnum;
505 $self->queue_statuschange('activateNetworkElements', $depend, $svc, 1, $ems);
506 unless (ref($err_or_queue)) {
507 $dbh->rollback if $oldAutoCommit;
508 return $err_or_queue;
511 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
517 my( $self, $svc, $arrayref ) = ( shift, shift, shift );
520 '<A HREF="http://'. $svc->ip_addr. '" target="_blank">SM</A>';
525 sub queue_statuschange {
526 my( $self, $method, $jobs, $svc, @args ) = @_;
528 # already in a transaction and can't die here
530 my $queue = new FS::queue {
531 'svcnum' => $svc->svcnum,
532 'job' => 'FS::part_export::prizm::statuschange',
534 my $error = $queue->insert(
535 ( map { $self->option($_) }
536 qw( url user password ) ),
542 unless ($error) { # successful insertion
543 foreach my $job ( @$jobs ) {
544 $error ||= $queue->depend_insert($job);
551 sub statuschange { # subroutine
552 my( $url, $user, $password, $method, $mac_addr, @args) = @_;
554 eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
557 my $prizm = new Net::Prizm (
558 namespace => 'NetworkIfService',
561 password => $password,
564 my $err_or_som = $prizm->getPrizmElements( [ 'MAC Address' ],
569 unless ref($err_or_som);
571 die "Can't find prizm element for " . $mac_addr
572 unless $err_or_som->result->[0];
576 if ($method =~ /suspendNetworkElements/ || $method =~ /activateNetworkElements/) {
577 $arg1 = [ $err_or_som->result->[0]->elementId ];
579 $arg1 = $err_or_som->result->[0]->elementId;
581 $err_or_som = $prizm->$method( $arg1, @args );
584 unless ref($err_or_som);