X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fpart_export%2Fsaisei.pm;h=f76051ea3361dd9487e2fd6ef8d6329305a95ffe;hp=5e6279b68b43de0303271219a753dd9874463cb7;hb=5f9edcbe9fb3b3eb905614927aa6120d50c06ff1;hpb=024e1169b90e76d4ecd61e6b8e7dbf1ade83bef1 diff --git a/FS/FS/part_export/saisei.pm b/FS/FS/part_export/saisei.pm index 5e6279b68..f76051ea3 100644 --- a/FS/FS/part_export/saisei.pm +++ b/FS/FS/part_export/saisei.pm @@ -10,8 +10,6 @@ use REST::Client; use Data::Dumper; use FS::Conf; -#@ISA = qw( FS::part_export::http ); - =pod =head1 NAME @@ -26,6 +24,30 @@ Saisei integration for Freeside This export offers basic svc_broadband provisioning for Saisei. +This is a customer integration with Saisei. This will setup a rate plan and tie +the rate plan to a host and access point via the Saisei API when the broadband service is provisioned. +It will also untie the rate plan via the API upon unprovisioning of the broadband service. + +Add a new export and fill out required fields: + +Create a broadband service. The broadband service name will become the Saisei rate plan name. +Set the upload and download speed, and set the modifier to fixed. +Set IP Address to required. +Attach Saisei export to service + +Create a tower and add a sector to that tower. The sector name will be the name of the access point, +Make sure you have set an up and down rate for the Tower and Sector. + +When you provision the service, enter the ip address associated to this service. +Select the Tower and Sector for it's access point. + +When the service is provisioned it will auto setup the rate plan. + This module also provides generic methods for working through the L. =cut @@ -46,23 +68,38 @@ tie my %options, 'Tie::IxHash', 'desc' => 'Export broadband service/account to Saisei', 'options' => \%options, 'notes' => <<'END', -This is customer integration with Saisei. +This is a customer integration with Saisei. This will setup a rate plan and tie +the rate plan to a host and access point via the Saisei API when the broadband service is provisioned. +It will also untie the rate plan via the API upon unprovisioning of the broadband service. +

+Add a new export and fill out required fields: +

+Create a broadband service. The broadband service name will become the Saisei rate plan name. +Set the upload and download speed, and set the modifier to fixed. +Set IP Address to required. +Attach Saisei export to service +

+Create a tower and add a sector to that tower. The sector name will be the name of the access point, +Make sure you have set an up and down rate for the Tower and Sector. +

+When you provision the service, enter the ip address associated to this service. +Select the Tower and Sector for it's access point. +

+When the service is provisioned it will auto setup the rate plan. END ); -#"/STM_IP:5000/rest/top/configurations/running/" is for http 5029 for https - -#Creating User Names -#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. -#Creating a user name requires a command of the following format: - -#'put', 'users/USER_NAME', {'description':description} -#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. - sub _export_insert { my ($self, $svc_broadband) = @_; - my $rateplan_name = $svc_broadband->{Hash}->{description}; - $rateplan_name =~ s/\s/_/g; + my $service_part = FS::Record::qsearchs( 'part_svc', { 'svcpart' => $svc_broadband->{Hash}->{svcpart} } ); + my $rateplan_name = $service_part->{Hash}->{svc}; + $rateplan_name =~ s/\s/_/g; # load needed info from our end my $cust_main = $svc_broadband->cust_main; @@ -83,19 +120,13 @@ sub _export_insert { # set rateplan to existing one or newly created one. my $rateplan = $existing_rateplan ? $existing_rateplan : $self->api_get_rateplan($rateplan_name); - my @email = map { $_->emailaddress } FS::Record::qsearch({ - 'table' => 'cust_contact', - 'select' => 'emailaddress', - 'addl_from' => ' JOIN contact_email USING (contactnum)', - 'hashref' => { 'custnum' => $cust_main->{Hash}->{custnum}, }, - }); - my $username = $email[0]; - my $description = $cust_main->{Hash}->{first}." ".$cust_main->{Hash}->{last}; + my $username = $svc_broadband->{Hash}->{svcnum}; + my $description = $svc_broadband->{Hash}->{description}; if (!$username) { $self->{'__saisei_error'} = 'no username - can not export'; - warn "No email found $username\n" if $self->option('debug'); - return; + warn "No user $username\n" if $self->option('debug'); + return $self->api_error; } else { # check for existing user. @@ -109,12 +140,65 @@ sub _export_insert { my $user = $existing_user ? $existing_user : $self->api_get_user($username); ## add access point ? - - ## tie host to user - $self->api_add_host_to_user($user->{collection}->[0]->{name}, $rateplan->{collection}->[0]->{name}, $svc_broadband->{Hash}->{ip_addr}) unless $self->{'__saisei_error'}; + my $tower_sector = FS::Record::qsearchs({ + 'table' => 'tower_sector', + 'select' => 'tower.towername, + tower.up_rate as toweruprate, + tower.down_rate as towerdownrate, + tower_sector.sectorname, + tower_sector.up_rate as sectoruprate, + tower_sector.down_rate as sectordownrate ', + 'addl_from' => 'LEFT JOIN tower USING ( towernum )', + 'hashref' => { + 'sectornum' => $svc_broadband->{Hash}->{sectornum}, + }, + }); + + my $existing_tower_ap; + my $tower_name = $tower_sector->{Hash}->{towername}; + $tower_name =~ s/\s/_/g; + + #check if tower has been set up as an access point. + $existing_tower_ap = $self->api_get_accesspoint($tower_name) unless $self->{'__saisei_error'};; + + #if tower does not exist as an access point create it. + $self->api_create_accesspoint( + $tower_name, + $tower_sector->{Hash}->{toweruprate}, + $tower_sector->{Hash}->{towerdownrate} + ) unless $existing_tower_ap; + + my $existing_sector_ap; + my $sector_name = $tower_sector->{Hash}->{sectorname}; + $sector_name =~ s/\s/_/g; + + #check if sector has been set up as an access point. + $existing_sector_ap = $self->api_get_accesspoint($sector_name); + + #if sector does not exist as an access point create it. + $self->api_create_accesspoint( + $sector_name, + $tower_sector->{Hash}->{sectoruprate}, + $tower_sector->{Hash}->{sectordownrate}, + $tower_name, + ) unless $existing_sector_ap; + + # Attach newly created sector to it's tower. + $self->api_modify_accesspoint($sector_name, $tower_name) unless ($self->{'__saisei_error'} || $existing_sector_ap); + + # set access point to existing one or newly created one. + my $accesspoint = $existing_sector_ap ? $existing_sector_ap : $self->api_get_accesspoint($sector_name); + + ## tie host to user add sector name as access point. + $self->api_add_host_to_user( + $user->{collection}->[0]->{name}, + $rateplan->{collection}->[0]->{name}, + $svc_broadband->{Hash}->{ip_addr}, + $accesspoint->{collection}->[0]->{name}, + ) unless $self->{'__saisei_error'}; } - return ''; + return $self->api_error; } @@ -166,15 +250,10 @@ set in the export options. =head2 api_call -Accepts I<$service>, I<$method>, I<$params> hashref and optional -I<$returnfield>. Places an api call to the specified service -and method with the specified params. Returns the decoded json -object returned by the api call. If I<$returnfield> is specified, -returns only that field of the decoded object, and errors out if -that field does not exist. Returns empty on failure; retrieve -error messages using L. - -Must run L first. +Accepts I<$method>, I<$path>, I<$params> hashref and optional. +Places an api call to the specified path and method with the specified params. +Returns the decoded json object returned by the api call. +Returns empty on failure; retrieve error messages using L. =cut @@ -193,7 +272,7 @@ sub api_call { my $client = REST::Client->new(); $client->addHeader("Authorization", "Basic ".encode_base64($auth_info)); $client->setHost('http://'.$self->{Hash}->{machine}.':'.$self->option('port')); - $client->$method('/rest/stm/configurations/running/'.$path, $data, { "Content-type" => 'application/json'}); + $client->$method('/rest/stm/configurations/running'.$path, $data, { "Content-type" => 'application/json'}); warn "Response Code is ".$client->responseCode()."\n" if $self->option('debug'); @@ -218,7 +297,7 @@ sub api_call { =head2 api_error -Returns the error string set by L methods, +Returns the error string set by L methods, or a blank string if most recent call produced no errors. =cut @@ -237,7 +316,7 @@ Gets a list of global policies. sub api_get_policies { my $self = shift; - my $get_policies = $self->api_call("GET", 'policies/?token=1&order=name&start=0&limit=20&select=name%2Cpercent_rate%2Cassured%2C'); + my $get_policies = $self->api_call("GET", '/policies/?token=1&order=name&start=0&limit=20&select=name%2Cpercent_rate%2Cassured%2C'); return if $self->api_error; $self->{'__saisei_error'} = "Did not receive any global policies" unless $get_policies; @@ -255,7 +334,7 @@ sub api_get_rateplan { my $self = shift; my $rateplan = shift; - my $get_rateplan = $self->api_call("GET", "rate_plans/$rateplan"); + my $get_rateplan = $self->api_call("GET", "/rate_plans/$rateplan"); return if $self->api_error; $self->{'__saisei_error'} = "Did not receive any rateplan info" unless $get_rateplan; @@ -273,7 +352,7 @@ sub api_get_user { my $self = shift; my $user = shift; - my $get_user = $self->api_call("GET", "users/$user"); + my $get_user = $self->api_call("GET", "/users/$user"); return if $self->api_error; $self->{'__saisei_error'} = "Did not receive any user info" unless $get_user; @@ -289,14 +368,14 @@ Gets user info for specific access point. sub api_get_accesspoint { my $self = shift; - my $accesspoint; + my $accesspoint = shift; - my $get_accesspoint = $self->api_call("GET", "access_points/$accesspoint"); + my $get_accesspoint = $self->api_call("GET", "/access_points/$accesspoint"); return if $self->api_error; - $self->{'__saisei_error'} = "Did not receive any user info" + $self->{'__saisei_error'} = "Did not receive any access point info" unless $get_accesspoint; - return; + return $get_accesspoint; } =head2 api_create_rateplan @@ -310,7 +389,7 @@ sub api_create_rateplan { my $new_rateplan = $self->api_call( "PUT", - "rate_plans/$rateplan", + "/rate_plans/$rateplan", { 'downstream_rate' => $svc->{Hash}->{speed_down}, 'upstream_rate' => $svc->{Hash}->{speed_up}, @@ -338,7 +417,7 @@ sub api_modify_rateplan { if ($policy->{background}) { $rate_multiplier = ".01"; } my $modified_rateplan = $self->api_call( "PUT", - "rate_plans/$rateplan_name/partitions/$policyname", + "/rate_plans/$rateplan_name/partitions/$policyname", { 'restricted' => $policy->{assured}, # policy_assured_flag 'rate_multiplier' => $rate_multiplier, # policy_background 0.1 @@ -357,7 +436,7 @@ sub api_modify_rateplan { =head2 api_create_user -Creates a rateplan. +Creates a user. =cut @@ -366,7 +445,7 @@ sub api_create_user { my $new_user = $self->api_call( "PUT", - "users/$user", + "/users/$user", { 'description' => $description, }, @@ -386,37 +465,64 @@ Creates a access point. =cut sub api_create_accesspoint { - my ($self,$accesspoint) = @_; - - #my $new_accesspoint = $self->api_call( - # "PUT", - # "access_points/$accesspoint", - # { - # 'description' => 'my description', - # }, - #); - - #$self->{'__saisei_error'} = "Access point not created" - # unless $new_accesspoint; # should never happen + my ($self,$accesspoint, $uprate, $downrate) = @_; + + # this has not been tested, but should work, if needed. + my $new_accesspoint = $self->api_call( + "PUT", + "/access_points/$accesspoint", + { + 'downstream_rate_limit' => $downrate, + 'upstream_rate_limit' => $uprate, + }, + ); + + $self->{'__saisei_error'} = "Access point not created" + unless $new_accesspoint; # should never happen + return; + +} + +=head2 api_modify_accesspoint + +Modify a access point. + +=cut + +sub api_modify_accesspoint { + my ($self, $accesspoint, $uplink) = @_; + + my $modified_rateplan = $self->api_call( + "PUT", + "/access_points/$accesspoint", + { + 'uplink' => $uplink, # name of attached access point + }, + ); + + $self->{'__saisei_error'} = "Rate Plan not modified" + unless $modified_rateplan; # should never happen + return; } =head2 api_add_host_to_user -ties host to user and rateplan. +ties host to user, rateplan and default access point. =cut sub api_add_host_to_user { - my ($self,$user, $rateplan, $ip) = @_; + my ($self,$user, $rateplan, $ip, $accesspoint) = @_; my $new_host = $self->api_call( "PUT", - "hosts/$ip", + "/hosts/$ip", { 'user' => $user, 'rate_plan' => $rateplan, + 'access_point' => $accesspoint, }, ); @@ -427,16 +533,31 @@ sub api_add_host_to_user { } -=head2 api_add_host_to_user +=head2 api_delete_host_to_user -ties host to user and rateplan. +unties host to user and rateplan. =cut sub api_delete_host_to_user { my ($self,$user, $rateplan, $ip) = @_; - my $delete_host = $self->api_call("DELETE", "hosts/$ip"); + my $default_rate_plan = $self->api_call("GET", '?token=1&select=default_rate_plan'); + return if $self->api_error; + $self->{'__saisei_error'} = "Did not receive a default rate plan" + unless $default_rate_plan; + + my $default_rateplan_name = $default_rate_plan->{collection}->[0]->{default_rate_plan}->{link}->{name}; + + my $delete_host = $self->api_call( + "PUT", + "/hosts/$ip", + { + 'user' => '', + 'access_point' => '', + 'rate_plan' => $default_rateplan_name, + }, + ); $self->{'__saisei_error'} = "Host not created" unless $delete_host; # should never happen