RT# 30783 Improve speed of ip address auto-assignment
[freeside.git] / FS / FS / addr_block.pm
index e00f587..eb84daf 100755 (executable)
@@ -1,16 +1,14 @@
 package FS::addr_block;
+use base qw(FS::Record);
 
 use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs qsearch dbh );
-use FS::router;
-use FS::svc_broadband;
-use FS::Conf;
-use NetAddr::IP;
 use Carp qw( carp );
 use List::Util qw( first );
-
-@ISA = qw( FS::Record );
+use NetAddr::IP;
+use FS::Conf;
+use FS::Record qw( qsearch dbh ); #qsearchs
+use FS::IP_Mixin;
+use FS::addr_range;
 
 =head1 NAME
 
@@ -182,25 +180,11 @@ sub check {
 Returns the FS::router object corresponding to this object.  If the 
 block is unassigned, returns undef.
 
-=cut
-
-sub router {
-  my $self = shift;
-  return qsearchs('router', { routernum => $self->routernum });
-}
-
 =item svc_broadband
 
 Returns a list of FS::svc_broadband objects associated
 with this object.
 
-=cut
-
-sub svc_broadband {
-  my $self = shift;
-  return qsearch('svc_broadband', { blocknum => $self->blocknum });
-}
-
 =item NetAddr
 
 Returns a NetAddr::IP object for this block's address and netmask.
@@ -225,41 +209,68 @@ sub 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 
 in the block (other than the network, broadcast, or gateway address).  If 
 there are no free addresses, returns nothing.  There are never free addresses
 when manual_flag is true.
 
-=item next_free_addr
-
-Returns a NetAddr::IP object for the first unassigned address in the block,
-or '' if there are none.
+There is no longer a method to return all free addresses in a block.
 
 =cut
 
-sub free_addrs {
+sub next_free_addr {
   my $self = shift;
+  my $selfaddr = $self->NetAddr;
 
-  return if $self->manual_flag;
+  return () if $self->manual_flag;
 
   my $conf = new FS::Conf;
   my @excludeaddr = $conf->config('exclude_ip_addr');
-  
+
   my %used = map { $_ => 1 }
   (
-    (map { $_->NetAddr->addr }
-      ($self,
-       qsearch('svc_broadband', { blocknum => $self->blocknum }))
-    ), @excludeaddr
+    @excludeaddr,
+    $selfaddr->addr,
+    $selfaddr->network->addr,
+    $selfaddr->broadcast->addr,
+    FS::IP_Mixin->used_addresses_in_block($self)
   );
 
-  grep { !$used{$_->addr} } $self->NetAddr->hostenum;
-
-}
+  # just do a linear search of the block
+  my $freeaddr = $selfaddr->network + 1;
+  while ( $freeaddr < $selfaddr->broadcast ) {
+    # also make sure it's not blocked from assignment by an address range
+    if ( !$used{$freeaddr->addr } ) {
+      my ($range) = grep { !$_->allow_use }
+                  FS::addr_range->any_contains($freeaddr->addr);
+      if ( !$range ) {
+        # then we've found a free address
+        return $freeaddr;
+      }
+      # otherwise, skip to the end of the range
+      $freeaddr = NetAddr::IP->new($range->end, $self->ip_netmask);
+    }
+    $freeaddr++;
+  }
+  return;
 
-sub next_free_addr {
-  my $self = shift;
-  ($self->free_addrs, '')[0]
 }
 
 =item allocate -- deprecated
@@ -381,12 +392,6 @@ To be implemented.
 
 Returns the agent (see L<FS::agent>) for this address block, if one exists.
 
-=cut
-
-sub agent {
-  qsearchs('agent', { 'agentnum' => shift->agentnum } );
-}
-
 =item label
 
 Returns text including the router name, gateway ip, and netmask for this
@@ -400,6 +405,24 @@ sub label {
   ($router ? $router->routername : '(unallocated)'). ':'. $self->NetAddr;
 }
 
+=item router
+
+Returns the router assigned to this block.
+
+=cut
+
+# necessary, because this can't be foreign keyed
+
+sub router {
+  my $self = shift;
+  my $routernum = $self->routernum;
+  if ( $routernum ) {
+    return FS::router->by_key($routernum);
+  } else {
+    return;
+  }
+}
+
 =back
 
 =head1 BUGS
@@ -410,4 +433,3 @@ now because that's the smallest block that makes any sense at all.
 =cut
 
 1;
-