add XML-RPC export, RT#17622
[freeside.git] / FS / FS / part_export / soma.pm
1 package FS::part_export::soma;
2
3 use vars qw(@ISA %info %options $DEBUG);
4 use Tie::IxHash;
5 use FS::Record qw(fields dbh);
6 use FS::part_export;
7
8 @ISA = qw(FS::part_export);
9 $DEBUG = 1;
10
11 tie %options, 'Tie::IxHash',
12   'url'         => { label => 'Soma OSS-API url', default=>'https://localhost:8088/ossapi/services' },
13   'data_app_id' => { label => 'SOMA Data Application Id', default => '' },
14 ;
15
16 my $notes = <<'EOT';
17 Real-time export of <b>svc_external</b> and <b>svc_broadband</b> record data
18 to SOMA Networks <a href="http://www.somanetworks.com">platform</a> via the
19 OSS-API.<br><br>
20
21 Freeside will attempt to create/delete a cpe for the ESN provided in
22 svc_external.  If a data application id is provided then freeside will
23 use the values provided in svc_broadband to manage the attributes and 
24 features of that cpe.
25
26 EOT
27
28 %info = (
29   'svc'      => [ qw ( svc_broadband svc_external ) ],
30   'desc'     => 'Real-time export to SOMA platform',
31   'options'  => \%options,
32   'nodomain' => 'Y',
33   'notes'    => $notes,
34 );
35
36 sub _export_insert {
37   my( $self, $svc ) = ( shift, shift );
38
39   warn "_export_insert called for service ". $svc->svcnum
40     if $DEBUG;
41
42   my %args = ( url => $self->option('url'), method => '_queueable_insert' );
43
44   $args{esn} = $self->esn($svc) or return 'No ESN found!';
45
46   my $svcdb = $svc->cust_svc->part_svc->svcdb;
47   $args{svcdb} = $svcdb;
48   if ( $svcdb eq 'svc_external' ) {
49     #do nothing
50   } elsif ( $svcdb eq 'svc_broadband' ){
51     $args{data_app_id} = $self->option('data_app_id')
52   } else {
53     return "Don't know how to provision $svcdb";
54   }
55
56   warn "dispatching statuschange" if $DEBUG;
57
58   eval { statuschange(%args) };
59   return $@ if $@;
60
61   '';
62 }
63
64 sub _export_delete {
65   my( $self, $svc ) = ( shift, shift );
66
67   my %args = ( url => $self->option('url'), method => '_queueable_delete' );
68
69   $args{esn} = $self->esn($svc) or return 'No ESN found!';
70
71   my $svcdb = $svc->cust_svc->part_svc->svcdb;
72   $args{svcdb} = $svcdb;
73   if ( $svcdb eq 'svc_external' ) {
74     #do nothing
75   } elsif ( $svcdb eq 'svc_broadband' ){
76     $args{data_app_id} = $self->option('data_app_id')
77   } else {
78     return "Don't know how to provision $svcdb";
79   }
80
81   eval { statuschange(%args) };
82   return $@ if $@;
83
84   '';
85 }
86
87 sub _export_replace {
88   my( $self, $new, $old ) = ( shift, shift, shift );
89
90   my %args = ( url => $self->option('url'), method => '_queueable_replace' );
91
92   $args{esn}     = $self->esn($old) or return 'No old ESN found!';
93   $args{new_esn} = $self->esn($new) or return 'No new ESN found!';
94
95   my $svcdb = $old->cust_svc->part_svc->svcdb;
96   $args{svcdb} = $svcdb;
97   if ( $svcdb eq 'svc_external' ) {
98     #do nothing
99   } elsif ( $svcdb eq 'svc_broadband' ){
100     $args{data_app_id} = $self->option('data_app_id')
101   } else {
102     return "Don't know how to provision $svcdb";
103   }
104
105   eval { statuschange(%args) };
106   return $@ if $@;
107
108   '';
109 }
110
111 sub _export_suspend {
112   my( $self, $svc ) = ( shift, shift );
113
114   $self->queue_statuschange('_queueable_suspend', $svc);
115 }
116
117 sub _export_unsuspend {
118   my( $self, $svc ) = ( shift, shift );
119
120   $self->queue_statuschange('_queueable_unsuspend', $svc);
121 }
122
123 sub queue_statuschange {
124   my( $self, $method, $svc ) = @_;
125
126   my %args = ( url => $self->option('url'), method => $method );
127
128   my $svcdb = $svc->cust_svc->part_svc->svcdb;
129   $args{svcdb} = $svcdb;
130   if ( $svcdb eq 'svc_external' ) {
131     #do absolutely nothing
132     return '';
133   } elsif ( $svcdb eq 'svc_broadband' ){
134     $args{data_app_id} = $self->option('data_app_id')
135   } else {
136     return "Don't know how to provision $svcdb";
137   }
138
139   $args{esn} = $self->esn($svc);
140
141   my $queue = new FS::queue {
142     'svcnum' => $svc->svcnum,
143     'job'    => 'FS::part_export::soma::statuschange',
144   };
145   my $error = $queue->insert( %args );
146
147   return $error if $error;
148
149   '';
150
151 }
152
153 sub statuschange {  # subroutine
154   my( %options ) = @_;
155
156   warn "statuschange called with options ". 
157        join (', ', map { "$_ => $options{$_}" } keys(%options))
158     if $DEBUG;
159
160   my $method = $options{method};
161
162   eval "use Net::Soma 0.01 qw(ApplicationDef ApplicationInstance
163                               AttributeDef AttributeInstance);";
164   die $@ if $@;
165
166   my %soma_objects = ();
167   foreach my $service ( qw ( CPECollection CPEAccess AppCatalog Applications ) )
168   {
169     $soma_objects{$service} = new Net::Soma ( namespace => $service."Service",
170                                               url       => $options{'url'},
171                                               die_on_fault => 1,
172                                             );
173   }
174   
175   my $cpeid = eval {$soma_objects{CPECollection}->getCPEByESN( $options{esn} )};
176   warn "failed to find CPE with ESN $options{esn}"
177     if ($DEBUG && !$cpeid);
178
179   if ( $method eq '_queueable_insert' && $options{svcdb} eq 'svc_external' ) {
180     if ( !$cpeid ) {
181       # only type 1 is used at this time
182       $cpeid = $soma_objects{CPECollection}->createCPE( $options{esn}, 1 );
183     } else {
184       $soma_objects{CPECollection}->releaseCPE( $cpeid );
185       die "Soma element for $options{esn} already exists";
186     }
187   }
188
189   die "Can't find soma element for $options{esn}"
190     unless $cpeid;
191
192   warn "dispatching $method from statuschange" if $DEBUG;
193   &{$method}( \%soma_objects, $cpeid, %options );
194
195 }
196
197 sub _queueable_insert {
198   my( $soma_objects, $cpeid, %options ) = @_;
199
200   warn "_queueable_insert called for $cpeid with options ". 
201        join (', ', map { "$_ => $options{$_}" } keys(%options))
202     if $DEBUG;
203
204   my $appid = $options{data_app_id};
205   if ($appid) {
206     my $application =
207       $soma_objects->{AppCatalog}
208                    ->getDefaultApplicationInstance($appid, $cpeid);
209
210     my $attribute =
211       $soma_objects->{AppCatalog}
212                    ->getDefaultApplicationAttributeInstance(2, 1, $cpeid);
213     $attribute->value('G');
214
215     my $i = 0;
216     foreach my $instance (@{$application->attributes}) {
217       unless ($instance->definitionId == $attribute->definitionId) {
218         $i++; next;
219       }
220       $application->attributes->[$i] = $attribute;
221       last;
222     }
223
224     $soma_objects->{Applications}->subscribeApp( $cpeid, $application );
225   }
226
227   $soma_objects->{CPECollection}->releaseCPE( $cpeid );
228
229   '';
230 }
231
232 sub _queueable_delete {
233   my( $soma_objects, $cpeid, %options ) = @_;
234
235   my $appid = $options{data_app_id};
236   my $norelease;
237
238   if ($appid) {
239     my $applications =
240       $soma_objects->{Applications}->getSubscribedApplications( $cpeid );
241
242     my $instance_id;
243     foreach $application (@$applications) {
244       next unless $application->definitionId == $appid;
245       $instance_id = $application->instanceId;
246     }
247
248     $soma_objects->{Applications}->unsubscribeApp( $cpeid, $instance_id );
249
250   } else {
251
252     $soma_objects->{CPECollection}->deleteCPE($cpeid);
253     $norelease = 1;
254
255   }
256
257   $soma_objects->{CPECollection}->releaseCPE( $cpeid ) unless $norelease;
258
259   '';
260 }
261
262 sub _queueable_replace {
263   my( $soma_objects, $cpeid, %options ) = @_;
264
265   my $appid = $options{data_app_id} || '';
266
267   if (exists($options{data_app_id})) {
268     my $applications =
269       $soma_objects->{Applications}->getSubscribedApplications( $cpeid );
270
271     my $instance_id;
272     foreach $application (@$applications) {
273       next unless $application->internalName eq 'dataApplication';
274       if ($application->definitionId != $options{data_app_id}) {
275         $instance_id = $application->instanceId;
276         $soma_objects->{Applications}->unsubscribeApp( $cpeid, $instance_id );
277       }
278     }
279
280     if ($appid && !$instance_id ) {
281       my $application =
282         $soma_objects->{AppCatalog}
283                      ->getDefaultApplicationInstance($appid, $cpeid);
284
285       $soma_objects->{Applications}->subscribeApp( $cpeid, $application );
286     }
287
288   } else {
289
290     $soma_objects->{CPEAccess}->switchCPE($cpeid, $options{new_esn})
291       unless( $options{new_esn} eq $options{esn});
292
293   }
294
295   $soma_objects->{CPECollection}->releaseCPE( $cpeid );
296
297   '';
298 }
299
300 sub _queueable_suspend {
301   my( $soma_objects, $cpeid, %options ) = @_;
302
303   my $appid = $options{data_app_id};
304
305   if ($appid) {
306     my $applications =
307       $soma_objects->{Applications}->getSubscribedApplications( $cpeid );
308
309     my $instance_id;
310     foreach $application (@$applications) {
311       next unless $application->definitionId == $appid;
312
313       $instance_id = $application->instanceId;
314       my $app_def =
315         $soma_objects->{AppCatalog}->getApplicationDef($appid, $cpeid);
316       my @attr_def = grep { $_->internalName eq 'status' }
317                           @{$app_def->attributes};
318
319       foreach my $attribute ( @{$application->attributes} ) {
320         next unless $attribute->definitionId == $attr_def[0]->definitionId;
321         $attribute->{value} = 'S';  
322
323         $soma_objects->{Applications}->setAppAttribute( $cpeid,
324                                                         $instance_id,
325                                                         $attribute
326                                                       );
327       }
328       
329     }
330
331   } else {
332
333     #do nothing
334
335   }
336
337   $soma_objects->{CPECollection}->releaseCPE( $cpeid );
338
339   '';
340 }
341
342 sub _queueable_unsuspend {
343   my( $soma_objects, $cpeid, %options ) = @_;
344
345   my $appid = $options{data_app_id};
346
347   if ($appid) {
348     my $applications =
349       $soma_objects->{Applications}->getSubscribedApplications( $cpeid );
350
351     my $instance_id;
352     foreach $application (@$applications) {
353       next unless $application->definitionId == $appid;
354
355       $instance_id = $application->instanceId;
356       my $app_def =
357         $soma_objects->{AppCatalog}->getApplicationDef($appid, $cpeid);
358       my @attr_def = grep { $_->internalName eq 'status' }
359                      @{$app_def->attributes};
360
361       foreach my $attribute ( @{$application->attributes} ) {
362         next unless $attribute->definitionId == $attr_def[0]->definitionId;
363         $attribute->{value} = 'E';  
364
365         $soma_objects->{Applications}->setAppAttribute( $cpeid,
366                                                         $instance_id,
367                                                         $attribute
368                                                       );
369       }
370       
371     }
372
373   } else {
374
375     #do nothing
376
377   }
378
379   $soma_objects->{CPECollection}->releaseCPE( $cpeid );
380
381   '';
382 }
383
384 sub esn {
385   my ( $self, $svc ) = @_;
386   my $svcdb = $svc->cust_svc->part_svc->svcdb;
387
388   if ($svcdb eq 'svc_external') {
389     my $esn = $svc->title;
390     $esn =~ /^\s*([\da-fA-F]{1,16})\s*$/ && ($esn = $1);
391     return sprintf( '%016s', $esn );
392   }
393   
394   my $cust_pkg = $svc->cust_svc->cust_pkg;
395   return '' unless $cust_pkg;
396
397   my @cust_svc = grep { $_->part_svc->svcdb eq 'svc_external' &&
398                         scalar( $_->part_svc->part_export('soma') )
399                       }
400                  $cust_pkg->cust_svc;
401   return '' unless scalar(@cust_svc);
402   warn "part_export::soma found multiple ESNs for cust_svc ". $svc->svcnum
403     if scalar( @cust_svc ) > 1;
404
405   my $esn = $cust_svc[0]->svc_x->title;
406   $esn =~ /^\s*([\da-fA-F]{1,16})\s*$/ && ($esn = $1);
407   
408   sprintf( '%016s', $esn );
409 }
410
411
412 1;