1 package FS::part_export::saisei;
4 use vars qw( @ISA %info );
5 use base qw( FS::part_export );
6 use Date::Format 'time2str';
17 FS::part_export::saisei
21 Saisei integration for Freeside
25 This export offers basic svc_broadband provisioning for Saisei.
27 This is a customer integration with Saisei. This will setup a rate plan and tie
28 the rate plan to a host via the Saisei API when the broadband service is provisioned.
29 It will also untie the rate plan via the API upon unprovisioning of the broadband service.
31 This export will use the broadband service descriptive label for the Saisei rate plan name and
32 will use the email from the first contact for the Saisei username that will be
33 attached to this rate plan. It will use the Saisei default Access Point.
35 Hostname or IP - Host name to Saisei API
36 Port - <I>Port number to Saisei API
37 User Name - <I>Saisei API user name
38 Password - <I>Saisei API password
40 This module also provides generic methods for working through the L</Saisei API>.
44 tie my %options, 'Tie::IxHash',
45 'port' => { label => 'Port',
47 'username' => { label => 'User Name',
49 'password' => { label => 'Password',
51 'debug' => { type => 'checkbox',
52 label => 'Enable debug warnings' },
56 'svc' => 'svc_broadband',
57 'desc' => 'Export broadband service/account to Saisei',
58 'options' => \%options,
60 This is a customer integration with Saisei. This will setup a rate plan and tie
61 the rate plan to a host via the Saisei API when the broadband service is provisioned.
62 It will also untie the rate plan via the API upon unprovisioning of the broadband service.
63 <P>This export will use the broadband service descriptive label for the Saisei rate plan name and
64 will use the email from the first contact for the Saisei username that will be
65 attached to this rate plan. It will use the Saisei default Access Point.
69 <LI>Hostname or IP - <I>Host name to Saisei API</I></LI>
70 <LI>Port - <I>Port number to Saisei API</I></LI>
71 <LI>User Name - <I>Saisei API user name</I></LI>
72 <LI>Password - <I>Saisei API password</I></LI>
78 my ($self, $svc_broadband) = @_;
79 my $rateplan_name = $svc_broadband->{Hash}->{description};
80 $rateplan_name =~ s/\s/_/g;
83 # load needed info from our end
84 my $cust_main = $svc_broadband->cust_main;
85 return "Could not load service customer" unless $cust_main;
86 my $conf = new FS::Conf;
89 my $policies = $self->api_get_policies();
91 # check for existing rate plan
92 my $existing_rateplan;
93 $existing_rateplan = $self->api_get_rateplan($rateplan_name) unless $self->{'__saisei_error'};
95 # if no existing rate plan create one and modify it.
96 $self->api_create_rateplan($svc_broadband, $rateplan_name) unless $existing_rateplan;
97 $self->api_modify_rateplan($policies->{collection}, $svc_broadband, $rateplan_name) unless ($self->{'__saisei_error'} || $existing_rateplan);
99 # set rateplan to existing one or newly created one.
100 my $rateplan = $existing_rateplan ? $existing_rateplan : $self->api_get_rateplan($rateplan_name);
102 my @email = map { $_->emailaddress } FS::Record::qsearch({
103 'table' => 'cust_contact',
104 'select' => 'emailaddress',
105 'addl_from' => ' JOIN contact_email USING (contactnum)',
106 'hashref' => { 'custnum' => $cust_main->{Hash}->{custnum}, },
108 my $username = $email[0];
109 my $description = $cust_main->{Hash}->{first}." ".$cust_main->{Hash}->{last};
112 $self->{'__saisei_error'} = 'no username - can not export';
113 warn "No email found $username\n" if $self->option('debug');
117 # check for existing user.
119 $existing_user = $self->api_get_user($username) unless $self->{'__saisei_error'};
121 # if no existing user create one.
122 $self->api_create_user($username, $description) unless $existing_user;
124 # set user to existing one or newly created one.
125 my $user = $existing_user ? $existing_user : $self->api_get_user($username);
127 ## add access point ?
130 $self->api_add_host_to_user($user->{collection}->[0]->{name}, $rateplan->{collection}->[0]->{name}, $svc_broadband->{Hash}->{ip_addr}) unless $self->{'__saisei_error'};
137 sub _export_replace {
138 my ($self, $svc_phone) = @_;
143 my ($self, $svc_broadband) = @_;
145 my $cust_main = $svc_broadband->cust_main;
146 return "Could not load service customer" unless $cust_main;
147 my $conf = new FS::Conf;
149 my $rateplan_name = $svc_broadband->{Hash}->{description};
150 $rateplan_name =~ s/\s/_/g;
152 my @email = map { $_->emailaddress } FS::Record::qsearch({
153 'table' => 'cust_contact',
154 'select' => 'emailaddress',
155 'addl_from' => ' JOIN contact_email USING (contactnum)',
156 'hashref' => { 'custnum' => $cust_main->{Hash}->{custnum}, },
158 my $username = $email[0];
161 $self->api_delete_host_to_user($username, $rateplan_name, $svc_broadband->{Hash}->{ip_addr}) unless $self->{'__saisei_error'};
166 sub _export_suspend {
167 my ($self, $svc_phone) = @_;
171 sub _export_unsuspend {
172 my ($self, $svc_phone) = @_;
178 These methods allow access to the Saisei API using the credentials
179 set in the export options.
185 Accepts I<$method>, I<$path>, I<$params> hashref and optional.
186 Places an api call to the specified path and method with the specified params.
187 Returns the decoded json object returned by the api call.
188 Returns empty on failure; retrieve error messages using L</api_error>.
193 my ($self,$method,$path,$params) = @_;
194 $self->{'__saisei_error'} = '';
195 my $auth_info = $self->option('username') . ':' . $self->option('password');
198 warn "Calling $method on http://"
199 .$self->{Hash}->{machine}.':'.$self->option('port')
200 ."/rest/stm/configurations/running/$path\n" if $self->option('debug');
202 my $data = encode_json($params) if keys %{ $params };
204 my $client = REST::Client->new();
205 $client->addHeader("Authorization", "Basic ".encode_base64($auth_info));
206 $client->setHost('http://'.$self->{Hash}->{machine}.':'.$self->option('port'));
207 $client->$method('/rest/stm/configurations/running'.$path, $data, { "Content-type" => 'application/json'});
209 warn "Response Code is ".$client->responseCode()."\n" if $self->option('debug');
213 if ($client->responseCode() eq '200' || $client->responseCode() eq '201') {
214 eval { $result = decode_json($client->responseContent()) };
216 $self->{'__saisei_error'} = "Error decoding json: $@";
221 $self->{'__saisei_error'} = "Bad response from server during $method: " . $client->responseContent();
222 warn "Response Content is\n".$client->responseContent."\n" if $self->option('debug');
232 Returns the error string set by L</PortaOne API> methods,
233 or a blank string if most recent call produced no errors.
239 return $self->{'__saisei_error'} || '';
242 =head2 api_get_policies
244 Gets a list of global policies.
248 sub api_get_policies {
251 my $get_policies = $self->api_call("GET", '/policies/?token=1&order=name&start=0&limit=20&select=name%2Cpercent_rate%2Cassured%2C');
252 return if $self->api_error;
253 $self->{'__saisei_error'} = "Did not receive any global policies"
254 unless $get_policies;
256 return $get_policies;
259 =head2 api_get_rateplan
261 Gets rateplan info for specific rateplan.
265 sub api_get_rateplan {
267 my $rateplan = shift;
269 my $get_rateplan = $self->api_call("GET", "/rate_plans/$rateplan");
270 return if $self->api_error;
271 $self->{'__saisei_error'} = "Did not receive any rateplan info"
272 unless $get_rateplan;
274 return $get_rateplan;
279 Gets user info for specific user.
287 my $get_user = $self->api_call("GET", "/users/$user");
288 return if $self->api_error;
289 $self->{'__saisei_error'} = "Did not receive any user info"
295 =head2 api_get_accesspoint
297 Gets user info for specific access point.
301 sub api_get_accesspoint {
305 my $get_accesspoint = $self->api_call("GET", "/access_points/$accesspoint");
306 return if $self->api_error;
307 $self->{'__saisei_error'} = "Did not receive any user info"
308 unless $get_accesspoint;
313 =head2 api_create_rateplan
319 sub api_create_rateplan {
320 my ($self, $svc, $rateplan) = @_;
322 my $new_rateplan = $self->api_call(
324 "/rate_plans/$rateplan",
326 'downstream_rate' => $svc->{Hash}->{speed_down},
327 'upstream_rate' => $svc->{Hash}->{speed_up},
331 $self->{'__saisei_error'} = "Rate Plan not created"
332 unless $new_rateplan; # should never happen
333 return $new_rateplan;
337 =head2 api_modify_rateplan
343 sub api_modify_rateplan {
344 my ($self,$policies,$svc,$rateplan_name) = @_;
346 foreach my $policy (@$policies) {
347 my $policyname = $policy->{name};
348 my $rate_multiplier = '';
349 if ($policy->{background}) { $rate_multiplier = ".01"; }
350 my $modified_rateplan = $self->api_call(
352 "/rate_plans/$rateplan_name/partitions/$policyname",
354 'restricted' => $policy->{assured}, # policy_assured_flag
355 'rate_multiplier' => $rate_multiplier, # policy_background 0.1
356 'rate' => $policy->{percent_rate}, # policy_percent_rate
360 $self->{'__saisei_error'} = "Rate Plan not modified"
361 unless $modified_rateplan; # should never happen
369 =head2 api_create_user
375 sub api_create_user {
376 my ($self,$user, $description) = @_;
378 my $new_user = $self->api_call(
382 'description' => $description,
386 $self->{'__saisei_error'} = "User not created"
387 unless $new_user; # should never happen
393 =head2 api_create_accesspoint
395 Creates a access point.
399 sub api_create_accesspoint {
400 my ($self,$accesspoint) = @_;
402 # this has not been tested, but should work, if needed.
403 #my $new_accesspoint = $self->api_call(
405 # "/access_points/$accesspoint",
407 # 'description' => 'my description',
411 #$self->{'__saisei_error'} = "Access point not created"
412 # unless $new_accesspoint; # should never happen
417 =head2 api_add_host_to_user
419 ties host to user, rateplan and default access point.
423 sub api_add_host_to_user {
424 my ($self,$user, $rateplan, $ip) = @_;
426 my $new_host = $self->api_call(
431 'rate_plan' => $rateplan,
435 $self->{'__saisei_error'} = "Host not created"
436 unless $new_host; # should never happen
442 =head2 api_delete_host_to_user
444 unties host to user and rateplan.
448 sub api_delete_host_to_user {
449 my ($self,$user, $rateplan, $ip) = @_;
451 my $default_rate_plan = $self->api_call("GET", '?token=1&select=default_rate_plan');
452 return if $self->api_error;
453 $self->{'__saisei_error'} = "Did not receive a default rate plan"
454 unless $default_rate_plan;
456 my $default_rateplan_name = $default_rate_plan->{collection}->[0]->{default_rate_plan}->{link}->{name};
458 my $delete_host = $self->api_call(
463 'access_point' => '<none>',
464 'rate_plan' => $default_rateplan_name,
468 $self->{'__saisei_error'} = "Host not created"
469 unless $delete_host; # should never happen