1 package FS::part_export::saisei;
4 use vars qw( @ISA %info );
5 use base qw( FS::part_export );
6 use Date::Format 'time2str';
13 #@ISA = qw( FS::part_export::http );
19 FS::part_export::saisei
23 Saisei integration for Freeside
27 This export offers basic svc_broadband provisioning for Saisei.
29 This module also provides generic methods for working through the L</Saisei API>.
33 tie my %options, 'Tie::IxHash',
34 'port' => { label => 'Port',
36 'username' => { label => 'User Name',
38 'password' => { label => 'Password',
40 'debug' => { type => 'checkbox',
41 label => 'Enable debug warnings' },
45 'svc' => 'svc_broadband',
46 'desc' => 'Export broadband service/account to Saisei',
47 'options' => \%options,
49 This is customer integration with Saisei.
53 #"/STM_IP:5000/rest/top/configurations/running/" is for http 5029 for https
56 #Users are tracked by their name which gives access to the internal slice data which in turn allows the viewing of Applications and Geo-Locations.
57 #Creating a user name requires a command of the following format: -
58 #'put', 'users/USER_NAME', {'description':description}
59 #When creating a user name it is usual to add a description and since a user attribute set does not normally contain the users plan name it is best to encode it into the description field.
62 my ($self, $svc_broadband) = @_;
63 my $rateplan_name = $svc_broadband->{Hash}->{description};
64 $rateplan_name =~ s/\s/_/g;
67 # load needed info from our end
68 my $cust_main = $svc_broadband->cust_main;
69 return "Could not load service customer" unless $cust_main;
70 my $conf = new FS::Conf;
73 my $policies = $self->api_get_policies();
75 # check for existing rate plan
76 my $existing_rateplan;
77 $existing_rateplan = $self->api_get_rateplan($rateplan_name) unless $self->{'__saisei_error'};
79 # if no existing rate plan create one and modify it.
80 $self->api_create_rateplan($svc_broadband, $rateplan_name) unless $existing_rateplan;
81 $self->api_modify_rateplan($policies->{collection}, $svc_broadband, $rateplan_name) unless ($self->{'__saisei_error'} || $existing_rateplan);
83 # set rateplan to existing one or newly created one.
84 my $rateplan = $existing_rateplan ? $existing_rateplan : $self->api_get_rateplan($rateplan_name);
86 my @email = map { $_->emailaddress } FS::Record::qsearch({
87 'table' => 'cust_contact',
88 'select' => 'emailaddress',
89 'addl_from' => ' JOIN contact_email USING (contactnum)',
90 'hashref' => { 'custnum' => $cust_main->{Hash}->{custnum}, },
92 my $username = $email[0];
93 my $description = $cust_main->{Hash}->{first}." ".$cust_main->{Hash}->{last};
96 $self->{'__saisei_error'} = 'no username - can not export';
97 warn "No email found $username\n" if $self->option('debug');
101 # check for existing user.
103 $existing_user = $self->api_get_user($username) unless $self->{'__saisei_error'};
105 # if no existing user create one.
106 $self->api_create_user($username, $description) unless $existing_user;
108 # set user to existing one or newly created one.
109 my $user = $existing_user ? $existing_user : $self->api_get_user($username);
111 ## add access point ?
114 $self->api_add_host_to_user($user->{collection}->[0]->{name}, $rateplan->{collection}->[0]->{name}, $svc_broadband->{Hash}->{ip_addr}) unless $self->{'__saisei_error'};
121 sub _export_replace {
122 my ($self, $svc_phone) = @_;
127 my ($self, $svc_broadband) = @_;
129 my $cust_main = $svc_broadband->cust_main;
130 return "Could not load service customer" unless $cust_main;
131 my $conf = new FS::Conf;
133 my $rateplan_name = $svc_broadband->{Hash}->{description};
134 $rateplan_name =~ s/\s/_/g;
136 my @email = map { $_->emailaddress } FS::Record::qsearch({
137 'table' => 'cust_contact',
138 'select' => 'emailaddress',
139 'addl_from' => ' JOIN contact_email USING (contactnum)',
140 'hashref' => { 'custnum' => $cust_main->{Hash}->{custnum}, },
142 my $username = $email[0];
145 $self->api_delete_host_to_user($username, $rateplan_name, $svc_broadband->{Hash}->{ip_addr}) unless $self->{'__saisei_error'};
150 sub _export_suspend {
151 my ($self, $svc_phone) = @_;
155 sub _export_unsuspend {
156 my ($self, $svc_phone) = @_;
162 These methods allow access to the Saisei API using the credentials
163 set in the export options.
169 Accepts I<$service>, I<$method>, I<$params> hashref and optional
170 I<$returnfield>. Places an api call to the specified service
171 and method with the specified params. Returns the decoded json
172 object returned by the api call. If I<$returnfield> is specified,
173 returns only that field of the decoded object, and errors out if
174 that field does not exist. Returns empty on failure; retrieve
175 error messages using L</api_error>.
177 Must run L</api_login> first.
182 my ($self,$method,$path,$params) = @_;
183 $self->{'__saisei_error'} = '';
184 my $auth_info = $self->option('username') . ':' . $self->option('password');
187 warn "Calling $method on http://"
188 .$self->{Hash}->{machine}.':'.$self->option('port')
189 ."/rest/stm/configurations/running/$path\n" if $self->option('debug');
191 my $data = encode_json($params) if keys %{ $params };
193 my $client = REST::Client->new();
194 $client->addHeader("Authorization", "Basic ".encode_base64($auth_info));
195 $client->setHost('http://'.$self->{Hash}->{machine}.':'.$self->option('port'));
196 $client->$method('/rest/stm/configurations/running/'.$path, $data, { "Content-type" => 'application/json'});
198 warn "Response Code is ".$client->responseCode()."\n" if $self->option('debug');
202 if ($client->responseCode() eq '200' || $client->responseCode() eq '201') {
203 eval { $result = decode_json($client->responseContent()) };
205 $self->{'__saisei_error'} = "Error decoding json: $@";
210 $self->{'__saisei_error'} = "Bad response from server during $method: " . $client->responseContent();
211 warn "Response Content is\n".$client->responseContent."\n" if $self->option('debug');
221 Returns the error string set by L</PortaOne API> methods,
222 or a blank string if most recent call produced no errors.
228 return $self->{'__saisei_error'} || '';
231 =head2 api_get_policies
233 Gets a list of global policies.
237 sub api_get_policies {
240 my $get_policies = $self->api_call("GET", 'policies/?token=1&order=name&start=0&limit=20&select=name%2Cpercent_rate%2Cassured%2C');
241 return if $self->api_error;
242 $self->{'__saisei_error'} = "Did not receive any global policies"
243 unless $get_policies;
245 return $get_policies;
248 =head2 api_get_rateplan
250 Gets rateplan info for specific rateplan.
254 sub api_get_rateplan {
256 my $rateplan = shift;
258 my $get_rateplan = $self->api_call("GET", "rate_plans/$rateplan");
259 return if $self->api_error;
260 $self->{'__saisei_error'} = "Did not receive any rateplan info"
261 unless $get_rateplan;
263 return $get_rateplan;
268 Gets user info for specific user.
276 my $get_user = $self->api_call("GET", "users/$user");
277 return if $self->api_error;
278 $self->{'__saisei_error'} = "Did not receive any user info"
284 =head2 api_get_accesspoint
286 Gets user info for specific access point.
290 sub api_get_accesspoint {
294 my $get_accesspoint = $self->api_call("GET", "access_points/$accesspoint");
295 return if $self->api_error;
296 $self->{'__saisei_error'} = "Did not receive any user info"
297 unless $get_accesspoint;
302 =head2 api_create_rateplan
308 sub api_create_rateplan {
309 my ($self, $svc, $rateplan) = @_;
311 my $new_rateplan = $self->api_call(
313 "rate_plans/$rateplan",
315 'downstream_rate' => $svc->{Hash}->{speed_down},
316 'upstream_rate' => $svc->{Hash}->{speed_up},
320 $self->{'__saisei_error'} = "Rate Plan not created"
321 unless $new_rateplan; # should never happen
322 return $new_rateplan;
326 =head2 api_modify_rateplan
332 sub api_modify_rateplan {
333 my ($self,$policies,$svc,$rateplan_name) = @_;
335 foreach my $policy (@$policies) {
336 my $policyname = $policy->{name};
337 my $rate_multiplier = '';
338 if ($policy->{background}) { $rate_multiplier = ".01"; }
339 my $modified_rateplan = $self->api_call(
341 "rate_plans/$rateplan_name/partitions/$policyname",
343 'restricted' => $policy->{assured}, # policy_assured_flag
344 'rate_multiplier' => $rate_multiplier, # policy_background 0.1
345 'rate' => $policy->{percent_rate}, # policy_percent_rate
349 $self->{'__saisei_error'} = "Rate Plan not modified"
350 unless $modified_rateplan; # should never happen
358 =head2 api_create_user
364 sub api_create_user {
365 my ($self,$user, $description) = @_;
367 my $new_user = $self->api_call(
371 'description' => $description,
375 $self->{'__saisei_error'} = "User not created"
376 unless $new_user; # should never happen
382 =head2 api_create_accesspoint
384 Creates a access point.
388 sub api_create_accesspoint {
389 my ($self,$accesspoint) = @_;
391 #my $new_accesspoint = $self->api_call(
393 # "access_points/$accesspoint",
395 # 'description' => 'my description',
399 #$self->{'__saisei_error'} = "Access point not created"
400 # unless $new_accesspoint; # should never happen
405 =head2 api_add_host_to_user
407 ties host to user and rateplan.
411 sub api_add_host_to_user {
412 my ($self,$user, $rateplan, $ip) = @_;
414 my $new_host = $self->api_call(
419 'rate_plan' => $rateplan,
423 $self->{'__saisei_error'} = "Host not created"
424 unless $new_host; # should never happen
430 =head2 api_add_host_to_user
432 ties host to user and rateplan.
436 sub api_delete_host_to_user {
437 my ($self,$user, $rateplan, $ip) = @_;
439 my $delete_host = $self->api_call("DELETE", "hosts/$ip");
441 $self->{'__saisei_error'} = "Host not created"
442 unless $delete_host; # should never happen