1 package FS::part_export::saisei;
4 use vars qw( @ISA %info );
5 use base qw( FS::part_export );
6 use Date::Format 'time2str';
18 FS::part_export::saisei
22 Saisei integration for Freeside
26 This export offers basic svc_broadband provisioning for Saisei.
28 This is a customer integration with Saisei. This will set up a rate plan and tie
29 the rate plan to a host and the access point via the Saisei API when the broadband service is provisioned.
30 It will also untie the host from the rate plan, setting it to the default rate plan via the API upon unprovisioning of the broadband service.
32 This will create and modify the rate plans at Saisei as soon as the broadband service attached to this export is created or modified.
33 This will also create and modify an access point at Saisei as soon as the tower is created or modified.
35 To use this export, follow the below instructions:
37 Create a new service definition and set the table to svc_broadband. The service name will become the Saisei rate plan name.
38 Set the upload and download speed for the service. This is required to be able to export the service to Saisei.
39 Attach this Saisei export to this service.
41 Create a tower and add a sector to that tower. The sector name will be the name of the access point,
42 Make sure you have set the up and down rate limit for the tower and the sector. This is required to be able to export the access point.
43 The tower and sector will be set up as access points at Saisei upon the creation of the tower or sector. They will be modified at Saisei when modified in freeside.
44 Each sector will be attached to its tower access point using the Saisei uplink field.
46 Create a package for the above created service, and order this package for a customer.
48 Provision the service, making sure to enter the IP address associated with this service and select the tower and sector for it's access point.
49 This provisioned service will then be exported as a host to Saisei.
51 Unprovisioning this service will set the host entry at Saisei to the default rate plan with the user and access point set to <none>.
53 After this export is set up and attached to a service, you can export the already provisioned services by clicking the link Export provisioned services attached to this export.
54 Clicking on this link will export all services attached to this export not currently exported to Saisei.
56 This module also provides generic methods for working through the L</Saisei API>.
60 tie my %scripts, 'Tie::IxHash',
61 'export_provisioned_services' => { component => '/elements/popup_link.html',
62 label => 'Export provisioned services',
63 description => 'will export provisioned services of part service with Saisei export attached.',
64 html_label => '<b>Export provisioned services attached to this export.</b>',
68 tie my %options, 'Tie::IxHash',
69 'port' => { label => 'Port',
71 'username' => { label => 'Saisei API User Name',
73 'password' => { label => 'Saisei API Password',
75 'debug' => { type => 'checkbox',
76 label => 'Enable debug warnings' },
80 'svc' => 'svc_broadband',
81 'desc' => 'Export broadband service/account to Saisei',
82 'options' => \%options,
83 'scripts' => \%scripts,
85 This is a customer integration with Saisei. This will set up a rate plan and tie
86 the rate plan to a host and the access point via the Saisei API when the broadband service is provisioned.
87 It will also untie the host from the rate plan, setting it to the default rate plan via the API upon unprovisioning of the broadband service.
89 This will create and modify the rate plans at Saisei as soon as the broadband service attached to this export is created or modified.
90 This will also create and modify an access point at Saisei as soon as the tower is created or modified.
92 To use this export, follow the below instructions:
96 Create a new service definition and set the table to svc_broadband. The service name will become the Saisei rate plan name.
97 Set the upload speed, download speed, and tower to be required for the service. This is required to be able to export the service to Saisei.
98 Attach this Saisei export to this service.
102 Create a tower and add a sector to that tower. The sector name will be the name of the access point,
103 Make sure you have set the up and down rate limit for the tower and the sector. This is required to be able to export the access point.
104 The tower and sector will be set up as access points at Saisei upon the creation of the tower or sector. They will be modified at Saisei when modified in freeside.
105 Each sector will be attached to its tower access point using the Saisei uplink field.
109 Create a package for the above created service, and order this package for a customer.
113 Provision the service, making sure to enter the IP address associated with this service, the upload and download speed are correct, and select the tower and sector for it's access point.
114 This provisioned service will then be exported as a host to Saisei.
116 Unprovisioning this service will set the host entry at Saisei to the default rate plan with the user and access point set to <i>none</i>.
120 After this export is set up and attached to a service, you can export the already provisioned services by clicking the link <b>Export provisioned services attached to this export</b>.
121 Clicking on this link will export all services attached to this export not currently exported to Saisei.
125 <A HREF="http://www.freeside.biz/mediawiki/index.php/Saisei_provisioning_export" target="_new">Documentation</a>
130 my ($self, $svc_broadband) = @_;
132 my $rateplan_name = $self->get_rateplan_name($svc_broadband);
134 # check for existing rate plan
135 my $existing_rateplan;
136 $existing_rateplan = $self->api_get_rateplan($rateplan_name) unless $self->{'__saisei_error'};
138 # if no existing rate plan create one and modify it.
139 $self->api_create_rateplan($svc_broadband, $rateplan_name) unless $existing_rateplan;
140 $self->api_modify_rateplan($svc_broadband, $rateplan_name) unless ($self->{'__saisei_error'} || $existing_rateplan);
141 return $self->api_error if $self->{'__saisei_error'};
143 # set rateplan to existing one or newly created one.
144 my $rateplan = $existing_rateplan ? $existing_rateplan : $self->api_get_rateplan($rateplan_name);
146 my $username = $svc_broadband->{Hash}->{svcnum};
147 my $description = $svc_broadband->{Hash}->{description};
150 $self->{'__saisei_error'} = 'no username - can not export';
151 return $self->api_error;
154 # check for existing user.
156 $existing_user = $self->api_get_user($username) unless $self->{'__saisei_error'};
158 # if no existing user create one.
159 $self->api_create_user($username, $description) unless $existing_user;
160 return $self->api_error if $self->{'__saisei_error'};
162 # set user to existing one or newly created one.
163 my $user = $existing_user ? $existing_user : $self->api_get_user($username);
166 my $tower_sector = FS::Record::qsearchs({
167 'table' => 'tower_sector',
168 'select' => 'tower.towername,
169 tower.up_rate_limit as tower_upratelimit,
170 tower.down_rate_limit as tower_downratelimit,
171 tower_sector.sectorname,
172 tower_sector.up_rate_limit as sector_upratelimit,
173 tower_sector.down_rate_limit as sector_downratelimit ',
174 'addl_from' => 'LEFT JOIN tower USING ( towernum )',
176 'sectornum' => $svc_broadband->{Hash}->{sectornum},
180 my $tower_name = $tower_sector->{Hash}->{towername};
181 $tower_name =~ s/\s/_/g;
184 'tower_name' => $tower_name,
185 'tower_uprate_limit' => $tower_sector->{Hash}->{tower_upratelimit},
186 'tower_downrate_limit' => $tower_sector->{Hash}->{tower_downratelimit},
189 my $tower_ap = process_tower($self, $tower_opt);
190 return $self->api_error if $self->{'__saisei_error'};
192 my $sector_name = $tower_sector->{Hash}->{sectorname};
193 $sector_name =~ s/\s/_/g;
196 'tower_name' => $tower_name,
197 'sector_name' => $sector_name,
198 'sector_uprate_limit' => $tower_sector->{Hash}->{sector_upratelimit},
199 'sector_downrate_limit' => $tower_sector->{Hash}->{sector_downratelimit},
200 'rateplan' => $rateplan_name,
202 my $accesspoint = process_sector($self, $sector_opt);
203 return $self->api_error if $self->{'__saisei_error'};
205 ## get custnum and pkgpart from cust_pkg for virtual access point
206 my $cust_pkg = FS::Record::qsearchs({
207 'table' => 'cust_pkg',
208 'hashref' => { 'pkgnum' => $svc_broadband->{Hash}->{pkgnum}, },
210 my $virtual_ap_name = $cust_pkg->{Hash}->{custnum}.'_'.$cust_pkg->{Hash}->{pkgpart}.'_'.$svc_broadband->{Hash}->{speed_down}.'_'.$svc_broadband->{Hash}->{speed_up};
212 my $virtual_ap_opt = {
213 'virtual_name' => $virtual_ap_name,
214 'sector_name' => $sector_name,
215 'virtual_uprate_limit' => $svc_broadband->{Hash}->{speed_up},
216 'virtual_downrate_limit' => $svc_broadband->{Hash}->{speed_down},
218 my $virtual_ap = process_virtual_ap($self, $virtual_ap_opt);
219 return $self->api_error if $self->{'__saisei_error'};
221 ## tie host to user add sector name as access point.
222 $self->api_add_host_to_user(
223 $user->{collection}->[0]->{name},
224 $rateplan->{collection}->[0]->{name},
225 $svc_broadband->{Hash}->{ip_addr},
226 $virtual_ap->{collection}->[0]->{name},
227 ) unless $self->{'__saisei_error'};
230 return $self->api_error;
234 sub _export_replace {
235 my ($self, $svc_broadband) = @_;
236 my $error = $self->_export_insert($svc_broadband);
241 my ($self, $svc_broadband) = @_;
243 my $rateplan_name = $self->get_rateplan_name($svc_broadband);
245 my $username = $svc_broadband->{Hash}->{svcnum};
247 ## untie host to user
248 $self->api_delete_host_to_user($username, $rateplan_name, $svc_broadband->{Hash}->{ip_addr}) unless $self->{'__saisei_error'};
253 sub _export_suspend {
254 my ($self, $svc_broadband) = @_;
258 sub _export_unsuspend {
259 my ($self, $svc_broadband) = @_;
264 my ($self, $svc_part) = @_;
266 if ( $FS::svc_Common::noexport_hack ) {
267 carp 'export_partsvc() suppressed by noexport_hack'
268 if $self->option('debug');
273 if ($svc_part->{Hash}->{svc_broadband__speed_down} eq "down" || $svc_part->{Hash}->{svc_broadband__speed_up} eq "up") {
274 for my $type (qw( down up )) {
275 my $speed_type = "broadband_".$type."stream";
276 foreach my $pkg_svc (FS::Record::qsearch({
277 'table' => 'pkg_svc',
278 'select' => 'pkg_svc.*, part_pkg_fcc_option.fccoptionname, part_pkg_fcc_option.optionvalue',
279 'addl_from' => ' LEFT JOIN part_pkg_fcc_option USING (pkgpart) ',
280 'extra_sql' => " WHERE pkg_svc.svcpart = ".$svc_part->{Hash}->{svcpart}." AND pkg_svc.quantity > 0 AND part_pkg_fcc_option.fccoptionname = '".$speed_type."'",
281 })) { $fcc_477_speeds->{
282 $pkg_svc->{Hash}->{pkgpart}}->{$speed_type} = $pkg_svc->{Hash}->{optionvalue} * 1000 unless !$pkg_svc->{Hash}->{optionvalue}; }
286 $fcc_477_speeds->{1}->{broadband_downstream} = $svc_part->{Hash}->{"svc_broadband__speed_down"};
287 $fcc_477_speeds->{1}->{broadband_upstream} = $svc_part->{Hash}->{"svc_broadband__speed_up"};
290 foreach my $key (keys %$fcc_477_speeds) {
292 $svc_part->{Hash}->{speed_down} = $fcc_477_speeds->{$key}->{broadband_downstream};
293 $svc_part->{Hash}->{speed_up} = $fcc_477_speeds->{$key}->{broadband_upstream};
294 $svc_part->{Hash}->{svc_broadband__speed_down} = $fcc_477_speeds->{$key}->{broadband_downstream};
295 $svc_part->{Hash}->{svc_broadband__speed_up} = $fcc_477_speeds->{$key}->{broadband_upstream};
297 my $temp_svc = $svc_part->{Hash};
298 my $svc_broadband = {};
299 map { if ($_ =~ /^svc_broadband__(.*)$/) { $svc_broadband->{Hash}->{$1} = $temp_svc->{$_}; } } keys %$temp_svc;
301 my $rateplan_name = $self->get_rateplan_name($svc_broadband, $svc_part->{Hash}->{svc});
303 # check for existing rate plan
304 my $existing_rateplan;
305 $existing_rateplan = $self->api_get_rateplan($rateplan_name) unless $self->{'__saisei_error'};
307 # Modify the existing rate plan with new service data.
308 $self->api_modify_existing_rateplan($svc_broadband, $rateplan_name) unless ($self->{'__saisei_error'} || !$existing_rateplan);
310 # if no existing rate plan create one and modify it.
311 $self->api_create_rateplan($svc_broadband, $rateplan_name) unless $existing_rateplan;
312 $self->api_modify_rateplan($svc_part, $rateplan_name) unless ($self->{'__saisei_error'} || $existing_rateplan);
316 return $self->api_error;
320 sub export_tower_sector {
321 my ($self, $tower) = @_;
323 if ( $FS::svc_Common::noexport_hack ) {
324 carp 'export_tower_sector() suppressed by noexport_hack'
325 if $self->option('debug');
329 #modify tower or create it.
330 my $tower_name = $tower->{Hash}->{towername};
331 $tower_name =~ s/\s/_/g;
333 'tower_name' => $tower_name,
334 'tower_uprate_limit' => $tower->{Hash}->{up_rate_limit},
335 'tower_downrate_limit' => $tower->{Hash}->{down_rate_limit},
336 'modify_existing' => '1', # modify an existing access point with this info
339 my $tower_access_point = process_tower($self, $tower_opt);
340 return $tower_access_point if $tower_access_point->{error};
342 #get list of all access points
344 'table' => 'tower_sector',
346 'hashref' => { 'towernum' => $tower->{Hash}->{towernum}, },
349 #for each one modify or create it.
350 foreach my $tower_sector ( FS::Record::qsearch($hash_opt) ) {
351 my $sector_name = $tower_sector->{Hash}->{sectorname};
352 $sector_name =~ s/\s/_/g;
354 'tower_name' => $tower_name,
355 'sector_name' => $sector_name,
356 'sector_uprate_limit' => $tower_sector->{Hash}->{up_rate_limit},
357 'sector_downrate_limit' => $tower_sector->{Hash}->{down_rate_limit},
358 'modify_existing' => '1', # modify an existing access point with this info
360 my $sector_access_point = process_sector($self, $sector_opt) unless ($sector_name eq "_default");
361 return $sector_access_point if $sector_access_point->{error};
364 return { error => $self->api_error, };
367 ## creates the rateplan name
368 sub get_rateplan_name {
369 my ($self, $svc_broadband, $svc_name) = @_;
371 my $service_part = FS::Record::qsearchs( 'part_svc', { 'svcpart' => $svc_broadband->{Hash}->{svcpart} } ) unless $svc_name;
372 my $service_name = $svc_name ? $svc_name : $service_part->{Hash}->{svc};
374 my $rateplan_name = $service_name . " " . $svc_broadband->{Hash}->{speed_down} . "-" . $svc_broadband->{Hash}->{speed_up};
375 $rateplan_name =~ s/\s/_/g; $rateplan_name =~ s/[^A-Za-z0-9\-_]//g;
377 return $rateplan_name;
382 These methods allow access to the Saisei API using the credentials
383 set in the export options.
389 Accepts I<$method>, I<$path>, I<$params> hashref and optional.
390 Places an api call to the specified path and method with the specified params.
391 Returns the decoded json object returned by the api call.
392 Returns empty on failure; retrieve error messages using L</api_error>.
397 my ($self,$method,$path,$params) = @_;
399 $self->{'__saisei_error'} = '';
400 my $auth_info = $self->option('username') . ':' . $self->option('password');
403 warn "Calling $method on http://"
404 .$self->{Hash}->{machine}.':'.$self->option('port')
405 ."/rest/top/configurations/running/$path\n" if $self->option('debug');
407 my $data = encode_json($params) if keys %{ $params };
409 my $client = REST::Client->new();
410 $client->addHeader("Authorization", "Basic ".encode_base64($auth_info));
411 $client->setHost('http://'.$self->{Hash}->{machine}.':'.$self->option('port'));
412 $client->$method('/rest/top/configurations/running'.$path, $data, { "Content-type" => 'application/json'});
414 warn "Saisei Response Code is ".$client->responseCode()."\n" if $self->option('debug');
418 if ($client->responseCode() eq '200' || $client->responseCode() eq '201') {
419 eval { $result = decode_json($client->responseContent()) };
421 $self->{'__saisei_error'} = "There was an error decoding the JSON data from Saisei. Bad JSON data logged in error log if debug option was set.";
422 warn "Saisei RC 201 Response Content is not json\n".$client->responseContent()."\n" if $self->option('debug');
426 elsif ($client->responseCode() eq '404') {
427 eval { $result = decode_json($client->responseContent()) };
429 $self->{'__saisei_error'} = "There was an error decoding the JSON data from Saisei. Bad JSON data logged in error log if debug option was set.";
430 warn "Saisei RC 404 Response Content is not json\n".$client->responseContent()."\n" if $self->option('debug');
433 ## check if message is for empty hash.
434 my($does_not_exist) = $result->{message} =~ /'(.*)' does not exist$/;
435 $self->{'__saisei_error'} = "Saisei Error: ".$result->{message} unless $does_not_exist;
436 warn "Saisei Response Content is\n".$client->responseContent."\n" if ($self->option('debug') && !$does_not_exist);
439 elsif ($client->responseCode() eq '500') {
440 $self->{'__saisei_error'} = "Could not connect to the host (".$self->{Hash}->{machine}.':'.$self->option('port').") during $method , we received the responce code: " . $client->responseCode();
441 warn "Saisei Response Content is\n".$client->responseContent."\n" if $self->option('debug');
445 $self->{'__saisei_error'} = "Received Bad response from server during $method , we received responce code: " . $client->responseCode();
446 warn "Saisei Response Content is\n".$client->responseContent."\n" if $self->option('debug');
456 Returns the error string set by L</Saisei API> methods,
457 or a blank string if most recent call produced no errors.
463 return $self->{'__saisei_error'} || '';
466 =head2 api_get_policies
468 Gets a list of global policies.
472 sub api_get_policies {
475 my $get_policies = $self->api_call("GET", '/policies/?token=1&order=name&start=0&limit=20&select=name%2Cpercent_rate%2Cassured%2C');
476 return if $self->api_error;
477 $self->{'__saisei_error'} = "Did not receive any global policies from Saisei."
478 unless $get_policies;
480 return $get_policies->{collection};
483 =head2 api_get_rateplan
485 Gets rateplan info for specific rateplan.
489 sub api_get_rateplan {
491 my $rateplan = shift;
493 my $get_rateplan = $self->api_call("GET", "/rate_plans/$rateplan");
494 return if $self->api_error;
496 return $get_rateplan;
501 Gets user info for specific user.
509 my $get_user = $self->api_call("GET", "/users/$user");
510 return if $self->api_error;
515 =head2 api_get_accesspoint
517 Gets user info for specific access point.
521 sub api_get_accesspoint {
523 my $accesspoint = shift;
525 my $get_accesspoint = $self->api_call("GET", "/access_points/$accesspoint");
526 return if $self->api_error;
528 return $get_accesspoint;
533 Gets user info for specific host.
541 my $get_host = $self->api_call("GET", "/hosts/$ip");
543 return { message => $self->api_error, } if $self->api_error;
548 =head2 api_create_rateplan
554 sub api_create_rateplan {
555 my ($self, $svc, $rateplan) = @_;
557 $self->{'__saisei_error'} = "There is no download speed set for the service $rateplan with host (".$svc->{Hash}->{ip_addr}."). All services that are to be exported to Saisei need to have a download speed set for them." if !$svc->{Hash}->{speed_down};
558 $self->{'__saisei_error'} = "There is no upload speed set for the service $rateplan with host (".$svc->{Hash}->{ip_addr}."). All services that are to be exported to Saisei need to have a upload speed set for them." if !$svc->{Hash}->{speed_up};
560 my $new_rateplan = $self->api_call(
562 "/rate_plans/$rateplan",
564 'downstream_rate' => $svc->{Hash}->{speed_down},
565 'upstream_rate' => $svc->{Hash}->{speed_up},
567 ) unless $self->{'__saisei_error'};
569 $self->{'__saisei_error'} = "Saisei could not create the rate plan $rateplan."
570 unless ($new_rateplan || $self->{'__saisei_error'});
572 return $new_rateplan;
576 =head2 api_modify_rateplan
578 Modify a new rateplan.
582 sub api_modify_rateplan {
583 my ($self,$svc,$rateplan_name) = @_;
586 my $policies = $self->api_get_policies();
588 foreach my $policy (@$policies) {
589 my $policyname = $policy->{name};
590 my $rate_multiplier = '';
591 if ($policy->{background}) { $rate_multiplier = ".01"; }
592 my $modified_rateplan = $self->api_call(
594 "/rate_plans/$rateplan_name/partitions/$policyname",
596 'restricted' => $policy->{assured}, # policy_assured_flag
597 'rate_multiplier' => $rate_multiplier, # policy_background 0.1
598 'rate' => $policy->{percent_rate}, # policy_percent_rate
602 $self->{'__saisei_error'} = "Saisei could not modify the rate plan $rateplan_name after it was created."
603 unless ($modified_rateplan || $self->{'__saisei_error'}); # should never happen
611 =head2 api_modify_existing_rateplan
613 Modify a existing rateplan.
617 sub api_modify_existing_rateplan {
618 my ($self,$svc,$rateplan_name) = @_;
620 my $modified_rateplan = $self->api_call(
622 "/rate_plans/$rateplan_name",
624 'downstream_rate' => $svc->{Hash}->{speed_down},
625 'upstream_rate' => $svc->{Hash}->{speed_up},
629 $self->{'__saisei_error'} = "Saisei could not modify the rate plan $rateplan_name."
630 unless ($modified_rateplan || $self->{'__saisei_error'}); # should never happen
636 =head2 api_create_user
642 sub api_create_user {
643 my ($self,$user, $description) = @_;
645 my $new_user = $self->api_call(
649 'description' => $description,
653 $self->{'__saisei_error'} = "Saisei could not create the user $user"
654 unless ($new_user || $self->{'__saisei_error'}); # should never happen
660 =head2 api_create_accesspoint
662 Creates a access point.
666 sub api_create_accesspoint {
667 my ($self,$accesspoint, $upratelimit, $downratelimit) = @_;
669 my $new_accesspoint = $self->api_call(
671 "/access_points/$accesspoint",
673 'downstream_rate_limit' => $downratelimit,
674 'upstream_rate_limit' => $upratelimit,
678 $self->{'__saisei_error'} = "Saisei could not create the access point $accesspoint"
679 unless ($new_accesspoint || $self->{'__saisei_error'}); # should never happen
684 =head2 api_modify_accesspoint
686 Modify a new access point.
690 sub api_modify_accesspoint {
691 my ($self, $accesspoint, $uplink) = @_;
693 my $modified_accesspoint = $self->api_call(
695 "/access_points/$accesspoint",
697 'uplink' => $uplink, # name of attached access point
701 $self->{'__saisei_error'} = "Saisei could not modify the access point $accesspoint after it was created."
702 unless ($modified_accesspoint || $self->{'__saisei_error'}); # should never happen
708 =head2 api_modify_existing_accesspoint
710 Modify a existing accesspoint.
714 sub api_modify_existing_accesspoint {
715 my ($self, $accesspoint, $uplink, $upratelimit, $downratelimit) = @_;
717 my $modified_accesspoint = $self->api_call(
719 "/access_points/$accesspoint",
721 'downstream_rate_limit' => $downratelimit,
722 'upstream_rate_limit' => $upratelimit,
723 # 'uplink' => $uplink, # name of attached access point
727 $self->{'__saisei_error'} = "Saisei could not modify the access point $accesspoint."
728 unless ($modified_accesspoint || $self->{'__saisei_error'}); # should never happen
734 =head2 api_add_host_to_user
736 ties host to user, rateplan and default access point.
740 sub api_add_host_to_user {
741 my ($self,$user, $rateplan, $ip, $accesspoint) = @_;
743 my $new_host = $self->api_call(
748 'rate_plan' => $rateplan,
749 'access_point' => $accesspoint,
753 $self->{'__saisei_error'} = "Saisei could not create the host $ip"
754 unless ($new_host || $self->{'__saisei_error'}); # should never happen
760 =head2 api_delete_host_to_user
762 unties host from user and rateplan.
763 this will set the host entry at Saisei to the default rate plan with the user and access point set to <none>.
767 sub api_delete_host_to_user {
768 my ($self,$user, $rateplan, $ip) = @_;
770 my $default_rate_plan = $self->api_call("GET", '?token=1&select=default_rate_plan');
771 return if $self->api_error;
772 $self->{'__saisei_error'} = "Can not delete the host as Saisei did not return a default rate plan. Please make sure Saisei has a default rateplan setup."
773 unless $default_rate_plan;
775 my $default_rateplan_name = $default_rate_plan->{collection}->[0]->{default_rate_plan}->{link}->{name};
777 my $delete_host = $self->api_call(
782 'access_point' => '<none>',
783 'rate_plan' => $default_rateplan_name,
787 $self->{'__saisei_error'} = "Saisei could not delete the host $ip"
788 unless ($delete_host || $self->{'__saisei_error'}); # should never happen
795 my ($self, $opt) = @_;
797 if (!$opt->{tower_uprate_limit} || !$opt->{tower_downrate_limit}) {
798 $self->{'__saisei_error'} = "Could not export tower ".$opt->{tower_name}." because there was no up or down rates attached to the tower. Saisei requires a up and down rate be attached to each tower.";
799 return { error => $self->api_error, };
802 my $existing_tower_ap;
803 my $tower_name = $opt->{tower_name};
805 #check if tower has been set up as an access point.
806 $existing_tower_ap = $self->api_get_accesspoint($tower_name) unless $self->{'__saisei_error'};
808 # modify the existing accesspoint if changing tower .
809 $self->api_modify_existing_accesspoint (
811 '', # tower does not have a uplink on sectors.
812 $opt->{tower_uprate_limit},
813 $opt->{tower_downrate_limit},
814 ) if $existing_tower_ap->{collection} && $opt->{modify_existing};
816 #if tower does not exist as an access point create it.
817 $self->api_create_accesspoint(
819 $opt->{tower_uprate_limit},
820 $opt->{tower_downrate_limit},
821 ) unless $existing_tower_ap->{collection};
823 my $accesspoint = $self->api_get_accesspoint($tower_name);
825 return { error => $self->api_error, } if $self->api_error;
830 my ($self, $opt) = @_;
832 if (!$opt->{sector_name} || $opt->{sector_name} eq '_default') {
833 $self->{'__saisei_error'} = "No sector attached to Tower (".$opt->{tower_name}.") for service ".$opt->{'rateplan'}.". Saisei requires a tower sector to be attached to each service that is exported to Saisei.";
834 return { error => $self->api_error, };
837 if (!$opt->{sector_uprate_limit} || !$opt->{sector_downrate_limit}) {
838 $self->{'__saisei_error'} = "Could not export sector ".$opt->{tower_name}." because there was no up or down rates attached to the sector. Saisei requires a up and down rate be attached to each sector.";
839 return { error => $self->api_error, };
842 my $existing_sector_ap;
843 my $sector_name = $opt->{sector_name};
845 #check if sector has been set up as an access point.
846 $existing_sector_ap = $self->api_get_accesspoint($sector_name);
848 # modify the existing accesspoint if changing sector .
849 $self->api_modify_existing_accesspoint (
852 $opt->{sector_uprate_limit},
853 $opt->{sector_downrate_limit},
854 ) if $existing_sector_ap && $opt->{modify_existing};
856 #if sector does not exist as an access point create it.
857 $self->api_create_accesspoint(
859 $opt->{sector_uprate_limit},
860 $opt->{sector_downrate_limit},
861 ) unless $existing_sector_ap;
863 # Attach newly created sector to it's tower.
864 $self->api_modify_accesspoint($sector_name, $opt->{tower_name}) unless ($self->{'__saisei_error'} || $existing_sector_ap);
866 # set access point to existing one or newly created one.
867 my $accesspoint = $existing_sector_ap ? $existing_sector_ap : $self->api_get_accesspoint($sector_name);
869 return { error => $self->api_error, } if $self->api_error;
873 =head2 require_tower_and_sector
875 sets whether the service export requires a sector with it's tower.
879 sub require_tower_and_sector {
883 sub required_fields {
884 my @fields = ('svc_broadband__ip_addr_required', 'svc_broadband__speed_up_required', 'svc_broadband__speed_down_required', 'svc_broadband__sectornum_required');
888 sub process_virtual_ap {
889 my ($self, $opt) = @_;
891 my $existing_virtual_ap;
892 my $virtual_name = $opt->{virtual_name};
894 #check if virtual_ap has been set up as an access point.
895 $existing_virtual_ap = $self->api_get_accesspoint($virtual_name);
897 # modify the existing virtual accesspoint if changing it. this should never happen
898 $self->api_modify_existing_accesspoint (
901 $opt->{virtual_uprate_limit},
902 $opt->{virtual_downrate_limit},
903 ) if $existing_virtual_ap && $opt->{modify_existing};
905 #if virtual ap does not exist as an access point create it.
906 $self->api_create_accesspoint(
908 $opt->{virtual_uprate_limit},
909 $opt->{virtual_downrate_limit},
910 ) unless $existing_virtual_ap;
913 if ($existing_virtual_ap && (ref $existing_virtual_ap->{collection}->[0]->{uplink} eq "HASH") && ($existing_virtual_ap->{collection}->[0]->{uplink}->{link}->{name} ne $opt->{sector_name})) {
917 # Attach newly created virtual ap to tower sector ap or if sector has changed.
918 $self->api_modify_accesspoint($virtual_name, $opt->{sector_name}) unless ($self->{'__saisei_error'} || ($existing_virtual_ap && !$update_sector));
920 # set access point to existing one or newly created one.
921 my $accesspoint = $existing_virtual_ap ? $existing_virtual_ap : $self->api_get_accesspoint($virtual_name);
926 sub export_provisioned_services {
930 my $part_export = FS::Record::qsearchs('part_export', { 'exportnum' => $param->{export_provisioned_services_exportnum}, } )
931 or die "You are trying to use an unknown exportnum $param->{export_provisioned_services_exportnum}. This export does not exist.\n";
934 my @svcparts = FS::Record::qsearch({
935 'table' => 'export_svc',
936 'addl_from' => 'LEFT JOIN part_svc USING ( svcpart ) ',
937 'hashref' => { 'exportnum' => $param->{export_provisioned_services_exportnum}, },
939 my $part_count = scalar @svcparts;
941 my $parts = join "', '", map { $_->{Hash}->{svcpart} } @svcparts;
943 my @svcs = FS::Record::qsearch({
944 'table' => 'cust_svc',
945 'addl_from' => 'LEFT JOIN svc_broadband USING ( svcnum ) ',
946 'extra_sql' => " WHERE svcpart in ('".$parts."')",
949 my $svc_count = scalar @svcs;
952 for (my $c=1; $c <=100; $c=$c+1) { $status{int($svc_count * ($c/100))} = $c; }
955 foreach my $svc (@svcs) {
956 if ($status{$process_count}) { my $s = $status{$process_count}; $job->update_statustext($s); }
957 ## check if service exists as host if not export it.
958 my $host = api_get_host($part_export, $svc->{Hash}->{ip_addr});
959 die ("Please double check your credentials as ".$host->{message}."\n") if $host->{message};
960 warn "Exporting service ".$svc->{Hash}->{ip_addr}."\n" if ($part_export->option('debug'));
961 my $export_error = _export_insert($part_export,$svc) unless $host->{collection};
963 warn "Error exporting service ".$svc->{Hash}->{ip_addr}."\n" if ($part_export->option('debug'));
964 die ("$export_error\n");