conifgurable siteName and docs
[freeside.git] / FS / FS / part_export / prizm.pm
1 package FS::part_export::prizm;
2
3 use vars qw(@ISA %info %options $DEBUG);
4 use Tie::IxHash;
5 use FS::Record qw(fields);
6 use FS::part_export;
7
8 @ISA = qw(FS::part_export);
9 $DEBUG = 1;
10
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)' },
17 ;
18
19 my $notes = <<'EOT';
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>
24
25 Freeside will attempt to create an element in an existing network with the
26 values provided in svc_broadband.  Of particular interest are
27 <ul>
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
34 </ul>
35
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.
38
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.
42
43 <ul>
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
48   <li> address2
49   <li> city
50   <li> state
51   <li> zipCode
52   <li> country
53   <li> workPhone - the daytime phone number
54   <li> homePhone - the night phone number
55   <li> freesideId - the freeside customer number
56 </ul>
57
58   Additionally set on the element are
59 <ul>
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
63 </ul>
64
65 Freeside provisions, suspends, and unsuspends elements BAM only unless the
66 'Full EMS' checkbox is checked.<br><br>
67
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
75 possibly harmful.
76
77 EOT
78
79 %info = (
80   'svc'      => 'svc_broadband',
81   'desc'     => 'Real-time export to Northbound Interface',
82   'options'  => \%options,
83   'nodomain' => 'Y',
84   'notes'    => $notes,
85 );
86
87 sub prizm_command {
88   my ($self,$namespace,$method) = (shift,shift,shift);
89
90   eval "use Net::Prizm qw(CustomerInfo PrizmElement);";
91   die $@ if $@;
92
93   my $prizm = new Net::Prizm (
94     namespace => $namespace,
95     url => $self->option('url'),
96     user => $self->option('user'),
97     password => $self->option('password'),
98   );
99   
100   $prizm->$method(@_);
101 }
102
103 sub queued_prizm_command {  # subroutine
104   my( $url, $user, $password, $namespace, $method, @args ) = @_;
105
106   eval "use Net::Prizm qw(CustomerInfo PrizmElement);";
107   die $@ if $@;
108
109   my $prizm = new Net::Prizm (
110     namespace => $namespace,
111     url => $url,
112     user => $user,
113     password => $password,
114   );
115   
116   $err_or_som = $prizm->$method( @args);
117
118   die $err_or_som
119     unless ref($err_or_som);
120
121   '';
122
123 }
124
125 sub _export_insert {
126   my( $self, $svc ) = ( shift, shift );
127
128   my $cust_main = $svc->cust_svc->cust_pkg->cust_main;
129
130   my $err_or_som = $self->prizm_command('CustomerIfService', 'getCustomers',
131                                         ['import_id'],
132                                         [$cust_main->custnum],
133                                         ['='],
134                                        );
135   return $err_or_som
136     unless ref($err_or_som);
137
138   my $pre = '';
139   if ( defined $cust_main->dbdef_table->column('ship_last') ) {
140     $pre = $cust_main->ship_last ? 'ship_' : '';
141   }
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)
145                      );
146   my $contact = join(" ", map { my $method = "$pre$_"; $cust_main->$method }
147                           qw (daytime night)
148                      );
149
150   my $pcustomer;
151   if ($err_or_som->result->[0]) {
152     $pcustomer = $err_or_som->result->[0]->customerId;
153   }else{
154     my $chashref = $cust_main->hashref;
155     my $customerinfo = {
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',
168                           ],
169       extraFieldValues => [ $chashref->{"${pre}country"}, $cust_main->custnum,
170                           ],
171     };
172
173     $err_or_som = $self->prizm_command('CustomerIfService', 'addCustomer',
174                                        $customerinfo);
175     return $err_or_som
176       unless ref($err_or_som);
177
178     $pcustomer = $err_or_som->result;
179   }
180   warn "multiple prizm customers found for $cust_main->custnum"
181     if scalar(@$pcustomer) > 1;
182
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],
187 #                                     ['='],
188 #                   );
189 #  return $err_or_som
190 #    unless ref($err_or_som);
191 #
192 #  return "No elements in network" unless exists $err_or_som->result->[0];
193
194   my $networkid = 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];
198 #      last;
199 #    }
200 #  }
201
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',
206                                       $networkid,
207                                       $svc->mac_addr,
208                                       substr($name . " " . $svc->description,
209                                              0, $element_name_length),
210                                       $location,
211                                       $contact,
212                                       sprintf("%032X", $svc->authkey),
213                                       $svc->cust_svc->cust_pkg->part_pkg->pkg,
214                                       $svc->vlan_profile,
215                                       ($self->option('ems') ? 1 : 0 ),
216                                      );
217   return $err_or_som
218     unless ref($err_or_som);
219
220   my (@names) = ('Management IP',
221                  'GPS Latitude',
222                  'GPS Longitude',
223                  'GPS Altitude',
224                  'Site Name',
225                  'Site Location',
226                  'Site Contact',
227                  );
228   my (@values) = ($svc->ip_addr,
229                   $svc->latitude,
230                   $svc->longitude,
231                   $svc->altitude,
232                   $name . " " . $svc->description,
233                   $location,
234                   $contact,
235                   );
236   $element = $err_or_som->result->elementId;
237   $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
238                                      [ $element ],
239                                      \@names,
240                                      \@values,
241                                      0,
242                                      1,
243                                     );
244   return $err_or_som
245     unless ref($err_or_som);
246
247   $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
248                                      [ $element ],
249                                      $svc->vlan_profile,
250                                      0,
251                                      1,
252                                     );
253   return $err_or_som
254     unless ref($err_or_som);
255
256   $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
257                                      [ $element ],
258                                      $svc->cust_svc->cust_pkg->part_pkg->pkg,
259                                      0,
260                                      1,
261                                     );
262   return $err_or_som
263     unless ref($err_or_som);
264
265   $err_or_som = $self->prizm_command('NetworkIfService',
266                                      'activateNetworkElements',
267                                      [ $element ],
268                                      1,
269                                      ( $self->option('ems') ? 1 : 0 ),
270                                     );
271
272   return $err_or_som
273     unless ref($err_or_som);
274
275   $err_or_som = $self->prizm_command('CustomerIfService',
276                                      'addElementToCustomer',
277                                      0,
278                                      $cust_main->custnum,
279                                      0,
280                                      $svc->mac_addr,
281                                     );
282
283   return $err_or_som
284     unless ref($err_or_som);
285
286   '';
287 }
288
289 sub _export_delete {
290   my( $self, $svc ) = ( shift, shift );
291
292   my $cust_pkg = $svc->cust_svc->cust_pkg;
293
294   my $depend = [];
295
296   if ($cust_pkg) {
297     my $queue = new FS::queue {
298       'svcnum' => $svc->svcnum,
299       'job'    => 'FS::part_export::prizm::queued_prizm_command',
300     };
301     $queue->insert(
302       ( map { $self->option($_) }
303             qw( url user password ) ),
304       'CustomerIfService',
305       'removeElementFromCustomer',
306       0,
307       $cust_pkg->custnum,
308       0,
309       $svc->mac_addr,
310     ) && push @$depend, $queue->jobnum;
311   }
312
313   $self->queue_statuschange('deleteElement', $depend, $svc, 1);
314 }
315
316 sub _export_replace {
317   my( $self, $new, $old ) = ( shift, shift, shift );
318
319   my $err_or_som = $self->prizm_command('NetworkIfService', 'getPrizmElements',
320                                         [ 'MAC Address' ],
321                                         [ $old->mac_addr ],
322                                         [ '=' ],
323                                        );
324   return $err_or_som
325     unless ref($err_or_som);
326
327   return "Can't find prizm element for " . $old->mac_addr
328     unless $err_or_som->result->[0];
329
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',
336                        );
337   
338   my (@values);
339   my (@names) = map { push @values, $new->$_; $freeside2prizm{$_} }
340     grep { $old->$_ ne $new->$_ }
341       grep { exists($freeside2prizm{$_}) }
342         fields( 'svc_broadband' );
343
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
348              : $cust_main->name;
349     push @values, $name . " " . $new->description;
350     push @names, "Site Name";
351   }
352
353   my $element = $err_or_som->result->[0]->elementId;
354
355   $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
356                                         [ $element ],
357                                         \@names,
358                                         \@values,
359                                         0,
360                                         1,
361                                        );
362   return $err_or_som
363     unless ref($err_or_som);
364
365   $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
366                                      [ $element ],
367                                      $new->vlan_profile,
368                                      0,
369                                      1,
370                                     )
371     if $old->vlan_profile ne $new->vlan_profile;
372
373   return $err_or_som
374     unless ref($err_or_som);
375
376   '';
377
378 }
379
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);
384 }
385
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);
390 }
391
392 sub queue_statuschange {
393   my( $self, $method, $jobs, $svc, @args ) = @_;
394
395   # already in a transaction and can't die here
396
397   my $queue = new FS::queue {
398     'svcnum' => $svc->svcnum,
399     'job'    => 'FS::part_export::prizm::statuschange',
400   };
401   $queue->insert(
402     ( map { $self->option($_) }
403           qw( url user password ) ),
404     $method,
405     $svc->mac_addr,
406     @args,
407   );
408
409   if ($queue->jobnum) {                   # successful insertion
410     foreach my $job ( @$jobs ) {
411       $queue->depend_insert($job);
412     }
413   }
414
415 }
416
417 sub statuschange {  # subroutine
418   my( $url, $user, $password, $method, $mac_addr, @args) = @_;
419
420   eval "use Net::Prizm qw(CustomerInfo PrizmElement);";
421   die $@ if $@;
422
423   my $prizm = new Net::Prizm (
424     namespace => 'NetworkIfService',
425     url => $url,
426     user => $user,
427     password => $password,
428   );
429   
430   my $err_or_som = $prizm->getPrizmElements( [ 'MAC Address' ],
431                                              [ $mac_addr ],
432                                              [ '=' ],
433                                            );
434   die $err_or_som
435     unless ref($err_or_som);
436
437   die "Can't find prizm element for " . $mac_addr
438     unless $err_or_som->result->[0];
439
440   my $arg1;
441   # yuck!
442   if ($method =~ /suspendNetworkElements/ || $method =~ /activateNetworkElements/) {
443     $arg1 = [ $err_or_som->result->[0]->elementId ];
444   }else{
445     $arg1 = $err_or_som->result->[0]->elementId;
446   }
447   $err_or_som = $prizm->$method( $arg1, @args );
448
449   die $err_or_som
450     unless ref($err_or_som);
451
452   '';
453
454 }
455
456
457 1;