1 package FS::part_export::prizm;
3 use vars qw(@ISA %info %options $DEBUG);
5 use FS::Record qw(fields);
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 'element_name_length' => { label => 'Size of siteName (best left blank)' },
20 Real-time export of <b>svc_broadband</b>, <b>cust_pkg</b>, and <b>cust_main</b>
21 record data to Motorola
22 <a href="http://motorola.canopywireless.com/products/prizm/">Canopy Prizm
23 software</a> via the Northbound interface.<br><br>
25 Freeside will attempt to create an element in an existing network with the
26 values provided in svc_broadband. Of particular interest are
28 <li> mac address - used to identify the element
29 <li> vlan profile - an exact match for a vlan profiles defined in prizm
30 <li> ip address - defines the management ip address of the prizm element
31 <li> latitude - GPS latitude
32 <li> longitude - GPS longitude
33 <li> altitude - GPS altitude
36 In addition freeside attempts to set the service plan name in prizm to the
37 name of the package in which the service resides.
39 The service is associated with a customer in prizm as well, and freeside
40 will create the customer should none already exist with import id matching
41 the freeside customer number. The following fields are set.
44 <li> importId - the freeside customer number
45 <li> customerType - freeside
46 <li> customerName - the name associated with the freeside shipping address
47 <li> address1 - the shipping address
53 <li> workPhone - the daytime phone number
54 <li> homePhone - the night phone number
55 <li> freesideId - the freeside customer number
58 Additionally set on the element are
60 <li> Site Name - The shipping name followed by the service broadband description field
61 <li> Site Location - the shipping address
62 <li> Site Contact - the daytime and night phone numbers
65 Freeside provisions, suspends, and unsuspends elements BAM only unless the
66 'Full EMS' checkbox is checked.<br><br>
68 When freeside provisions an element the siteName is copied internally by
69 prizm in such a manner that it is possible for the value to exceed the size
70 of the column used in the prizm database. Therefore freeside truncates
71 by default this value to 50 characters. It is thought that this
72 column is the account_name column of the element_user_account table. It
73 may be possible to lift this limit by modifying the prizm database and
74 setting a new appropriate value on this export. This is untested and
80 'svc' => 'svc_broadband',
81 'desc' => 'Real-time export to Northbound Interface',
82 'options' => \%options,
88 my ($self,$namespace,$method) = (shift,shift,shift);
90 eval "use Net::Prizm qw(CustomerInfo PrizmElement);";
93 my $prizm = new Net::Prizm (
94 namespace => $namespace,
95 url => $self->option('url'),
96 user => $self->option('user'),
97 password => $self->option('password'),
103 sub queued_prizm_command { # subroutine
104 my( $url, $user, $password, $namespace, $method, @args ) = @_;
106 eval "use Net::Prizm qw(CustomerInfo PrizmElement);";
109 my $prizm = new Net::Prizm (
110 namespace => $namespace,
113 password => $password,
116 $err_or_som = $prizm->$method( @args);
119 unless ref($err_or_som);
126 my( $self, $svc ) = ( shift, shift );
128 my $cust_main = $svc->cust_svc->cust_pkg->cust_main;
130 my $err_or_som = $self->prizm_command('CustomerIfService', 'getCustomers',
132 [$cust_main->custnum],
136 unless ref($err_or_som);
139 if ( defined $cust_main->dbdef_table->column('ship_last') ) {
140 $pre = $cust_main->ship_last ? 'ship_' : '';
142 my $name = $pre ? $cust_main->ship_name : $cust_main->name;
143 my $location = join(" ", map { my $method = "$pre$_"; $cust_main->$method }
144 qw (address1 address2 city state zip)
146 my $contact = join(" ", map { my $method = "$pre$_"; $cust_main->$method }
151 if ($err_or_som->result->[0]) {
152 $pcustomer = $err_or_som->result->[0]->customerId;
154 my $chashref = $cust_main->hashref;
156 importId => $cust_main->custnum,
157 customerName => $name,
158 customerType => 'freeside',
159 address1 => $chashref->{"${pre}address1"},
160 address2 => $chashref->{"${pre}address2"},
161 city => $chashref->{"${pre}city"},
162 state => $chashref->{"${pre}state"},
163 zipCode => $chashref->{"${pre}zip"},
164 workPhone => $chashref->{"${pre}daytime"},
165 homePhone => $chashref->{"${pre}night"},
166 email => @{[$cust_main->invoicing_list_emailonly]}[0],
167 extraFieldNames => [ 'country', 'freesideId',
169 extraFieldValues => [ $chashref->{"${pre}country"}, $cust_main->custnum,
173 $err_or_som = $self->prizm_command('CustomerIfService', 'addCustomer',
176 unless ref($err_or_som);
178 $pcustomer = $err_or_som->result;
180 warn "multiple prizm customers found for $cust_main->custnum"
181 if scalar(@$pcustomer) > 1;
183 # #kinda big question/expensive
184 # $err_or_som = $self->prizm_command('NetworkIfService', 'getPrizmElements',
185 # ['Network Default Gateway Address'],
186 # [$svc->addr_block->ip_gateway],
190 # unless ref($err_or_som);
192 # return "No elements in network" unless exists $err_or_som->result->[0];
195 # for (my $i = 0; $i < $err_or_som->result->[0]->attributeNames; $i++) {
196 # if ($err_or_som->result->[0]->attributeNames->[$i] eq "Network.ID"){
197 # $networkid = $err_or_som->result->[0]->attributeValues->[$i];
202 my $element_name_length = 50;
203 $element_name_length = $1
204 if $self->option('element_name_length') =~ /^\s*(\d+)\s*$/;
205 $err_or_som = $self->prizm_command('NetworkIfService', 'addProvisionedElement',
208 substr($name . " " . $svc->description,
209 0, $element_name_length),
212 sprintf("%032X", $svc->authkey),
213 $svc->cust_svc->cust_pkg->part_pkg->pkg,
215 ($self->option('ems') ? 1 : 0 ),
218 unless ref($err_or_som);
220 my (@names) = ('Management IP',
228 my (@values) = ($svc->ip_addr,
232 $name . " " . $svc->description,
236 $element = $err_or_som->result->elementId;
237 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
245 unless ref($err_or_som);
247 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
254 unless ref($err_or_som);
256 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
258 $svc->cust_svc->cust_pkg->part_pkg->pkg,
263 unless ref($err_or_som);
265 $err_or_som = $self->prizm_command('NetworkIfService',
266 'activateNetworkElements',
269 ( $self->option('ems') ? 1 : 0 ),
273 unless ref($err_or_som);
275 $err_or_som = $self->prizm_command('CustomerIfService',
276 'addElementToCustomer',
284 unless ref($err_or_som);
290 my( $self, $svc ) = ( shift, shift );
292 my $cust_pkg = $svc->cust_svc->cust_pkg;
297 my $queue = new FS::queue {
298 'svcnum' => $svc->svcnum,
299 'job' => 'FS::part_export::prizm::queued_prizm_command',
302 ( map { $self->option($_) }
303 qw( url user password ) ),
305 'removeElementFromCustomer',
310 ) && push @$depend, $queue->jobnum;
313 $self->queue_statuschange('deleteElement', $depend, $svc, 1);
316 sub _export_replace {
317 my( $self, $new, $old ) = ( shift, shift, shift );
319 my $err_or_som = $self->prizm_command('NetworkIfService', 'getPrizmElements',
325 unless ref($err_or_som);
327 return "Can't find prizm element for " . $old->mac_addr
328 unless $err_or_som->result->[0];
330 my %freeside2prizm = ( mac_addr => 'MAC Address',
331 ip_addr => 'Management IP',
332 latitude => 'GPS Latitude',
333 longitude => 'GPS Longitude',
334 altitude => 'GPS Altitude',
335 authkey => 'Authentication Key',
339 my (@names) = map { push @values, $new->$_; $freeside2prizm{$_} }
340 grep { $old->$_ ne $new->$_ }
341 grep { exists($freeside2prizm{$_}) }
342 fields( 'svc_broadband' );
344 if ($old->description ne $new->description) {
345 my $cust_main = $old->cust_svc->cust_pkg->cust_main;
346 my $name = defined($cust_main->dbdef_table->column('ship_last'))
347 ? $cust_main->ship_name
349 push @values, $name . " " . $new->description;
350 push @names, "Site Name";
353 my $element = $err_or_som->result->[0]->elementId;
355 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
363 unless ref($err_or_som);
365 $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
371 if $old->vlan_profile ne $new->vlan_profile;
374 unless ref($err_or_som);
380 sub _export_suspend {
381 my( $self, $svc ) = ( shift, shift );
382 my $ems = $self->option('ems') ? 1 : 0;
383 $self->queue_statuschange('suspendNetworkElements', [], $svc, 1, $ems);
386 sub _export_unsuspend {
387 my( $self, $svc ) = ( shift, shift );
388 my $ems = $self->option('ems') ? 1 : 0;
389 $self->queue_statuschange('activateNetworkElements', [], $svc, 1, $ems);
392 sub queue_statuschange {
393 my( $self, $method, $jobs, $svc, @args ) = @_;
395 # already in a transaction and can't die here
397 my $queue = new FS::queue {
398 'svcnum' => $svc->svcnum,
399 'job' => 'FS::part_export::prizm::statuschange',
402 ( map { $self->option($_) }
403 qw( url user password ) ),
409 if ($queue->jobnum) { # successful insertion
410 foreach my $job ( @$jobs ) {
411 $queue->depend_insert($job);
417 sub statuschange { # subroutine
418 my( $url, $user, $password, $method, $mac_addr, @args) = @_;
420 eval "use Net::Prizm qw(CustomerInfo PrizmElement);";
423 my $prizm = new Net::Prizm (
424 namespace => 'NetworkIfService',
427 password => $password,
430 my $err_or_som = $prizm->getPrizmElements( [ 'MAC Address' ],
435 unless ref($err_or_som);
437 die "Can't find prizm element for " . $mac_addr
438 unless $err_or_som->result->[0];
442 if ($method =~ /suspendNetworkElements/ || $method =~ /activateNetworkElements/) {
443 $arg1 = [ $err_or_som->result->[0]->elementId ];
445 $arg1 = $err_or_som->result->[0]->elementId;
447 $err_or_som = $prizm->$method( $arg1, @args );
450 unless ref($err_or_som);