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 $performance_profile = $svc->performance_profile;
204 $performance_profile ||= $svc->cust_svc->cust_pkg->part_pkg->pkg;
206 my $element_name_length = 50;
207 $element_name_length = $1
208 if $self->option('element_name_length') =~ /^\s*(\d+)\s*$/;
209 $err_or_som = $self->prizm_command('NetworkIfService', 'addProvisionedElement',
212 substr($name . " " . $svc->description,
213 0, $element_name_length),
216 sprintf("%032X", $svc->authkey),
217 $performance_profile,
219 ($self->option('ems') ? 1 : 0 ),
222 unless ref($err_or_som);
224 my (@names) = ('Management IP',
232 my (@values) = ($svc->ip_addr,
236 $name . " " . $svc->description,
240 $element = $err_or_som->result->elementId;
241 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
249 unless ref($err_or_som);
251 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
258 unless ref($err_or_som);
260 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
262 $performance_profile,
267 unless ref($err_or_som);
269 $err_or_som = $self->prizm_command('NetworkIfService',
270 'activateNetworkElements',
273 ( $self->option('ems') ? 1 : 0 ),
277 unless ref($err_or_som);
279 $err_or_som = $self->prizm_command('CustomerIfService',
280 'addElementToCustomer',
288 unless ref($err_or_som);
294 my( $self, $svc ) = ( shift, shift );
296 my $oldAutoCommit = $FS::UID::AutoCommit;
297 local $FS::UID::AutoCommit = 0;
300 my $cust_pkg = $svc->cust_svc->cust_pkg;
305 my $queue = new FS::queue {
306 'svcnum' => $svc->svcnum,
307 'job' => 'FS::part_export::prizm::queued_prizm_command',
309 my $error = $queue->insert(
310 ( map { $self->option($_) }
311 qw( url user password ) ),
313 'removeElementFromCustomer',
321 $dbh->rollback if $oldAutoCommit;
325 push @$depend, $queue->jobnum;
329 $self->queue_statuschange('deleteElement', $depend, $svc, 1);
331 unless (ref($err_or_queue)) {
332 $dbh->rollback if $oldAutoCommit;
333 return $err_or_queue;
336 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
341 sub _export_replace {
342 my( $self, $new, $old ) = ( shift, shift, shift );
344 my $err_or_som = $self->prizm_command('NetworkIfService', 'getPrizmElements',
350 unless ref($err_or_som);
352 return "Can't find prizm element for " . $old->mac_addr
353 unless $err_or_som->result->[0];
355 my %freeside2prizm = ( mac_addr => 'MAC Address',
356 ip_addr => 'Management IP',
357 latitude => 'GPS Latitude',
358 longitude => 'GPS Longitude',
359 altitude => 'GPS Altitude',
360 authkey => 'Authentication Key',
364 my (@names) = map { push @values, $new->$_; $freeside2prizm{$_} }
365 grep { $old->$_ ne $new->$_ }
366 grep { exists($freeside2prizm{$_}) }
367 fields( 'svc_broadband' );
369 if ($old->description ne $new->description) {
370 my $cust_main = $old->cust_svc->cust_pkg->cust_main;
371 my $name = defined($cust_main->dbdef_table->column('ship_last'))
372 ? $cust_main->ship_name
374 push @values, $name . " " . $new->description;
375 push @names, "Site Name";
378 my $element = $err_or_som->result->[0]->elementId;
380 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
388 unless ref($err_or_som);
390 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
396 if $old->vlan_profile ne $new->vlan_profile;
399 unless ref($err_or_som);
401 my $performance_profile = $new->performance_profile;
402 $performance_profile ||= $new->cust_svc->cust_pkg->part_pkg->pkg;
404 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
406 $performance_profile,
411 unless ref($err_or_som);
417 sub _export_suspend {
418 my( $self, $svc ) = ( shift, shift );
420 my $ems = $self->option('ems') ? 1 : 0;
421 my $err_or_queue = '';
423 my $oldAutoCommit = $FS::UID::AutoCommit;
424 local $FS::UID::AutoCommit = 0;
428 $self->queue_statuschange('suspendNetworkElements', [], $svc, 1, $ems);
429 unless (ref($err_or_queue)) {
430 $dbh->rollback if $oldAutoCommit;
431 return $err_or_queue;
433 push @$depend, $err_or_queue->jobnum;
435 if ($ems && $self->option('always_bam')) {
437 $self->queue_statuschange('suspendNetworkElements', $depend, $svc, 1, 0);
438 unless (ref($err_or_queue)) {
439 $dbh->rollback if $oldAutoCommit;
440 return $err_or_queue;
444 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
449 sub _export_unsuspend {
450 my( $self, $svc ) = ( shift, shift );
452 my $ems = $self->option('ems') ? 1 : 0;
453 my $err_or_queue = '';
455 my $oldAutoCommit = $FS::UID::AutoCommit;
456 local $FS::UID::AutoCommit = 0;
459 if ($ems && $self->option('always_bam')) {
461 $self->queue_statuschange('activateNetworkElements', [], $svc, 1, 0);
462 unless (ref($err_or_queue)) {
463 $dbh->rollback if $oldAutoCommit;
464 return $err_or_queue;
466 push @$depend, $err_or_queue->jobnum;
470 $self->queue_statuschange('activateNetworkElements', $depend, $svc, 1, $ems);
471 unless (ref($err_or_queue)) {
472 $dbh->rollback if $oldAutoCommit;
473 return $err_or_queue;
476 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
482 my( $self, $svc, $arrayref ) = ( shift, shift, shift );
484 push @$arrayref, '<A HREF="http://'. $svc->ip_addr. '">SM</A>';
489 sub queue_statuschange {
490 my( $self, $method, $jobs, $svc, @args ) = @_;
492 # already in a transaction and can't die here
494 my $queue = new FS::queue {
495 'svcnum' => $svc->svcnum,
496 'job' => 'FS::part_export::prizm::statuschange',
498 my $error = $queue->insert(
499 ( map { $self->option($_) }
500 qw( url user password ) ),
506 unless ($error) { # successful insertion
507 foreach my $job ( @$jobs ) {
508 $error ||= $queue->depend_insert($job);
515 sub statuschange { # subroutine
516 my( $url, $user, $password, $method, $mac_addr, @args) = @_;
518 eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
521 my $prizm = new Net::Prizm (
522 namespace => 'NetworkIfService',
525 password => $password,
528 my $err_or_som = $prizm->getPrizmElements( [ 'MAC Address' ],
533 unless ref($err_or_som);
535 die "Can't find prizm element for " . $mac_addr
536 unless $err_or_som->result->[0];
540 if ($method =~ /suspendNetworkElements/ || $method =~ /activateNetworkElements/) {
541 $arg1 = [ $err_or_som->result->[0]->elementId ];
543 $arg1 = $err_or_som->result->[0]->elementId;
545 $err_or_som = $prizm->$method( $arg1, @args );
548 unless ref($err_or_som);