[freeside-commits] branch FREESIDE_3_BRANCH_30783 created. 020fe5db4a3ad50dad3eebd26316821e114ac9e7
Mitch Jackson
mitch at freeside.biz
Tue Oct 23 14:54:28 PDT 2018
The branch, FREESIDE_3_BRANCH_30783 has been created
at 020fe5db4a3ad50dad3eebd26316821e114ac9e7 (commit)
- Log -----------------------------------------------------------------
commit 020fe5db4a3ad50dad3eebd26316821e114ac9e7
Author: Mitch Jackson <mitch at freeside.biz>
Date: Sun Jul 8 20:02:25 2018 -0500
RT# 30783 js fix for ip selection
diff --git a/httemplate/elements/tr-select-router_block_ip.html b/httemplate/elements/tr-select-router_block_ip.html
index eac41cfad..72640d3d5 100644
--- a/httemplate/elements/tr-select-router_block_ip.html
+++ b/httemplate/elements/tr-select-router_block_ip.html
@@ -4,8 +4,8 @@ var ip_addr_curr_value = <% $opt{'ip_addr'} |js_string %>;
var blocknum_curr_value = <% $opt{'blocknum'} |js_string %>;
function update_ip_addr() {
- var routernum = $('#router_select_0').val();
- var blocknum = $('#router_select_1').val();
+ var routernum = $('#router_select_0').val() || "";
+ var blocknum = $('#router_select_1').val() || "";
var e_input_ip_addr = $('#input_ip_addr');
var e_router_select_1 = $('#router_select_1');
commit 0327c04ef25a6879a85b7a4a352147f4746703ee
Author: Mitch Jackson <mitch at freeside.biz>
Date: Fri Jun 29 13:04:33 2018 -0500
RT# 30783 Clean up json code for free_addrs
diff --git a/FS/FS/svc_IP_Mixin.pm b/FS/FS/svc_IP_Mixin.pm
index 19c7e05fc..56165dce5 100644
--- a/FS/FS/svc_IP_Mixin.pm
+++ b/FS/FS/svc_IP_Mixin.pm
@@ -132,7 +132,7 @@ sub _used_addresses {
# parameter to bypass FS::Record objects creation and just
# return hashrefs from DBI. 200,000 hashrefs are many seconds faster
# than 200,000 FS::Record objects
- my %qsearch = (
+ my %qsearch_param = (
table => $class->table,
select => $ip_field,
hashref => \%qsearch,
@@ -140,7 +140,8 @@ sub _used_addresses {
);
if ( $octets ) {
my $block_str = join('.', (split(/\D/, $block_na->first))[0..$octets-1]);
- $qsearch{extra_sql} .= " AND $ip_field LIKE ".dbh->quote("${block_str}.%");
+ $qsearch_param{extra_sql}
+ .= " AND $ip_field LIKE ".dbh->quote("${block_str}.%");
}
if ( $block->ip_netmask % 8 ) {
@@ -154,7 +155,7 @@ sub _used_addresses {
return
map { $_->$ip_field }
- qsearch( \%qsearch );
+ qsearch( \%qsearch_param );
}
sub _is_used {
diff --git a/httemplate/elements/tr-select-router_block_ip.html b/httemplate/elements/tr-select-router_block_ip.html
index 535e953c4..eac41cfad 100644
--- a/httemplate/elements/tr-select-router_block_ip.html
+++ b/httemplate/elements/tr-select-router_block_ip.html
@@ -71,7 +71,7 @@ function populate_ip_select() {
% }
if ( blocknum && $.isNumeric(blocknum) && ! e.is(':hidden')) {
$.getJSON(
- '<% $p %>json/free_addresses_in_block.json.html',
+ '<% $p %>misc/xmlhttp-free_addresses_in_block.json.html',
{blocknum: blocknum},
function(ip_json) {
$.each( ip_json, function(idx, val) {
diff --git a/httemplate/json/free_addresses_in_block.json.html b/httemplate/misc/xmlhttp-free_addresses_in_block.json.html
similarity index 94%
rename from httemplate/json/free_addresses_in_block.json.html
rename to httemplate/misc/xmlhttp-free_addresses_in_block.json.html
index 6785aac6b..801718d35 100644
--- a/httemplate/json/free_addresses_in_block.json.html
+++ b/httemplate/misc/xmlhttp-free_addresses_in_block.json.html
@@ -3,7 +3,7 @@
Unless block is larger than /24 - Does somebody really want to populate
65k addresses into a HTML selectbox?
</%doc>
-<% encode_rest($json) %>\
+<% encode_json($json) %>\
<%init>
my $json = [];
commit e81c112f4cb6b4e617697a3308b5128ff14a5f4c
Author: Mitch Jackson <mitch at freeside.biz>
Date: Tue Jun 26 18:18:51 2018 -0500
RT# 30783 Clean up IP utility code
diff --git a/FS/FS/IP_Mixin.pm b/FS/FS/IP_Mixin.pm
index 07fa9e776..1967ccd57 100644
--- a/FS/FS/IP_Mixin.pm
+++ b/FS/FS/IP_Mixin.pm
@@ -268,45 +268,27 @@ sub router {
FS::router->by_key($self->routernum);
}
-=item used_addresses [ BLOCK ]
+=item used_addresses [ FS::addr_block ]
-Returns a list of all addresses that are in use by a service. If called as an
-instance method, excludes that instance from the search.
+Returns a list of all addresses in use within the given L<FS::addr_block>.
-Does not filter by block, will return ALL used addresses. ref:f197bdbaa1
+If called as an instance method, excludes that instance from the search.
=cut
sub used_addresses {
- my $self = shift;
- my $block = shift;
- return ( map { $_->_used_addresses($block, $self) } @subclasses );
-}
-
-sub _used_addresses {
- my $class = shift;
- die "$class->_used_addresses not implemented";
-}
-
-=item used_addresses_in_block [ FS::addr_block ]
-
-Returns a list of all addresses in use within the given L<FS::addr_block>
-
-=cut
-
-sub used_addresses_in_block {
my ($self, $block) = @_;
(
$block->ip_gateway ? $block->ip_gateway : (),
$block->NetAddr->broadcast->addr,
- map { $_->_used_addresses_in_block($block, $self ) } @subclasses
+ map { $_->_used_addresses($block, $self ) } @subclasses
);
}
-sub _used_addresses_in_block {
+sub _used_addresses {
my $class = shift;
- die "$class->_used_addresses_in_block not implemented";
+ die "$class->_used_addresses not implemented";
}
=item is_used ADDRESS
diff --git a/FS/FS/addr_block.pm b/FS/FS/addr_block.pm
index 31c7cfff0..a9f7d4b02 100755
--- a/FS/FS/addr_block.pm
+++ b/FS/FS/addr_block.pm
@@ -249,7 +249,7 @@ sub free_addrs {
my %used_addr_map =
map {$_ => 1}
- FS::IP_Mixin->used_addresses_in_block($self),
+ FS::IP_Mixin->used_addresses($self),
FS::Conf->new()->config('exclude_ip_addr');
[
@@ -285,7 +285,7 @@ sub next_free_addr {
$selfaddr->addr,
$selfaddr->network->addr,
$selfaddr->broadcast->addr,
- FS::IP_Mixin->used_addresses_in_block($self)
+ FS::IP_Mixin->used_addresses($self)
);
# just do a linear search of the block
diff --git a/FS/FS/svc_IP_Mixin.pm b/FS/FS/svc_IP_Mixin.pm
index ce9218c10..19c7e05fc 100644
--- a/FS/FS/svc_IP_Mixin.pm
+++ b/FS/FS/svc_IP_Mixin.pm
@@ -91,38 +91,21 @@ sub svc_ip_check {
}
sub _used_addresses {
+ my ($class, $block, $exclude_svc) = @_;
- # Returns all addresses in use. Does not filter with $block. ref:f197bdbaa1
-
- my ($class, $block, $exclude) = @_;
- my $ip_field = $class->table_info->{'ip_field'}
- or return ();
- # if the service doesn't have an ip_field, then it has no IP addresses
- # in use, yes?
-
- my %hash = ( $ip_field => { op => '!=', value => '' } );
- #$hash{'blocknum'} = $block->blocknum if $block;
- $hash{'svcnum'} = { op => '!=', value => $exclude->svcnum } if ref $exclude;
- map { my $na = $_->NetAddr; $na ? $na->addr : () }
- qsearch({
- table => $class->table,
- hashref => \%hash,
- extra_sql => " AND $ip_field != '0e0'",
- });
-}
-
-sub _used_addresses_in_block {
- my ($class, $block) = @_;
-
- croak "_used_addresses_in_block() requires an FS::addr_block parameter"
+ croak "_used_addresses() requires an FS::addr_block parameter"
unless ref $block && $block->isa('FS::addr_block');
my $ip_field = $class->table_info->{'ip_field'};
if ( !$ip_field ) {
- carp "_used_addresses_in_block() skipped, no ip_field";
+ carp "_used_addresses() skipped, no ip_field";
return;
}
+ my %qsearch = ( $ip_field => { op => '!=', value => '' });
+ $qsearch{svcnum} = { op => '!=', value => $exclude_svc->svcnum }
+ if ref $exclude_svc && $exclude_svc->svcnum;
+
my $block_na = $block->NetAddr;
my $octets;
@@ -152,7 +135,7 @@ sub _used_addresses_in_block {
my %qsearch = (
table => $class->table,
select => $ip_field,
- hashref => { $ip_field => { op => '!=', value => '' }},
+ hashref => \%qsearch,
extra_sql => " AND $ip_field != '0e0' ",
);
if ( $octets ) {
commit f1b5eac395f720a40baac99b0fd04f0da198086e
Author: Mitch Jackson <mitch at freeside.biz>
Date: Tue Jun 26 17:34:50 2018 -0500
RT# 30783 Selectbox of available IPs when provisioning
diff --git a/FS/FS/addr_block.pm b/FS/FS/addr_block.pm
index b692259a4..31c7cfff0 100755
--- a/FS/FS/addr_block.pm
+++ b/FS/FS/addr_block.pm
@@ -240,7 +240,7 @@ sub cidr {
=item free_addrs
-Returns a sorted list of free addresses in the block.
+Returns an aref sorted list of free addresses in the block.
=cut
@@ -252,7 +252,11 @@ sub free_addrs {
FS::IP_Mixin->used_addresses_in_block($self),
FS::Conf->new()->config('exclude_ip_addr');
- grep { !exists $used_addr_map{$_} } map { $_->addr } $self->NetAddr->hostenum;
+ [
+ grep { !exists $used_addr_map{$_} }
+ map { $_->addr }
+ $self->NetAddr->hostenum
+ ];
}
=item next_free_addr
diff --git a/httemplate/elements/tr-select-router_block_ip.html b/httemplate/elements/tr-select-router_block_ip.html
index 2aa715e29..535e953c4 100644
--- a/httemplate/elements/tr-select-router_block_ip.html
+++ b/httemplate/elements/tr-select-router_block_ip.html
@@ -2,34 +2,110 @@
var manual_addr_routernum = <% encode_json(\%manual_addr_routernum) %>;
var ip_addr_curr_value = <% $opt{'ip_addr'} |js_string %>;
var blocknum_curr_value = <% $opt{'blocknum'} |js_string %>;
-function update_ip_addr(obj, i) {
- var routernum = document.getElementById('router_select_0').value;
- var select_blocknum = document.getElementById('router_select_1');
- var blocknum = select_blocknum.value;
- var input_ip_addr = document.getElementById('input_ip_addr');
+
+function update_ip_addr() {
+ var routernum = $('#router_select_0').val();
+ var blocknum = $('#router_select_1').val();
+ var e_input_ip_addr = $('#input_ip_addr');
+ var e_router_select_1 = $('#router_select_1');
+
+ <% # Is block is automatically selected for this router? %>
if ( manual_addr_routernum[routernum] == 'Y' ) {
-%# hide block selection and default ip address to its previous value
- select_blocknum.style.display = 'none';
- input_ip_addr.value = ip_addr_curr_value;
- }
- else {
-%# the reverse
- select_blocknum.style.display = '';
-%# default ip address to null, unless the router/block are set to the
-%# previous value, in which case default it to current value
+ show_ip_input();
+ hide_ip_select();
+ e_router_select_1.hide();
+ e_input_ip_addr.val( ip_addr_curr_value );
+ } else {
+ e_router_select_1.show();
+ e_input_ip_addr.attr('placeholder', <% mt('(automatic)') | js_string %> );
if ( routernum == router_curr_values[0] &&
- blocknum == router_curr_values[1] ) {
- input_ip_addr.value = ip_addr_curr_value;
+ blocknum == router_curr_values[1] ) {
+ e_input_ip_addr.val( ip_addr_curr_value );
} else {
- input_ip_addr.value = <% mt('(automatic)') |js_string %>;
+ e_input_ip_addr.val('');
}
}
+ show_or_hide_toggle_ip();
+ populate_ip_select();
+}
+
+function toggle_ip_input() {
+ if ( $('#input_ip_addr').is(':hidden') ) {
+ show_ip_input();
+ } else {
+ show_ip_select();
+ }
+}
+
+function show_ip_input() {
+ $('#input_ip_addr').show();
+ $('#select_ip_addr').hide();
+ depopulate_ip_select();
+}
+
+function show_ip_select() {
+ var e_input_ip_addr = $('#input_ip_addr');
+ var e_select_ip_addr = $('#select_ip_addr');
+
+ e_select_ip_addr.width( e_input_ip_addr.width() );
+ e_input_ip_addr.hide();
+ e_select_ip_addr.show();
+ populate_ip_select();
+}
+
+function populate_ip_select() {
+ depopulate_ip_select();
+ var e = $('#select_ip_addr');
+ var blocknum = $('#router_select_1').val();
+
+ var opts = [ '<option value="">loading...</option>' ];
+ e.html(opts.join(''));
+
+% if ( $opt{ip_addr} ) {
+ opts = [
+ '<option value="<% $opt{ip_addr} |h %>"><% $opt{ip_addr} |h %></option>',
+ '<option value="">-----------</option>'
+ ];
+% } else {
+ opts = [ '<option value=""><% mt('(automatic)') |h %></option>' ];
+% }
+ if ( blocknum && $.isNumeric(blocknum) && ! e.is(':hidden')) {
+ $.getJSON(
+ '<% $p %>json/free_addresses_in_block.json.html',
+ {blocknum: blocknum},
+ function(ip_json) {
+ $.each( ip_json, function(idx, val) {
+ opts.push(
+ '<option' + (val == ip_addr_curr_value ? 'selected' : '') + '>'
+ + val
+ + '</option>'
+ );
+ });
+ e.html(opts.join(''));
+ }
+ );
+ }
}
-function clearhint_ip_addr (what) {
- if ( what.value == <% mt('(automatic)') |js_string %> )
- what.value = '';
+
+function depopulate_ip_select() {
+ $('#select_ip_addr').children().remove();
}
+
+function propogate_ip_select() {
+ $('#input_ip_addr').val( $('#select_ip_addr').val() );
+}
+
+function show_or_hide_toggle_ip() {
+ if ( $('#router_select_1').val() ) {
+ $('#toggle_ip').show();
+ } else {
+ show_ip_input();
+ $('#toggle_ip').hide();
+ }
+}
+
</script>
+
<& /elements/tr-td-label.html, label => ($opt{'label'} || 'Router'), required => $opt{'required'} &>
<td>
<& /elements/select-tiered.html, prefix => 'router_', tiers => [
@@ -58,14 +134,20 @@ function clearhint_ip_addr (what) {
</td></tr>
<& /elements/tr-td-label.html, label => ($opt{'ip_addr_label'} || 'IP address'), required => $opt{'ip_addr_required'} &>
<td>
-% #warn Dumper \%fixed;
% if ( exists $fixed{$ip_field} ) {
<input type="hidden" id="input_ip_addr" name="<% $ip_field %>"
value="<% $opt{'ip_addr'} |h%>"><% $opt{'ip_addr'} || '' %>
% }
% else {
- <input type="text" id="input_ip_addr" name="<% $ip_field %>"
- value="<% $opt{'ip_addr'} |h%>" onfocus="clearhint_ip_addr(this)">
+ <input type="text"
+ id="input_ip_addr"
+ name="<% $ip_field %>"
+ value="<% $opt{'ip_addr'} | h %>"
+ onfocus="clearhint_ip_addr(this)">
+ <select id="select_ip_addr" style="display: none;" onChange='javascript:propogate_ip_select();'>
+ <option><% mt('loading') |h %>...</option>
+ </select>
+ <button type="button" onClick='javascript:toggle_ip_input();' id="toggle_ip" style="display: none;">▼</button>
% }
</td> </tr>
<script type="text/javascript">
diff --git a/httemplate/json/free_addresses_in_block.json.html b/httemplate/json/free_addresses_in_block.json.html
new file mode 100644
index 000000000..6785aac6b
--- /dev/null
+++ b/httemplate/json/free_addresses_in_block.json.html
@@ -0,0 +1,18 @@
+<%doc>
+ Return a json array containing all free ip addresses within a given block
+ Unless block is larger than /24 - Does somebody really want to populate
+ 65k addresses into a HTML selectbox?
+</%doc>
+<% encode_rest($json) %>\
+<%init>
+
+my $json = [];
+
+my $blocknum = $cgi->param('blocknum');
+
+my $addr_block = qsearchs( addr_block => { blocknum => $blocknum });
+
+$json = $addr_block->free_addrs
+ if ref $addr_block && $addr_block->ip_netmask >= 24;
+
+</%init>
commit 8162d15e07f1f998caa4ce522c732d1bc3211ac7
Author: Mitch Jackson <mitch at freeside.biz>
Date: Mon Jun 25 14:09:42 2018 -0500
RT# 30783 Improve speed of ip address auto-assignment
diff --git a/FS/FS/addr_block.pm b/FS/FS/addr_block.pm
index a39e1f1bb..b692259a4 100755
--- a/FS/FS/addr_block.pm
+++ b/FS/FS/addr_block.pm
@@ -281,7 +281,7 @@ sub next_free_addr {
$selfaddr->addr,
$selfaddr->network->addr,
$selfaddr->broadcast->addr,
- FS::IP_Mixin->used_addresses($self)
+ FS::IP_Mixin->used_addresses_in_block($self)
);
# just do a linear search of the block
commit 2ee7f0c27233d800254d5244fc5913d881b48800
Author: Mitch Jackson <mitch at freeside.biz>
Date: Mon Jun 25 14:07:52 2018 -0500
RT# 30783 Add network block enumerating utils
diff --git a/FS/FS/IP_Mixin.pm b/FS/FS/IP_Mixin.pm
index 8920cebc5..07fa9e776 100644
--- a/FS/FS/IP_Mixin.pm
+++ b/FS/FS/IP_Mixin.pm
@@ -270,9 +270,10 @@ sub router {
=item used_addresses [ BLOCK ]
-Returns a list of all addresses (in BLOCK, or in all blocks)
-that are in use. If called as an instance method, excludes
-that instance from the search.
+Returns a list of all addresses that are in use by a service. If called as an
+instance method, excludes that instance from the search.
+
+Does not filter by block, will return ALL used addresses. ref:f197bdbaa1
=cut
@@ -287,6 +288,27 @@ sub _used_addresses {
die "$class->_used_addresses not implemented";
}
+=item used_addresses_in_block [ FS::addr_block ]
+
+Returns a list of all addresses in use within the given L<FS::addr_block>
+
+=cut
+
+sub used_addresses_in_block {
+ my ($self, $block) = @_;
+
+ (
+ $block->ip_gateway ? $block->ip_gateway : (),
+ $block->NetAddr->broadcast->addr,
+ map { $_->_used_addresses_in_block($block, $self ) } @subclasses
+ );
+}
+
+sub _used_addresses_in_block {
+ my $class = shift;
+ die "$class->_used_addresses_in_block not implemented";
+}
+
=item is_used ADDRESS
Returns a string describing what object is using ADDRESS, or
diff --git a/FS/FS/addr_block.pm b/FS/FS/addr_block.pm
index f07de490b..a39e1f1bb 100755
--- a/FS/FS/addr_block.pm
+++ b/FS/FS/addr_block.pm
@@ -238,6 +238,23 @@ sub cidr {
$self->NetAddr->cidr;
}
+=item free_addrs
+
+Returns a sorted list of free addresses in the block.
+
+=cut
+
+sub free_addrs {
+ my $self = shift;
+
+ my %used_addr_map =
+ map {$_ => 1}
+ FS::IP_Mixin->used_addresses_in_block($self),
+ FS::Conf->new()->config('exclude_ip_addr');
+
+ grep { !exists $used_addr_map{$_} } map { $_->addr } $self->NetAddr->hostenum;
+}
+
=item next_free_addr
Returns a NetAddr::IP object corresponding to the first unassigned address
@@ -433,4 +450,3 @@ now because that's the smallest block that makes any sense at all.
=cut
1;
-
diff --git a/FS/FS/svc_IP_Mixin.pm b/FS/FS/svc_IP_Mixin.pm
index 8b2b5f17e..ce9218c10 100644
--- a/FS/FS/svc_IP_Mixin.pm
+++ b/FS/FS/svc_IP_Mixin.pm
@@ -3,7 +3,8 @@ use base 'FS::IP_Mixin';
use strict;
use NEXT;
-use FS::Record qw(qsearchs qsearch);
+use Carp qw(croak carp);
+use FS::Record qw(qsearchs qsearch dbh);
use FS::Conf;
use FS::router;
use FS::part_svc_router;
@@ -90,6 +91,9 @@ sub svc_ip_check {
}
sub _used_addresses {
+
+ # Returns all addresses in use. Does not filter with $block. ref:f197bdbaa1
+
my ($class, $block, $exclude) = @_;
my $ip_field = $class->table_info->{'ip_field'}
or return ();
@@ -107,6 +111,69 @@ sub _used_addresses {
});
}
+sub _used_addresses_in_block {
+ my ($class, $block) = @_;
+
+ croak "_used_addresses_in_block() requires an FS::addr_block parameter"
+ unless ref $block && $block->isa('FS::addr_block');
+
+ my $ip_field = $class->table_info->{'ip_field'};
+ if ( !$ip_field ) {
+ carp "_used_addresses_in_block() skipped, no ip_field";
+ return;
+ }
+
+ my $block_na = $block->NetAddr;
+
+ my $octets;
+ if ($block->ip_netmask >= 24) {
+ $octets = 3;
+ } elsif ($block->ip_netmask >= 16) {
+ $octets = 2;
+ } elsif ($block->ip_netmask >= 8) {
+ $octets = 1;
+ }
+
+ # e.g.
+ # SELECT ip_addr
+ # FROM svc_broadband
+ # WHERE ip_addr != ''
+ # AND ip_addr != '0e0'
+ # AND ip_addr LIKE '10.0.2.%';
+ #
+ # For /24, /16 and /8 this approach is fast, even when svc_broadband table
+ # contains 650,000+ ip records. For other allocations, this approach is
+ # not speedy, but usable.
+ #
+ # Note: A use case like this would could greatly benefit from a qsearch()
+ # parameter to bypass FS::Record objects creation and just
+ # return hashrefs from DBI. 200,000 hashrefs are many seconds faster
+ # than 200,000 FS::Record objects
+ my %qsearch = (
+ table => $class->table,
+ select => $ip_field,
+ hashref => { $ip_field => { op => '!=', value => '' }},
+ extra_sql => " AND $ip_field != '0e0' ",
+ );
+ if ( $octets ) {
+ my $block_str = join('.', (split(/\D/, $block_na->first))[0..$octets-1]);
+ $qsearch{extra_sql} .= " AND $ip_field LIKE ".dbh->quote("${block_str}.%");
+ }
+
+ if ( $block->ip_netmask % 8 ) {
+ # Some addresses returned by qsearch may be outside the network block,
+ # so each ip address is tested to be in the block before it's returned.
+ return
+ grep { $block_na->contains( NetAddr::IP->new( $_ ) ) }
+ map { $_->$ip_field }
+ qsearch( \%qsearch );
+ }
+
+ return
+ map { $_->$ip_field }
+ qsearch( \%qsearch );
+}
+
sub _is_used {
my ($class, $addr, $exclude) = @_;
my $ip_field = $class->table_info->{'ip_field'}
-----------------------------------------------------------------------
More information about the freeside-commits
mailing list