don't enumerate the whole subnet when looking for a free address, #18556
[freeside.git] / FS / FS / addr_block.pm
index 0fe2476..686bdbd 100755 (executable)
@@ -8,6 +8,7 @@ use FS::svc_broadband;
 use FS::Conf;
 use NetAddr::IP;
 use Carp qw( carp );
 use FS::Conf;
 use NetAddr::IP;
 use Carp qw( carp );
+use List::Util qw( first );
 
 @ISA = qw( FS::Record );
 
 
 @ISA = qw( FS::Record );
 
@@ -76,14 +77,35 @@ otherwise returns false.
 Deletes this record from the database.  If there is an error, returns the
 error, otherwise returns false.
 
 Deletes this record from the database.  If there is an error, returns the
 error, otherwise returns false.
 
+=cut
+
 sub delete {
   my $self = shift;
 sub delete {
   my $self = shift;
-  return 'Block must be deallocated before deletion'
-    if $self->router;
-
-  $self->SUPER::delete;
+  return 'Block must be deallocated and have no services before deletion'
+    if $self->router || $self->svc_broadband;
+
+    local $SIG{HUP} = 'IGNORE';
+    local $SIG{INT} = 'IGNORE';
+    local $SIG{QUIT} = 'IGNORE';
+    local $SIG{TERM} = 'IGNORE';
+    local $SIG{TSTP} = 'IGNORE';
+    local $SIG{PIPE} = 'IGNORE';
+
+    my $oldAutoCommit = $FS::UID::AutoCommit;
+    local $FS::UID::AutoCommit = 0;
+    my $dbh = dbh;
+    
+    my $error = $self->SUPER::delete;
+    if ( $error ) {
+       $dbh->rollback if $oldAutoCommit;
+       return $error;
+    }
+  
+    $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+    '';
 }
 
 }
 
+
 =item replace OLD_RECORD
 
 Replaces OLD_RECORD with this one in the database.  If there is an error,
 =item replace OLD_RECORD
 
 Replaces OLD_RECORD with this one in the database.  If there is an error,
@@ -205,32 +227,40 @@ sub cidr {
 
 Returns a NetAddr::IP object corresponding to the first unassigned address 
 in the block (other than the network, broadcast, or gateway address).  If 
 
 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 false.  There are never free addresses
+there are no free addresses, returns nothing.  There are never free addresses
 when manual_flag is true.
 
 when manual_flag is true.
 
+There is no longer a method to return all free addresses in a block.
+
 =cut
 
 sub next_free_addr {
   my $self = shift;
 =cut
 
 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 $conf = new FS::Conf;
   my @excludeaddr = $conf->config('exclude_ip_addr');
-  
-my @used =
-( (map { $_->NetAddr->addr }
-    ($self,
-     qsearch('svc_broadband', { blocknum => $self->blocknum }))
-  ), @excludeaddr
-);
-
-  my @free = $self->NetAddr->hostenum;
-  while (my $ip = shift @free) {
-    if (not grep {$_ eq $ip->addr;} @used) { return $ip; };
-  }
 
 
-  '';
+  my %used = map { $_ => 1 }
+  (
+    @excludeaddr,
+    $selfaddr->addr,
+    $selfaddr->network->addr,
+    $selfaddr->broadcast->addr,
+    (map { $_->NetAddr->addr }
+       qsearch('svc_broadband', { blocknum => $self->blocknum })
+    ), @excludeaddr
+  );
+
+  # just do a linear search of the block
+  my $freeaddr = $selfaddr->network + 1;
+  while ( $freeaddr < $selfaddr->broadcast ) {
+    return $freeaddr unless $used{ $freeaddr->addr };
+    $freeaddr++;
+  }
+  return;
 
 }
 
 
 }