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 and download speed 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 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.
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},
201 my $accesspoint = process_sector($self, $sector_opt);
202 return $self->api_error if $self->{'__saisei_error'};
204 ## get custnum and pkgpart from cust_pkg for virtual access point
205 my $cust_pkg = FS::Record::qsearchs({
206 'table' => 'cust_pkg',
207 'hashref' => { 'pkgnum' => $svc_broadband->{Hash}->{pkgnum}, },
209 my $virtual_ap_name = $cust_pkg->{Hash}->{custnum}.'_'.$cust_pkg->{Hash}->{pkgpart}.'_'.$svc_broadband->{Hash}->{speed_down}.'_'.$svc_broadband->{Hash}->{speed_up};
211 my $virtual_ap_opt = {
212 'virtual_name' => $virtual_ap_name,
213 'sector_name' => $sector_name,
214 'virtual_uprate_limit' => $svc_broadband->{Hash}->{speed_up},
215 'virtual_downrate_limit' => $svc_broadband->{Hash}->{speed_down},
217 my $virtual_ap = process_virtual_ap($self, $virtual_ap_opt);
218 return $self->api_error if $self->{'__saisei_error'};
220 ## tie host to user add sector name as access point.
221 $self->api_add_host_to_user(
222 $user->{collection}->[0]->{name},
223 $rateplan->{collection}->[0]->{name},
224 $svc_broadband->{Hash}->{ip_addr},
225 $virtual_ap->{collection}->[0]->{name},
226 ) unless $self->{'__saisei_error'};
229 return $self->api_error;
233 sub _export_replace {
234 my ($self, $svc_broadband) = @_;
235 my $error = $self->_export_insert($svc_broadband);
240 my ($self, $svc_broadband) = @_;
242 my $rateplan_name = $self->get_rateplan_name($svc_broadband);
244 my $username = $svc_broadband->{Hash}->{svcnum};
246 ## untie host to user
247 $self->api_delete_host_to_user($username, $rateplan_name, $svc_broadband->{Hash}->{ip_addr}) unless $self->{'__saisei_error'};
252 sub _export_suspend {
253 my ($self, $svc_broadband) = @_;
257 sub _export_unsuspend {
258 my ($self, $svc_broadband) = @_;
263 my ($self, $svc_part) = @_;
265 if ( $FS::svc_Common::noexport_hack ) {
266 carp 'export_partsvc() suppressed by noexport_hack'
267 if $self->option('debug');
272 if ($svc_part->{Hash}->{svc_broadband__speed_down} eq "down" || $svc_part->{Hash}->{svc_broadband__speed_up} eq "up") {
273 for my $type (qw( down up )) {
274 my $speed_type = "broadband_".$type."stream";
275 foreach my $pkg_svc (FS::Record::qsearch({
276 'table' => 'pkg_svc',
277 'select' => 'pkg_svc.*, part_pkg_fcc_option.fccoptionname, part_pkg_fcc_option.optionvalue',
278 'addl_from' => ' LEFT JOIN part_pkg_fcc_option USING (pkgpart) ',
279 'extra_sql' => " WHERE pkg_svc.svcpart = ".$svc_part->{Hash}->{svcpart}." AND pkg_svc.quantity > 0 AND part_pkg_fcc_option.fccoptionname = '".$speed_type."'",
280 })) { $fcc_477_speeds->{
281 $pkg_svc->{Hash}->{pkgpart}}->{$speed_type} = $pkg_svc->{Hash}->{optionvalue} * 1000 unless !$pkg_svc->{Hash}->{optionvalue}; }
285 $fcc_477_speeds->{1}->{broadband_downstream} = $svc_part->{Hash}->{"svc_broadband__speed_down"};
286 $fcc_477_speeds->{1}->{broadband_upstream} = $svc_part->{Hash}->{"svc_broadband__speed_up"};
289 foreach my $key (keys %$fcc_477_speeds) {
291 $svc_part->{Hash}->{speed_down} = $fcc_477_speeds->{$key}->{broadband_downstream};
292 $svc_part->{Hash}->{speed_up} = $fcc_477_speeds->{$key}->{broadband_upstream};
293 $svc_part->{Hash}->{svc_broadband__speed_down} = $fcc_477_speeds->{$key}->{broadband_downstream};
294 $svc_part->{Hash}->{svc_broadband__speed_up} = $fcc_477_speeds->{$key}->{broadband_upstream};
296 my $temp_svc = $svc_part->{Hash};
297 my $svc_broadband = {};
298 map { if ($_ =~ /^svc_broadband__(.*)$/) { $svc_broadband->{Hash}->{$1} = $temp_svc->{$_}; } } keys %$temp_svc;
300 my $rateplan_name = $self->get_rateplan_name($svc_broadband, $svc_part->{Hash}->{svc});
302 # check for existing rate plan
303 my $existing_rateplan;
304 $existing_rateplan = $self->api_get_rateplan($rateplan_name) unless $self->{'__saisei_error'};
306 # Modify the existing rate plan with new service data.
307 $self->api_modify_existing_rateplan($svc_broadband, $rateplan_name) unless ($self->{'__saisei_error'} || !$existing_rateplan);
309 # if no existing rate plan create one and modify it.
310 $self->api_create_rateplan($svc_broadband, $rateplan_name) unless $existing_rateplan;
311 $self->api_modify_rateplan($svc_part, $rateplan_name) unless ($self->{'__saisei_error'} || $existing_rateplan);
315 return $self->api_error;
319 sub export_tower_sector {
320 my ($self, $tower) = @_;
322 if ( $FS::svc_Common::noexport_hack ) {
323 carp 'export_tower_sector() suppressed by noexport_hack'
324 if $self->option('debug');
328 #modify tower or create it.
329 my $tower_name = $tower->{Hash}->{towername};
330 $tower_name =~ s/\s/_/g;
332 'tower_name' => $tower_name,
333 'tower_uprate_limit' => $tower->{Hash}->{up_rate_limit},
334 'tower_downrate_limit' => $tower->{Hash}->{down_rate_limit},
335 'modify_existing' => '1', # modify an existing access point with this info
338 my $tower_access_point = process_tower($self, $tower_opt);
339 return $tower_access_point if $tower_access_point->{error};
341 #get list of all access points
343 'table' => 'tower_sector',
345 'hashref' => { 'towernum' => $tower->{Hash}->{towernum}, },
348 #for each one modify or create it.
349 foreach my $tower_sector ( FS::Record::qsearch($hash_opt) ) {
350 my $sector_name = $tower_sector->{Hash}->{sectorname};
351 $sector_name =~ s/\s/_/g;
353 'tower_name' => $tower_name,
354 'sector_name' => $sector_name,
355 'sector_uprate_limit' => $tower_sector->{Hash}->{up_rate_limit},
356 'sector_downrate_limit' => $tower_sector->{Hash}->{down_rate_limit},
357 'modify_existing' => '1', # modify an existing access point with this info
359 my $sector_access_point = process_sector($self, $sector_opt) unless ($sector_name eq "_default");
360 return $sector_access_point if $sector_access_point->{error};
363 return { error => $self->api_error, };
366 ## creates the rateplan name
367 sub get_rateplan_name {
368 my ($self, $svc_broadband, $svc_name) = @_;
370 my $service_part = FS::Record::qsearchs( 'part_svc', { 'svcpart' => $svc_broadband->{Hash}->{svcpart} } ) unless $svc_name;
371 my $service_name = $svc_name ? $svc_name : $service_part->{Hash}->{svc};
373 my $rateplan_name = $service_name . " " . $svc_broadband->{Hash}->{speed_down} . "-" . $svc_broadband->{Hash}->{speed_up};
374 $rateplan_name =~ s/\s/_/g; $rateplan_name =~ s/[^A-Za-z0-9\-_]//g;
376 return $rateplan_name;
381 These methods allow access to the Saisei API using the credentials
382 set in the export options.
388 Accepts I<$method>, I<$path>, I<$params> hashref and optional.
389 Places an api call to the specified path and method with the specified params.
390 Returns the decoded json object returned by the api call.
391 Returns empty on failure; retrieve error messages using L</api_error>.
396 my ($self,$method,$path,$params) = @_;
398 $self->{'__saisei_error'} = '';
399 my $auth_info = $self->option('username') . ':' . $self->option('password');
402 warn "Calling $method on http://"
403 .$self->{Hash}->{machine}.':'.$self->option('port')
404 ."/rest/top/configurations/running/$path\n" if $self->option('debug');
406 my $data = encode_json($params) if keys %{ $params };
408 my $client = REST::Client->new();
409 $client->addHeader("Authorization", "Basic ".encode_base64($auth_info));
410 $client->setHost('http://'.$self->{Hash}->{machine}.':'.$self->option('port'));
411 $client->$method('/rest/top/configurations/running'.$path, $data, { "Content-type" => 'application/json'});
413 warn "Saisei Response Code is ".$client->responseCode()."\n" if $self->option('debug');
417 if ($client->responseCode() eq '200' || $client->responseCode() eq '201') {
418 eval { $result = decode_json($client->responseContent()) };
420 $self->{'__saisei_error'} = "Error decoding json from Saisei";
421 warn "Saisei RC 201 Response Content is not json\n".$client->responseContent()."\n" if $self->option('debug');
425 elsif ($client->responseCode() eq '404') {
426 eval { $result = decode_json($client->responseContent()) };
428 $self->{'__saisei_error'} = "Error decoding json from Saisei";
429 warn "Saisei RC 404 Response Content is not json\n".$client->responseContent()."\n" if $self->option('debug');
432 ## check if message is for empty hash.
433 my($does_not_exist) = $result->{message} =~ /'(.*)' does not exist$/;
434 $self->{'__saisei_error'} = "Error ".$result->{message} unless $does_not_exist;
435 warn "Saisei Response Content is\n".$client->responseContent."\n" if ($self->option('debug') && !$does_not_exist);
438 elsif ($client->responseCode() eq '500') {
439 $self->{'__saisei_error'} = "Can't connect to host during $method , received responce code: " . $client->responseCode() . " and message: " . $client->responseContent();
440 warn "Saisei Response Content is\n".$client->responseContent."\n" if $self->option('debug');
444 $self->{'__saisei_error'} = "Bad response from server during $method , received responce code: " . $client->responseCode() . " and message: " . $client->responseContent();
445 warn "Saisei Response Content is\n".$client->responseContent."\n" if $self->option('debug');
455 Returns the error string set by L</Saisei API> methods,
456 or a blank string if most recent call produced no errors.
462 return $self->{'__saisei_error'} || '';
465 =head2 api_get_policies
467 Gets a list of global policies.
471 sub api_get_policies {
474 my $get_policies = $self->api_call("GET", '/policies/?token=1&order=name&start=0&limit=20&select=name%2Cpercent_rate%2Cassured%2C');
475 return if $self->api_error;
476 $self->{'__saisei_error'} = "Did not receive any global policies"
477 unless $get_policies;
479 return $get_policies->{collection};
482 =head2 api_get_rateplan
484 Gets rateplan info for specific rateplan.
488 sub api_get_rateplan {
490 my $rateplan = shift;
492 my $get_rateplan = $self->api_call("GET", "/rate_plans/$rateplan");
493 return if $self->api_error;
495 return $get_rateplan;
500 Gets user info for specific user.
508 my $get_user = $self->api_call("GET", "/users/$user");
509 return if $self->api_error;
514 =head2 api_get_accesspoint
516 Gets user info for specific access point.
520 sub api_get_accesspoint {
522 my $accesspoint = shift;
524 my $get_accesspoint = $self->api_call("GET", "/access_points/$accesspoint");
525 return if $self->api_error;
527 return $get_accesspoint;
532 Gets user info for specific host.
540 my $get_host = $self->api_call("GET", "/hosts/$ip");
542 return { message => $self->api_error, } if $self->api_error;
547 =head2 api_create_rateplan
553 sub api_create_rateplan {
554 my ($self, $svc, $rateplan) = @_;
556 $self->{'__saisei_error'} = "No downrate listed for service $rateplan" if !$svc->{Hash}->{speed_down};
557 $self->{'__saisei_error'} = "No uprate listed for service $rateplan" if !$svc->{Hash}->{speed_up};
559 my $new_rateplan = $self->api_call(
561 "/rate_plans/$rateplan",
563 'downstream_rate' => $svc->{Hash}->{speed_down},
564 'upstream_rate' => $svc->{Hash}->{speed_up},
566 ) unless $self->{'__saisei_error'};
568 $self->{'__saisei_error'} = "Rate Plan not created"
569 unless ($new_rateplan || $self->{'__saisei_error'});
571 return $new_rateplan;
575 =head2 api_modify_rateplan
577 Modify a new rateplan.
581 sub api_modify_rateplan {
582 my ($self,$svc,$rateplan_name) = @_;
585 my $policies = $self->api_get_policies();
587 foreach my $policy (@$policies) {
588 my $policyname = $policy->{name};
589 my $rate_multiplier = '';
590 if ($policy->{background}) { $rate_multiplier = ".01"; }
591 my $modified_rateplan = $self->api_call(
593 "/rate_plans/$rateplan_name/partitions/$policyname",
595 'restricted' => $policy->{assured}, # policy_assured_flag
596 'rate_multiplier' => $rate_multiplier, # policy_background 0.1
597 'rate' => $policy->{percent_rate}, # policy_percent_rate
601 $self->{'__saisei_error'} = "Rate Plan not modified after create"
602 unless ($modified_rateplan || $self->{'__saisei_error'}); # should never happen
610 =head2 api_modify_existing_rateplan
612 Modify a existing rateplan.
616 sub api_modify_existing_rateplan {
617 my ($self,$svc,$rateplan_name) = @_;
619 my $modified_rateplan = $self->api_call(
621 "/rate_plans/$rateplan_name",
623 'downstream_rate' => $svc->{Hash}->{speed_down},
624 'upstream_rate' => $svc->{Hash}->{speed_up},
628 $self->{'__saisei_error'} = "Rate Plan not modified"
629 unless ($modified_rateplan || $self->{'__saisei_error'}); # should never happen
635 =head2 api_create_user
641 sub api_create_user {
642 my ($self,$user, $description) = @_;
644 my $new_user = $self->api_call(
648 'description' => $description,
652 $self->{'__saisei_error'} = "User not created"
653 unless ($new_user || $self->{'__saisei_error'}); # should never happen
659 =head2 api_create_accesspoint
661 Creates a access point.
665 sub api_create_accesspoint {
666 my ($self,$accesspoint, $upratelimit, $downratelimit) = @_;
668 my $new_accesspoint = $self->api_call(
670 "/access_points/$accesspoint",
672 'downstream_rate_limit' => $downratelimit,
673 'upstream_rate_limit' => $upratelimit,
677 $self->{'__saisei_error'} = "Access point not created"
678 unless ($new_accesspoint || $self->{'__saisei_error'}); # should never happen
683 =head2 api_modify_accesspoint
685 Modify a new access point.
689 sub api_modify_accesspoint {
690 my ($self, $accesspoint, $uplink) = @_;
692 my $modified_accesspoint = $self->api_call(
694 "/access_points/$accesspoint",
696 'uplink' => $uplink, # name of attached access point
700 $self->{'__saisei_error'} = "Rate Plan not modified"
701 unless ($modified_accesspoint || $self->{'__saisei_error'}); # should never happen
707 =head2 api_modify_existing_accesspoint
709 Modify a existing accesspoint.
713 sub api_modify_existing_accesspoint {
714 my ($self, $accesspoint, $uplink, $upratelimit, $downratelimit) = @_;
716 my $modified_accesspoint = $self->api_call(
718 "/access_points/$accesspoint",
720 'downstream_rate_limit' => $downratelimit,
721 'upstream_rate_limit' => $upratelimit,
722 # 'uplink' => $uplink, # name of attached access point
726 $self->{'__saisei_error'} = "Access point not modified"
727 unless ($modified_accesspoint || $self->{'__saisei_error'}); # should never happen
733 =head2 api_add_host_to_user
735 ties host to user, rateplan and default access point.
739 sub api_add_host_to_user {
740 my ($self,$user, $rateplan, $ip, $accesspoint) = @_;
742 my $new_host = $self->api_call(
747 'rate_plan' => $rateplan,
748 'access_point' => $accesspoint,
752 $self->{'__saisei_error'} = "Host not created"
753 unless ($new_host || $self->{'__saisei_error'}); # should never happen
759 =head2 api_delete_host_to_user
761 unties host from user and rateplan.
762 this will set the host entry at Saisei to the default rate plan with the user and access point set to <none>.
766 sub api_delete_host_to_user {
767 my ($self,$user, $rateplan, $ip) = @_;
769 my $default_rate_plan = $self->api_call("GET", '?token=1&select=default_rate_plan');
770 return if $self->api_error;
771 $self->{'__saisei_error'} = "Did not receive a default rate plan"
772 unless $default_rate_plan;
774 my $default_rateplan_name = $default_rate_plan->{collection}->[0]->{default_rate_plan}->{link}->{name};
776 my $delete_host = $self->api_call(
781 'access_point' => '<none>',
782 'rate_plan' => $default_rateplan_name,
786 $self->{'__saisei_error'} = "Host not created"
787 unless ($delete_host || $self->{'__saisei_error'}); # should never happen
794 my ($self, $opt) = @_;
796 if (!$opt->{tower_uprate_limit} || !$opt->{tower_downrate_limit}) {
797 $self->{'__saisei_error'} = "Can not export tower, no up or down rates attached to tower";
798 return { error => $self->api_error, };
801 my $existing_tower_ap;
802 my $tower_name = $opt->{tower_name};
804 #check if tower has been set up as an access point.
805 $existing_tower_ap = $self->api_get_accesspoint($tower_name) unless $self->{'__saisei_error'};
807 # modify the existing accesspoint if changing tower .
808 $self->api_modify_existing_accesspoint (
810 '', # tower does not have a uplink on sectors.
811 $opt->{tower_uprate_limit},
812 $opt->{tower_downrate_limit},
813 ) if $existing_tower_ap->{collection} && $opt->{modify_existing};
815 #if tower does not exist as an access point create it.
816 $self->api_create_accesspoint(
818 $opt->{tower_uprate_limit},
819 $opt->{tower_downrate_limit},
820 ) unless $existing_tower_ap->{collection};
822 my $accesspoint = $self->api_get_accesspoint($tower_name);
824 return { error => $self->api_error, } if $self->api_error;
829 my ($self, $opt) = @_;
831 if (!$opt->{sector_uprate_limit} || !$opt->{sector_downrate_limit}) {
832 $self->{'__saisei_error'} = "Can not export sector, no up or down rates attached to sector";
833 return { error => $self->api_error, };
836 my $existing_sector_ap;
837 my $sector_name = $opt->{sector_name};
839 #check if sector has been set up as an access point.
840 $existing_sector_ap = $self->api_get_accesspoint($sector_name);
842 # modify the existing accesspoint if changing sector .
843 $self->api_modify_existing_accesspoint (
846 $opt->{sector_uprate_limit},
847 $opt->{sector_downrate_limit},
848 ) if $existing_sector_ap && $opt->{modify_existing};
850 #if sector does not exist as an access point create it.
851 $self->api_create_accesspoint(
853 $opt->{sector_uprate_limit},
854 $opt->{sector_downrate_limit},
855 ) unless $existing_sector_ap;
857 # Attach newly created sector to it's tower.
858 $self->api_modify_accesspoint($sector_name, $opt->{tower_name}) unless ($self->{'__saisei_error'} || $existing_sector_ap);
860 # set access point to existing one or newly created one.
861 my $accesspoint = $existing_sector_ap ? $existing_sector_ap : $self->api_get_accesspoint($sector_name);
863 return { error => $self->api_error, } if $self->api_error;
867 sub process_virtual_ap {
868 my ($self, $opt) = @_;
870 my $existing_virtual_ap;
871 my $virtual_name = $opt->{virtual_name};
873 #check if virtual_ap has been set up as an access point.
874 $existing_virtual_ap = $self->api_get_accesspoint($virtual_name);
876 # modify the existing virtual accesspoint if changing it. this should never happen
877 $self->api_modify_existing_accesspoint (
880 $opt->{virtual_uprate_limit},
881 $opt->{virtual_downrate_limit},
882 ) if $existing_virtual_ap && $opt->{modify_existing};
884 #if virtual ap does not exist as an access point create it.
885 $self->api_create_accesspoint(
887 $opt->{virtual_uprate_limit},
888 $opt->{virtual_downrate_limit},
889 ) unless $existing_virtual_ap;
892 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})) {
896 # Attach newly created virtual ap to tower sector ap or if sector has changed.
897 $self->api_modify_accesspoint($virtual_name, $opt->{sector_name}) unless ($self->{'__saisei_error'} || ($existing_virtual_ap && !$update_sector));
899 # set access point to existing one or newly created one.
900 my $accesspoint = $existing_virtual_ap ? $existing_virtual_ap : $self->api_get_accesspoint($virtual_name);
905 sub export_provisioned_services {
909 my $part_export = FS::Record::qsearchs('part_export', { 'exportnum' => $param->{export_provisioned_services_exportnum}, } )
910 or die "unknown exportnum $param->{export_provisioned_services_exportnum}";
913 my @svcparts = FS::Record::qsearch({
914 'table' => 'export_svc',
915 'addl_from' => 'LEFT JOIN part_svc USING ( svcpart ) ',
916 'hashref' => { 'exportnum' => $param->{export_provisioned_services_exportnum}, },
918 my $part_count = scalar @svcparts;
920 my $parts = join "', '", map { $_->{Hash}->{svcpart} } @svcparts;
922 my @svcs = FS::Record::qsearch({
923 'table' => 'cust_svc',
924 'addl_from' => 'LEFT JOIN svc_broadband USING ( svcnum ) ',
925 'extra_sql' => " WHERE svcpart in ('".$parts."')",
928 my $svc_count = scalar @svcs;
931 for (my $c=1; $c <=100; $c=$c+1) { $status{int($svc_count * ($c/100))} = $c; }
934 foreach my $svc (@svcs) {
935 if ($status{$process_count}) { my $s = $status{$process_count}; $job->update_statustext($s); }
936 ## check if service exists as host if not export it.
937 my $host = api_get_host($part_export, $svc->{Hash}->{ip_addr});
938 die $host->{message} if $host->{message};
939 warn "Exporting service ".$svc->{Hash}->{ip_addr}."\n" if ($part_export->option('debug'));
940 my $export_error = _export_insert($part_export,$svc) unless $host->{collection};
941 die $export_error if $export_error;