Changeset 125 for branches/stable


Ignore:
Timestamp:
01/14/05 18:03:44 (19 years ago)
Author:
Kris Deugau
Message:

/branches/stable

Complete merge of code rewrite from /trunk ~r104 to r124

Location:
branches/stable
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • branches/stable/cgi-bin/IPDB.pm

    r76 r125  
    1414use warnings;
    1515use Exporter;
     16use DBI;
    1617use Net::SMTP;
    1718use POSIX;
    1819use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
    1920
    20 $VERSION        = 1.0;
     21$VERSION        = 2.0;
    2122@ISA            = qw(Exporter);
    22 @EXPORT_OK      = qw(&connectDB &checkDBSanity &allocateBlock &mailNotify);
     23@EXPORT_OK    = qw(
     24        %disp_alloctypes %list_alloctypes @citylist @poplist @masterblocks
     25        %allocated %free %routed %bigfree
     26        &initIPDBGlobals &connectDB &finish &checkDBSanity &allocateBlock &deleteBlock
     27        &mailNotify
     28        );
    2329
    2430@EXPORT         = (); # Export nothing by default.
    25 %EXPORT_TAGS    = ( ALL => [qw( &connectDB &checkDBSanity &allocateBlock &mailNotify)]
    26                   );
    27 
    28 
     31%EXPORT_TAGS    = ( ALL => [qw(
     32                %disp_alloctypes %list_alloctypes @citylist @poplist @masterblocks
     33                %allocated %free %routed %bigfree
     34                &initIPDBGlobals &connectDB &finish &checkDBSanity &allocateBlock
     35                &deleteBlock &mailNotify
     36                )]
     37        );
     38
     39##
     40## Global variables
     41##
     42our %disp_alloctypes;
     43our %list_alloctypes;
     44our @citylist;
     45our @poplist;
     46our @masterblocks;
     47our %allocated;
     48our %free;
     49our %routed;
     50our %bigfree;
     51
     52# Let's initialize the globals.
     53## IPDB::initIPDBGlobals()
     54# Initialize all globals.  Takes a database handle, returns a success or error code
     55sub initIPDBGlobals {
     56  my $dbh = $_[0];
     57  my $sth;
     58
     59  # Initialize alloctypes hashes
     60  $sth = $dbh->prepare("select * from alloctypes order by listorder");
     61  $sth->execute;
     62  while (my @data = $sth->fetchrow_array) {
     63    $disp_alloctypes{$data[0]} = $data[2];
     64    if ($data[3] < 900) {
     65      $list_alloctypes{$data[0]} = $data[1];
     66    }
     67  }
     68
     69  # City and POP listings
     70  $sth = $dbh->prepare("select * from cities");
     71  $sth->execute;
     72  return (undef,$sth->errstr) if $sth->err;
     73  while (my @data = $sth->fetchrow_array) {
     74    push @citylist, $data[0];
     75    if ($data[1] eq 'y') {
     76      push @poplist, $data[0];
     77    }
     78  }
     79
     80  # Master block list
     81  $sth = $dbh->prepare("select * from masterblocks order by cidr");
     82  $sth->execute;
     83  for (my $i=0; my @data = $sth->fetchrow_array(); $i++) {
     84    $masterblocks[$i] = new NetAddr::IP $data[0];
     85    $allocated{"$masterblocks[$i]"} = 0;
     86    $free{"$masterblocks[$i]"} = 0;
     87    $bigfree{"$masterblocks[$i]"} = 128; # Larger number means smaller block.
     88                                        # Set to 128 to prepare for IPv6
     89    $routed{"$masterblocks[$i]"} = 0;
     90  }
     91  return (undef,$sth->errstr) if $sth->err;
     92
     93  return (1,"OK");
     94} # end initIPDBGlobals
     95
     96
     97## IPDB::connectDB()
    2998# Creates connection to IPDB.
    30 # Default is a PostgreSQL db;  could be any DBMS with the
    31 # right changes.  MySQL in comments.  Note that some DBMS's don't
    32 # support transactions, this is a Bad Thing!
     99# Requires the database name, username, and password.
    33100# Returns a handle to the db.
     101# Set up for a PostgreSQL db;  could be any transactional DBMS with the
     102# right changes.
     103# This definition should be sub connectDB($$$) to be technically correct,
     104# but this breaks.  GRR.
    34105sub connectDB {
     106  my ($dbname,$user,$pass) = @_;
    35107  my $dbh;
    36   my $DSN = "DBI:Pg:dbname=ipdb";
    37   my $user = 'ipdb';
    38   my $pw   = 'ipdbpwd';
     108  my $DSN = "DBI:Pg:dbname=$dbname";
     109#  my $user = 'ipdb';
     110#  my $pw   = 'ipdbpwd';
    39111
    40112# Note that we want to autocommit by default, and we will turn it off locally as necessary.
    41   $dbh = DBI->connect($DSN, $user, $pw, { AutoCommit => 1 } )
    42     or return undef if(!$dbh);
    43 
    44   return $dbh;
     113# We may not want to print gobbledygook errors;  YMMV.  Have to ponder that further.
     114  $dbh = DBI->connect($DSN, $user, $pass, {
     115        AutoCommit => 1,
     116        PrintError => 0
     117        })
     118    or return (undef, $DBI::errstr) if(!$dbh);
     119
     120# Return here if we can't select.  Note that this indicates a
     121# problem executing the select.
     122  my $sth = $dbh->prepare('select cidr from masterblocks');
     123  $sth->execute();
     124  return (undef,$DBI::errstr) if ($sth->err);
     125
     126# See if the select returned anything (or null data).  This should
     127# succeed if the select executed, but...
     128  $sth->fetchrow();
     129  return (undef,$DBI::errstr)  if ($sth->err);
     130
     131# If we get here, we should be OK.
     132  return ($dbh,"DB connection OK");
    45133} # end connectDB
    46134
     135
     136## IPDB::finish()
     137# Cleans up after database handles and so on.
     138# Requires a database handle
     139sub finish {
     140  my $dbh = $_[0];
     141  $dbh->disconnect;
     142} # end finish
     143
     144
     145## IPDB::checkDBSanity()
    47146# Quick check to see if the db is responding.  A full integrity
    48147# check will have to be a separate tool to walk the IP allocation trees.
    49148sub checkDBSanity {
    50   my $dbh = connectDB();
     149  my ($dbh) = $_[0];
    51150
    52151  if (!$dbh) {
    53     print "Cannot connect to the database!";
     152    print "No database handle, or connection has been closed.";
     153    return -1;
    54154  } else {
    55155    # it connects, try a stmt.
     
    62162    } else {
    63163      print "Connected to the database, but could not execute test statement.  ".$sth->errstr();
     164      return -1;
    64165    }
    65166  }
    66167  # Clean up after ourselves.
    67   $dbh->disconnect;
     168#  $dbh->disconnect;
    68169} # end checkDBSanity
    69170
    70171
    71 # allocateBlock()
     172## IPDB::allocateBlock()
    72173# Does all of the magic of actually allocating a netblock
    73 sub allocateBlock($) {
    74 }
    75 
    76 
    77 # mailNotify()
     174# Requires database handle, block to allocate, custid, type, city,
     175#       description, notes, circuit ID, block to allocate from,
     176# Returns a success code and optional error message.
     177sub allocateBlock {
     178  my ($dbh,undef,undef,$custid,$type,$city,$desc,$notes,$circid) = @_;
     179 
     180  my $cidr = new NetAddr::IP $_[1];
     181  my $alloc_from = new NetAddr::IP $_[2];
     182  my $sth;
     183
     184  # To contain the error message, if any.
     185  my $msg = "Unknown error allocating $cidr as '$type'";
     186
     187  # Enable transactions and error handling
     188  local $dbh->{AutoCommit} = 0; # These need to be local so we don't
     189  local $dbh->{RaiseError} = 1; # step on our toes by accident.
     190
     191  if ($type =~ /^[cdsmw]i$/) {
     192    $msg = "Unable to assign static IP $cidr to $custid";
     193    eval {
     194      # We'll just have to put up with the oddities caused by SQL (un)sort order
     195      $sth = $dbh->prepare("select * from poolips where pool='$alloc_from'".
     196        " and available='y' order by ip");
     197      $sth->execute;
     198
     199# update poolips set custid='$custid',city='$city',available='n',
     200#       description='$desc',notes='$notes',circuitid='$circid'
     201#       where ip=(select ip from poolips where pool='$alloc_from'
     202#       and available='y' order by ip limit 1);
     203##err Need better handling here;  what if there's no free IPs when this sub gets called?
     204      my @data = $sth->fetchrow_array;
     205      my $cidr = $data[1];
     206
     207      $sth = $dbh->prepare("update poolips set custid='$custid',".
     208        "city='$city',available='n',description='$desc',notes='$notes',".
     209        "circuitid='$circid'".
     210        " where ip='$cidr'");
     211      $sth->execute;
     212      $dbh->commit;
     213    };
     214    if ($@) {
     215      $msg .= ": '".$sth->errstr."'";
     216      eval { $dbh->rollback; };
     217      return ('FAIL',$msg);
     218    } else {
     219      return ('OK',"$cidr");
     220    }
     221
     222  } else { # end IP-from-pool allocation
     223
     224    if ($cidr == $alloc_from) {
     225      # Easiest case- insert in one table, delete in the other, and go home.  More or less.
     226      # insert into allocations values (cidr,custid,type,city,desc) and
     227      # delete from freeblocks where cidr='cidr'
     228      # For data safety on non-transaction DBs, we delete first.
     229
     230      eval {
     231        $msg = "Unable to allocate $cidr as '$disp_alloctypes{$type}'";
     232        if ($type eq 'rr') {
     233          $sth = $dbh->prepare("update freeblocks set routed='y',city='$city'".
     234            " where cidr='$cidr'");
     235          $sth->execute;
     236          $sth = $dbh->prepare("insert into routed values ('$cidr',".
     237            $cidr->masklen.",'$city')");
     238          $sth->execute;
     239        } else {
     240          # common stuff for end-use, dialup, dynDSL, pools, etc, etc.
     241          $sth = $dbh->prepare("delete from freeblocks where cidr='$cidr'");
     242          $sth->execute;
     243
     244          $sth = $dbh->prepare("insert into allocations values ('$cidr',".
     245                "'$custid','$type','$city','$desc','$notes',".
     246                $cidr->masklen.",'$circid')");
     247          $sth->execute;
     248
     249          # And initialize the pool, if necessary
     250          if ($type =~ /^.p$/) {
     251            $msg = "Could not initialize IPs in new $disp_alloctypes{$type} pool $cidr";
     252            initPool($dbh,$cidr,$type,$city,($type eq 'dp' ? "all" : "normal"));
     253          }
     254
     255        } # routing vs non-routing netblock
     256
     257        $dbh->commit;
     258      }; # end of eval
     259      if ($@) {
     260        $msg = $@;
     261        eval { $dbh->rollback; };
     262        return ('FAIL',$@);
     263      } else {
     264        return ('OK',"OK");
     265      }
     266
     267    } else { # cidr != alloc_from
     268
     269      # Hard case.  Allocation is smaller than free block.
     270      my $wantmaskbits = $cidr->masklen;
     271      my $maskbits = $alloc_from->masklen;
     272
     273      my @newfreeblocks;        # Holds free blocks generated from splitting the source freeblock.
     274
     275      # This determines which blocks will be left "free" after allocation.  We take the
     276      # block we're allocating from, and split it in half.  We see which half the wanted
     277      # block is in, and repeat until the wanted block is equal to one of the halves.
     278      my $i=0;
     279      my $tmp_from = $alloc_from;       # So we don't munge $alloc_from
     280      while ($maskbits++ < $wantmaskbits) {
     281        my @subblocks = $tmp_from->split($maskbits);
     282        $newfreeblocks[$i++] = (($cidr->within($subblocks[0])) ? $subblocks[1] : $subblocks[0]);
     283        $tmp_from = ( ($cidr->within($subblocks[0])) ? $subblocks[0] : $subblocks[1] );
     284      } # while
     285
     286      # Begin SQL transaction block
     287      eval {
     288        $msg = "Unable to allocate $cidr as '$disp_alloctypes{$type}'";
     289
     290        # Delete old freeblocks entry
     291        $sth = $dbh->prepare("delete from freeblocks where cidr='$alloc_from'");
     292        $sth->execute();
     293
     294        # now we have to do some magic for routing blocks
     295        if ($type eq 'rr') {
     296
     297          # Insert the new freeblocks entries
     298          # Note that non-routed blocks are assigned to <NULL>
     299          $sth = $dbh->prepare("insert into freeblocks values (?, ?, '<NULL>','n')");
     300          foreach my $block (@newfreeblocks) {
     301            $sth->execute("$block", $block->masklen);
     302          }
     303
     304          # Insert the entry in the routed table
     305          $sth = $dbh->prepare("insert into routed values ('$cidr',".
     306            $cidr->masklen.",'$city')");
     307          $sth->execute;
     308          # Insert the (almost) same entry in the freeblocks table
     309          $sth = $dbh->prepare("insert into freeblocks values ('$cidr',".
     310            $cidr->masklen.",'$city','y')");
     311          $sth->execute;
     312
     313        } else { # done with alloctype == rr
     314
     315          # Insert the new freeblocks entries
     316          $sth = $dbh->prepare("insert into freeblocks values (?, ?, ".
     317                "(select city from routed where cidr >>= '$cidr'),'y')");
     318          foreach my $block (@newfreeblocks) {
     319            $sth->execute("$block", $block->masklen);
     320          }
     321
     322          # Insert the allocations entry
     323          $sth = $dbh->prepare("insert into allocations values ('$cidr',".
     324                "'$custid','$type','$city','$desc','$notes',".$cidr->masklen.
     325                ",'$circid')");
     326          $sth->execute;
     327
     328          # And initialize the pool, if necessary
     329          if ($type =~ /^.p$/) {
     330            $msg = "Could not initialize IPs in new $disp_alloctypes{$type} $cidr";
     331            initPool($dbh,$cidr,$type,$city,($type eq 'dp' ? "all" : "normal"));
     332          }
     333
     334        } # done with netblock alloctype != rr
     335
     336        $dbh->commit;
     337      }; # end eval
     338      if ($@) {
     339        eval { $dbh->rollback; };
     340        return ('FAIL',$msg);
     341      } else {
     342        return ('OK',"OK");
     343      }
     344
     345    } # end fullcidr != alloc_from
     346
     347  } # end static-IP vs netblock allocation
     348
     349} # end allocateBlock()
     350
     351
     352## IPDB::initPool()
     353# Initializes a pool
     354# Requires a database handle, the pool CIDR, type, city, and a parameter
     355# indicating whether the pool should allow allocation of literally every
     356# IP, or if it should reserve network/gateway/broadcast IPs
     357# Note that this is NOT done in a transaction, that's why it's a private
     358# function and should ONLY EVER get called from allocateBlock()
     359sub initPool {
     360  my ($dbh,undef,$type,$city,$class) = @_;
     361  my $pool = new NetAddr::IP $_[1];
     362
     363  my ($pooltype) = ($type =~ /^(.)p$/);
     364  my $sth;
     365
     366  # have to insert all pool IPs into poolips table as "unallocated".
     367  $sth = $dbh->prepare("insert into poolips values ('$pool',".
     368        " ?, '6750400', '$city', '$pooltype', 'y', '', '', '')");
     369  my @poolip_list = $pool->hostenum;
     370  if ($class eq 'all') { # (DSL-ish block - *all* IPs available
     371    $sth->execute($pool->addr);
     372    for (my $i=0; $i<=$#poolip_list; $i++) {
     373      $sth->execute($poolip_list[$i]->addr);
     374    }
     375    $pool--;
     376    $sth->execute($pool->addr);
     377  } else { # (real netblock)
     378    for (my $i=1; $i<=$#poolip_list; $i++) {
     379      $sth->execute($poolip_list[$i]->addr);
     380    }
     381  }
     382} # end initPool()
     383
     384
     385## IPDB::deleteBlock()
     386# Removes an allocation from the database, including deleting IPs
     387# from poolips and recombining entries in freeblocks if possible
     388# Also handles "deleting" a static IP allocation, and removal of a master
     389# Requires a database handle, the block to delete, and the type of block
     390sub deleteBlock {
     391  my ($dbh,undef,$type) = @_;
     392  my $cidr = new NetAddr::IP $_[1];
     393
     394  my $sth;
     395
     396  # To contain the error message, if any.
     397  my $msg = "Unknown error deallocating $type $cidr";
     398  # Enable transactions and exception-on-errors... but only for this sub
     399  local $dbh->{AutoCommit} = 0;
     400  local $dbh->{RaiseError} = 1;
     401
     402  # First case.  The "block" is a static IP
     403  # Note that we still need some additional code in the odd case
     404  # of a netblock-aligned contiguous group of static IPs
     405  if ($type =~ /^.i$/) {
     406
     407    eval {
     408      $msg = "Unable to deallocate $type $cidr";
     409      $sth = $dbh->prepare("update poolips set custid='6750400',available='y',".
     410        "city=(select city from allocations where cidr >>= '$cidr'),".
     411        "description='',notes='',circuitid='' where ip='$cidr'");
     412      $sth->execute;
     413      $dbh->commit;
     414    };
     415    if ($@) {
     416      eval { $dbh->rollback; };
     417      return ('FAIL',$msg);
     418    } else {
     419      return ('OK',"OK");
     420    }
     421
     422  } elsif ($type eq 'mm') { # end alloctype =~ /.i/
     423
     424    $msg = "Unable to delete master block $cidr";
     425    eval {
     426      $sth = $dbh->prepare("delete from masterblocks where cidr='$cidr'");
     427      $sth->execute;
     428      $sth = $dbh->prepare("delete from freeblocks where cidr='$cidr'");
     429      $sth->execute;
     430      $dbh->commit;
     431    };
     432    if ($@) {
     433      eval { $dbh->rollback; };
     434      return ('FAIL', $msg);
     435    } else {
     436      return ('OK',"OK");
     437    }
     438
     439  } else { # end alloctype master block case
     440
     441    ## This is a big block; but it HAS to be done in a chunk.  Any removal
     442    ## of a netblock allocation may result in a larger chunk of free
     443    ## contiguous IP space - which may in turn be combined into a single
     444    ## netblock rather than a number of smaller netblocks.
     445
     446    eval {
     447
     448      if ($type eq 'rr') {
     449        $msg = "Unable to remove routing allocation $cidr";
     450        $sth = $dbh->prepare("delete from routed where cidr='$cidr'");
     451        $sth->execute;
     452        # Make sure block getting deleted is properly accounted for.
     453        $sth = $dbh->prepare("update freeblocks set routed='n',city='<NULL>'".
     454                " where cidr='$cidr'");
     455        $sth->execute;
     456        # Set up query to start compacting free blocks.
     457        $sth = $dbh->prepare("select * from freeblocks where ".
     458                "maskbits<=".$cidr->masklen." and routed='n' order by maskbits desc");
     459
     460      } else { # end alloctype routing case
     461
     462        $sth = $dbh->prepare("delete from allocations where cidr='$cidr'");
     463        $sth->execute;
     464        # Special case - delete pool IPs
     465        if ($type =~ /^.p$/) {
     466          # We have to delete the IPs from the pool listing.
     467          $sth = $dbh->prepare("delete from poolips where pool='$cidr'");
     468          $sth->execute;
     469        }
     470
     471        # Set up query for compacting free blocks.
     472        $sth = $dbh->prepare("select * from freeblocks where cidr <<= ".
     473                "(select cidr from routed where cidr >>= '$cidr') ".
     474                " and maskbits<=".$cidr->masklen." and routed='y' order by maskbits desc");
     475
     476      } # end alloctype general case
     477
     478      # Now we look for larger-or-equal-sized free blocks in the same master (routed)
     479      # (super)block. If there aren't any, we can't combine blocks anyway.  If there
     480      # are, we check to see if we can combine blocks.
     481      # Execute the statement prepared in the if-else above.
     482
     483      $sth->execute;
     484
     485# NetAddr::IP->compact() attempts to produce the smallest inclusive block
     486# from the caller and the passed terms.
     487# EG:  if you call $cidr->compact($ip1,$ip2,$ip3) when $cidr, $ip1, $ip2,
     488#       and $ip3 are consecutive /27's starting on .0 (.0-.31, .32-.63,
     489#       .64-.95, and .96-.128), you will get an array containing a single
     490#       /25 as element 0 (.0-.127).  Order is not important;  you could have
     491#       $cidr=.32/27, $ip1=.96/27, $ip2=.0/27, and $ip3=.64/27.
     492
     493      my (@together, @combinelist);
     494      my $i=0;
     495      while (my @data = $sth->fetchrow_array) {
     496        my $testIP = new NetAddr::IP $data[0];
     497        @together = $testIP->compact($cidr);
     498        my $num = @together;
     499        if ($num == 1) {
     500          $cidr = $together[0];
     501          $combinelist[$i++] = $testIP;
     502        }
     503      }
     504
     505      # Clear old freeblocks entries - if any.  $i==0 if not.
     506      if ($i>0) {
     507        $sth = $dbh->prepare("delete from freeblocks where cidr=?");
     508        foreach my $block (@combinelist) {
     509          $sth->execute("$block");
     510        }
     511      }
     512
     513      # insert "new" freeblocks entry
     514      if ($type eq 'rr') {
     515        $sth = $dbh->prepare("insert into freeblocks values ('$cidr',".$cidr->masklen.
     516                ",'<NULL>','n')");
     517      } else {
     518        $sth = $dbh->prepare("insert into freeblocks values ('$cidr',".$cidr->masklen.
     519                ",(select city from routed where cidr >>= '$cidr'),'y')");
     520      }
     521      $sth->execute;
     522
     523      # If we got here, we've succeeded.  Whew!
     524      $dbh->commit;
     525    }; # end eval
     526    if ($@) {
     527      eval { $dbh->rollback; };
     528      return ('FAIL', $msg);
     529    } else {
     530      return ('OK',"OK");
     531    }
     532
     533  } # end alloctype != netblock
     534
     535} # end deleteBlock()
     536
     537
     538## IPDB::mailNotify()
    78539# Sends notification mail to recipients regarding an IPDB operation
    79540sub mailNotify ($$$) {
     
    84545  $mailer->to($recip);
    85546  $mailer->data("From: \"IP Database\" <ipdb\@example.com>\n",
    86         "To: $recip\n",
    87547        "Date: ".strftime("%a, %d %b %Y %H:%M:%S %z",localtime)."\n",
    88548        "Subject: {IPDB} $subj\n",
  • branches/stable/cgi-bin/consistency-check.pl

    r64 r125  
    1010
    1111use DBI;
    12 use IPDB qw(:ALL);
     12use IPDB 2.0 qw(:ALL);
    1313use NetAddr::IP;
    1414
    15 $dbh = connectDB;
     15($dbh,$errstr) = connectDB("ipdb", "ipdb", "ipdbpwd");
    1616
    1717# Schlep up the masters
  • branches/stable/cgi-bin/freespace.pl

    r117 r125  
    88# Last update by $Author$
    99###
     10# Copyright (C) 2004 - Kris Deugau
    1011
    1112use DBI;
    12 use IPDB qw(:ALL);
     13use IPDB 2.0 qw(:ALL);
    1314use NetAddr::IP;
    1415
    15 $dbh = connectDB;
     16($dbh,errstr) = connectDB("ipdb", "ipdb", "ipdbpwd");
    1617
    1718print "Content-type: text/plain\n\n";
  • branches/stable/cgi-bin/main.cgi

    r124 r125  
    1414use DBI;
    1515use CommonWeb qw(:ALL);
    16 use IPDB qw(:ALL);
     16use IPDB 2.0 qw(:ALL);
    1717use CustIDCK;
    1818use POSIX qw(ceil);
     
    3333syslog "debug", "$authuser active";
    3434
    35 checkDBSanity();
     35# Why not a global DB handle?  (And a global statement handle, as well...)
     36# Use the connectDB function, otherwise we end up confusing ourselves
     37my $ip_dbh;
     38my $sth;
     39my $errstr;
     40($ip_dbh,$errstr) = connectDB("ipdb", "ipdb", "ipdbpwd");
     41if (!$ip_dbh) {
     42  printAndExit("Failed to connect to database: $errstr\n");
     43}
     44checkDBSanity($ip_dbh);
     45initIPDBGlobals($ip_dbh);
    3646
    3747#prototypes
     
    4858
    4959# Stuff that gets loaded from the database
    50 my @masterblocks;
    51 my @citylist;
    52 my @poplist;
    53 my %disp_alloctypes;
    54 my %list_alloctypes;
    55 my %allocated;  # Count for allocated blocks in a master block
    56 my %free;       # Count for free blocks (routed and unrouted) in a master block
    57 my %bigfree;    # Tracking largest free block in a master block
    58 my %routed;     # Number of routed blocks in a master block
    59 
    60 # Why not a global DB handle?  (And a global statement handle, as well...)
    61 # We already know the DB is happy, (checkDBSanity) otherwise we wouldn't be here.
    62 # Use the connectDB function, otherwise we end up confusing ourselves
    63 my $ip_dbh = connectDB;
    64 my $sth;
     60#my @citylist;
     61#my @poplist;
     62#my %allocated; # Count for allocated blocks in a master block
     63#my %free;      # Count for free blocks (routed and unrouted) in a master block
     64#my %bigfree;   # Tracking largest free block in a master block
     65#my %routed;    # Number of routed blocks in a master block
    6566
    6667# Slurp up the master block list - we need this several places
    6768# While we're at it, initialize the related hashes.
    68 $sth = $ip_dbh->prepare("select * from masterblocks order by cidr");
    69 $sth->execute;
    70 for (my $i=0; my @data = $sth->fetchrow_array(); $i++) {
    71   $masterblocks[$i] = new NetAddr::IP $data[0];
    72   $allocated{"$masterblocks[$i]"} = 0;
    73   $free{"$masterblocks[$i]"} = 0;
    74   $bigfree{"$masterblocks[$i]"} = 128;  # Larger number means smaller block.
    75                                         # Set to 128 to prepare for IPv6
    76   $routed{"$masterblocks[$i]"} = 0;
    77 }
    78 
    79 # Initialize the city and poplist arrays
    80 $sth = $ip_dbh->prepare("select * from cities order by city");
    81 $sth->execute;
    82 my $i = 0;
    83 my $j = 0;
    84 while (my @data = $sth->fetchrow_array) {
    85   $citylist[$i++] = $data[0];
    86   if ($data[1] eq 'y') {
    87     $poplist[$j++] = $data[0];
    88   }
    89 }
    90 
    91 # Initialize alloctypes hashes
    92 $sth = $ip_dbh->prepare("select * from alloctypes order by listorder");
    93 $sth->execute;
    94 while (my @data = $sth->fetchrow_array) {
    95   $disp_alloctypes{$data[0]} = $data[2];
    96   if ($data[3] < 900) {
    97     $list_alloctypes{$data[0]} = $data[1];
    98   }
    99 }
     69#$sth = $ip_dbh->prepare("select * from masterblocks order by cidr");
     70#$sth->execute;
     71#for (my $i=0; my @data = $sth->fetchrow_array(); $i++) {
     72#  $masterblocks[$i] = new NetAddr::IP $data[0];
     73#  $allocated{"$masterblocks[$i]"} = 0;
     74#  $free{"$masterblocks[$i]"} = 0;
     75#  $bigfree{"$masterblocks[$i]"} = 128; # Larger number means smaller block.
     76#                                       # Set to 128 to prepare for IPv6
     77#  $routed{"$masterblocks[$i]"} = 0;
     78#}
     79
    10080
    10181
     
    11494  my $cidr = new NetAddr::IP $webvar{cidr};
    11595
    116   print "<div type=heading align=center>Adding $cidr as master block....\n";
     96  print "<div type=heading align=center>Adding $cidr as master block....</div>\n";
    11797
    11898  # Allow transactions, and raise an exception on errors so we can catch it later.
     
    142122    eval { $ip_dbh->rollback; };
    143123    syslog "err", "Could not add master block '$webvar{cidr}' to database: '$@'";
    144     printAndExit("Could not add master block $webvar{cidr} to database: $@");
    145   }
    146 
    147   print "Success!</div>\n";
    148 
    149   printFooter;
     124    printError("Could not add master block $webvar{cidr} to database: $@");
     125  } else {
     126    print "<div type=heading align=center>Success!</div>\n";
     127    syslog "info", "$authuser added master block $webvar{cidr}";
     128  }
     129
    150130} # end add new master
    151131
     
    169149    viewBy($webvar{searchfor}, $webvar{input});
    170150  }
    171   printFooter();
    172151}
    173152
     
    208187  printAndExit("Error $boing:  ".$excuses[$rnd/30.0]);
    209188}
    210 
    211 
    212 #end main()
    213 
    214 # Shut up error log warning about not disconnecting.  Maybe.
    215 $ip_dbh->disconnect;
    216 # Just in case something waaaayyy down isn't in place properly...
    217 exit 0;
     189## Finally! Done with that NASTY "case" emulation!
     190
     191
     192
     193# Clean up IPDB globals, DB handle, etc.
     194finish($ip_dbh);
     195# We print the footer here, so we don't have to do it elsewhere.
     196printFooter;
     197# Just in case something waaaayyy down isn't in place
     198# properly... we exit explicitly.
     199exit;
     200
    218201
    219202
     
    314297    } else {
    315298      # This shouldn't happen, but if it does, whoever gets it deserves what they get...
    316       printAndExit("Invalid query.");
     299      printError("Invalid query.");
    317300    }
    318301  } else {
    319302    # This shouldn't happen, but if it does, whoever gets it deserves what they get...
    320     printAndExit("Invalid searchfor.");
     303    printError("Invalid searchfor.");
    321304  }
    322305} # viewBy
     
    425408
    426409# Initial display:  Show master blocks with total allocated subnets, total free subnets
    427 sub showSummary
    428 {
     410sub showSummary {
     411  # this is horrible-ugly-bad and will Go Away real soon now(TM)
    429412  print "Content-type: text/html\n\n";
    430413
     
    432415        'Free netblocks', 'Largest free block');
    433416
    434 # Snag the allocations.
    435 # I think it's too confusing to leave out internal allocations.
    436   $sth = $ip_dbh->prepare("select * from allocations");
    437   $sth->execute();
    438   while (my @data = $sth->fetchrow_array()) {
    439     # cidr,custid,type,city,description
    440     # We only need the cidr
    441     my $cidr = new NetAddr::IP $data[0];
    442     foreach my $master (@masterblocks) {
    443       if ($master->contains($cidr)) {
    444         $allocated{"$master"}++;
    445       }
    446     }
    447   }
    448 
    449 # Snag routed blocks
    450   $sth = $ip_dbh->prepare("select * from routed");
    451   $sth->execute();
    452   while (my @data = $sth->fetchrow_array()) {
    453     # cidr,maskbits,city
    454     # We only need the cidr
    455     my $cidr = new NetAddr::IP $data[0];
    456     foreach my $master (@masterblocks) {
    457       if ($master->contains($cidr)) {
    458         $routed{"$master"}++;
    459       }
    460     }
    461   }
    462 
    463 # Snag the free blocks.
    464   $sth = $ip_dbh->prepare("select * from freeblocks");
    465   $sth->execute();
    466   while (my @data = $sth->fetchrow_array()) {
    467     # cidr,maskbits,city
    468     # We only need the cidr
    469     my $cidr = new NetAddr::IP $data[0];
    470     foreach my $master (@masterblocks) {
    471       if ($master->contains($cidr)) {
    472         $free{"$master"}++;
    473         if ($cidr->masklen < $bigfree{"$master"}) { $bigfree{"$master"} = $cidr->masklen; }
    474       }
    475     }
    476   }
    477 
    478 # Print the data.
     417  my %allocated;
     418  my %free;
     419  my %routed;
     420  my %bigfree;
     421
     422  # Count the allocations.
     423  $sth = $ip_dbh->prepare("select count(*) from allocations where cidr <<= ?");
     424  foreach my $master (@masterblocks) {
     425    $sth->execute("$master");
     426    $sth->bind_columns(\$allocated{"$master"});
     427    $sth->fetch();
     428  }
     429
     430  # Count routed blocks
     431  $sth = $ip_dbh->prepare("select count(*) from routed where cidr <<= ?");
     432  foreach my $master (@masterblocks) {
     433    $sth->execute("$master");
     434    $sth->bind_columns(\$routed{"$master"});
     435    $sth->fetch();
     436  }
     437
     438  # Count the free blocks.
     439  $sth = $ip_dbh->prepare("select count(*) from freeblocks where cidr <<= ?");
     440  foreach my $master (@masterblocks) {
     441    $sth->execute("$master");
     442    $sth->bind_columns(\$free{"$master"});
     443    $sth->fetch();
     444  }
     445
     446  # Find the largest free block in each master
     447  $sth = $ip_dbh->prepare("select maskbits from freeblocks where cidr <<= ? order by maskbits limit 1");
     448  foreach my $master (@masterblocks) {
     449    $sth->execute("$master");
     450    $sth->bind_columns(\$bigfree{"$master"});
     451    $sth->fetch();
     452  }
     453
     454  # Print the data.
    479455  my $count=0;
    480456  foreach my $master (@masterblocks) {
     
    492468  print "Note:  Free blocks noted here include both routed and unrouted blocks.\n";
    493469
    494   # Because of the way this sub gets called, we don't need to print the footer here.
    495   # (index.shtml makes an SSI #include call to cgi-bin/main.cgi?action=index)
    496   # If we do, the footer comes in twice...
    497   #printFooter;
    498470} # showSummary
    499471
     
    510482        qq($webvar{block}:</div></center><br>\n);
    511483
     484  my %allocated;
     485  my %free;
     486  my %routed;
     487  my %bigfree;
     488
    512489  my $master = new NetAddr::IP $webvar{block};
    513490  my @localmasters;
    514491
    515   $sth = $ip_dbh->prepare("select * from routed order by cidr");
     492  # Fetch only the blocks relevant to this master
     493  $sth = $ip_dbh->prepare("select * from routed where cidr <<= '$master' order by cidr");
    516494  $sth->execute();
    517495
     
    519497  while (my @data = $sth->fetchrow_array()) {
    520498    my $cidr = new NetAddr::IP $data[0];
    521     if ($master->contains($cidr)) {
    522       $localmasters[$i++] = $cidr;
    523       $free{"$cidr"} = 0;
    524       $allocated{"$cidr"} = 0;
     499    $localmasters[$i++] = $cidr;
     500    $free{"$cidr"} = 0;
     501    $allocated{"$cidr"} = 0;
     502    $bigfree{"$cidr"} = 128;
    525503    # Retain the routing destination
    526       $routed{"$cidr"} = $data[2];
    527     }
    528   }
    529 
    530 # Check if there were actually any blocks routed from this master
     504    $routed{"$cidr"} = $data[2];
     505  }
     506
     507  # Check if there were actually any blocks routed from this master
    531508  if ($i > 0) {
    532509    startTable('Routed block','Routed to','Allocated blocks',
    533510        'Free blocks','Largest free block');
    534511
    535   # Count the allocations
    536     $sth = $ip_dbh->prepare("select * from allocations");
    537     $sth->execute();
    538     while (my @data = $sth->fetchrow_array()) {
    539       # cidr,custid,type,city,description
    540       # We only need the cidr
    541       my $cidr = new NetAddr::IP $data[0];
    542       foreach my $master (@localmasters) {
    543         if ($master->contains($cidr)) {
    544           $allocated{"$master"}++;
    545         }
    546       }
    547     }
    548 
    549     # initialize bigfree base points
    550     foreach my $lmaster (@localmasters) {
    551       $bigfree{"$lmaster"} = 128;
    552     }
    553 
    554     # Snag the free blocks.
    555     $sth = $ip_dbh->prepare("select * from freeblocks");
    556     $sth->execute();
    557     while (my @data = $sth->fetchrow_array()) {
    558       # cidr,maskbits,city
    559       # We only need the cidr
    560       my $cidr = new NetAddr::IP $data[0];
    561       foreach my $lmaster (@localmasters) {
    562         if ($lmaster->contains($cidr)) {
    563           $free{"$lmaster"}++;
    564           if ($cidr->masklen < $bigfree{"$lmaster"}) {
    565             $bigfree{"$lmaster"} = $cidr->masklen;
    566           }
    567         }
    568         # check for largest free block
    569       }
     512    # Count the allocations
     513    $sth = $ip_dbh->prepare("select count(*) from allocations where cidr <<= ?");
     514    foreach my $master (@localmasters) {
     515      $sth->execute("$master");
     516      $sth->bind_columns(\$allocated{"$master"});
     517      $sth->fetch();
     518    }
     519
     520    # Count the free blocks.
     521    $sth = $ip_dbh->prepare("select count(*) from freeblocks where cidr <<= ?");
     522    foreach my $master (@localmasters) {
     523      $sth->execute("$master");
     524      $sth->bind_columns(\$free{"$master"});
     525      $sth->fetch();
     526    }
     527
     528    # Get the size of the largest free block
     529    $sth = $ip_dbh->prepare("select maskbits from freeblocks where cidr <<= ? order by maskbits limit 1");
     530    foreach my $master (@localmasters) {
     531      $sth->execute("$master");
     532      $sth->bind_columns(\$bigfree{"$master"});
     533      $sth->fetch();
    570534    }
    571535
     
    603567  # Snag the free blocks.
    604568  my $count = 0;
    605   $sth = $ip_dbh->prepare("select * from freeblocks where routed='n' order by cidr");
     569  $sth = $ip_dbh->prepare("select cidr from freeblocks where cidr <<='$master' and ".
     570        "routed='n' order by cidr");
    606571  $sth->execute();
    607572  while (my @data = $sth->fetchrow_array()) {
    608     # cidr,maskbits,city
    609     # We only need the cidr
    610573    my $cidr = new NetAddr::IP $data[0];
    611     if ($master->contains($cidr)) {
    612       my @row = ("$cidr", $cidr->range);
    613       printRow(\@row, 'color1' ) if($count%2==0);
    614       printRow(\@row, 'color2' ) if($count%2!=0);
    615       $count++;
    616     }
     574    my @row = ("$cidr", $cidr->range);
     575    printRow(\@row, 'color1' ) if($count%2==0);
     576    printRow(\@row, 'color2' ) if($count%2!=0);
     577    $count++;
    617578  }
    618579
    619580  print "</table>\n";
    620   printFooter;
    621581} # showMaster
    622582
     
    641601        qq($master ($data[2]):</div></center><br>\n);
    642602
    643   $sth = $ip_dbh->prepare("select * from allocations order by cidr");
     603  startTable('CIDR allocation','Customer Location','Type','CustID','Description/Name');
     604
     605  # Snag the allocations for this block
     606  $sth = $ip_dbh->prepare("select * from allocations where cidr <<= '$master' order by cidr");
    644607  $sth->execute();
    645 
    646   startTable('CIDR allocation','Customer Location','Type','CustID','Description/Name');
    647608
    648609  my $count=0;
    649610  while (my @data = $sth->fetchrow_array()) {
    650     # cidr,custid,type,city,description,notes,maskbits
     611    # cidr,custid,type,city,description,notes,maskbits,circuitid
    651612    my $cidr = new NetAddr::IP $data[0];
    652     if (!$master->contains($cidr)) { next; }
    653613
    654614    # Clean up extra spaces that are borking things.
     
    691651  # unrouted free blocks, but it's better to let the database do the work if we can.
    692652  $count = 0;
    693   $sth = $ip_dbh->prepare("select * from freeblocks where routed='y' order by cidr");
     653  $sth = $ip_dbh->prepare("select * from freeblocks where routed='y' and cidr <<= '$master' order by cidr");
    694654  $sth->execute();
    695655  while (my @data = $sth->fetchrow_array()) {
    696656    # cidr,maskbits,city
    697657    my $cidr = new NetAddr::IP $data[0];
    698     if ($master->contains($cidr)) {
    699       my @row = ("<a href=\"/ip/cgi-bin/main.cgi?action=assign&block=$cidr\">$cidr</a>",
     658    my @row = ("<a href=\"/ip/cgi-bin/main.cgi?action=assign&block=$cidr\">$cidr</a>",
    700659        $cidr->range);
    701       printRow(\@row, 'color1') if ($count%2 == 0);
    702       printRow(\@row, 'color2') if ($count%2 != 0);
    703       $count++;
    704     }
     660    printRow(\@row, 'color1') if ($count%2 == 0);
     661    printRow(\@row, 'color2') if ($count%2 != 0);
     662    $count++;
    705663  }
    706664
    707665  print "</table>\n";
    708   printFooter;
    709666} # showRBlock
    710667
     
    762719  print "</table>\n";
    763720
    764   printFooter;
    765721} # end listPool
    766722
    767723
    768 # Should this maybe just be a full static page?  It just spews out some predefined HTML.
     724# Show "Add new allocation" page.  Note that the actual page may
     725# be one of two templates, and the lists come from the database.
    769726sub assignBlock {
    770727  printHeader('');
     
    824781  print $html;
    825782
    826   printFooter();
    827783} # assignBlock
    828784
     
    837793  # Going to manually validate some items.
    838794  # custid and city are automagic.
    839   validateInput();
    840 
    841 # This isn't always useful.
    842 #  if (!$webvar{maskbits}) {
    843 #    printAndExit("Please enter a CIDR block length.");
    844 #  }
     795  return if !validateInput();
    845796
    846797# Several different cases here.
     
    860811        " ptype='$base' and (city='Sudbury' or city='North Bay')";
    861812    } else {
    862 ## $city doesn't seem to get defined here.
    863 my $city;       # Shut up Perl's "strict" scoping/usage check.
    864813      $sql = "select * from poolips where available='y' and".
    865814        " ptype='$base' and city='$webvar{pop}'";
     
    891840
    892841      if (!$webvar{maskbits}) {
    893         printAndExit("Please specify a CIDR mask length.");
     842        printError("Please specify a CIDR mask length.");
     843        return;
    894844      }
    895845      my $sql;
     
    908858          " a set of smaller netblocks or a single smaller netblock.";
    909859      } else {
     860##fixme
     861# This section needs serious Pondering.
    910862        if ($webvar{alloctype} =~ /^[cdsmw]p$/) {
    911863          if (($webvar{city} !~ /^(Sudbury|North Bay)$/) && ($webvar{alloctype} eq 'dp')) {
    912             printAndExit("You must chose Sudbury or North Bay for DSL pools."); }
     864            printError("You must chose Sudbury or North Bay for DSL pools.");
     865            return;
     866          }
    913867          $city = $webvar{city};
    914868          $failmsg = "No suitable free block found.<br>\nYou will have to route another".
    915             " superblock <br>\nfrom one of the master blocks in Sudbury or chose a smaller".
     869            " superblock from one of the<br>\nmaster blocks in Sudbury or chose a smaller".
    916870            " block size for the pool.";
    917871        } else {
     
    933887      my @data = $sth->fetchrow_array();
    934888      if ($data[0] eq "") {
    935         printAndExit($failmsg);
     889        printError($failmsg);
     890        return;
    936891      }
    937892      $cidr = new NetAddr::IP $data[0];
     
    982937  print $html;
    983938
    984   printFooter;
    985939} # end confirmAssign
    986940
     
    990944  # Some things are done more than once.
    991945  printHeader('');
    992   validateInput();
    993 
    994   # Set some things that may be needed
    995   # Don't set $cidr here as it may not even be a valid IP address.
    996   my $alloc_from = new NetAddr::IP $webvar{alloc_from};
    997 
    998 # dynDSL (dy), sIP DSL(dp), and server pools (sp) are nominally allocated to Sudbury
    999 # no matter what else happens.
    1000 #  if ($webvar{alloctype} =~ /^([sd]p|dy)$/) { $webvar{city} = "Sudbury"; }
    1001 # OOPS.  forgot about North Bay DSL.
    1002 #### Gotta make this cleaner and more accurate
    1003 #  if ($webvar{alloctype} eq "sp") { $webvar{city} = "Sudbury"; }
    1004 
    1005 # Same ordering as confirmation page
    1006 
    1007   if ($webvar{alloctype} =~ /^[cdsmw]i$/) {
    1008     my ($base,$tmp) = split //, $webvar{alloctype};     # split into individual chars
    1009 
    1010     # We'll just have to put up with the oddities caused by SQL (un)sort order
    1011     $sth = $ip_dbh->prepare("select * from poolips where pool='$webvar{alloc_from}'".
    1012         " and available='y' order by ip");
    1013     $sth->execute;
    1014 
    1015     my @data = $sth->fetchrow_array;
    1016     my $cidr = $data[1];
    1017 
    1018     $sth = $ip_dbh->prepare("update poolips set custid='$webvar{custid}',".
    1019         "city='$webvar{city}',available='n',description='$webvar{desc}',".
    1020         "circuitid='$webvar{circid}'".
    1021         " where ip='$cidr'");
    1022     $sth->execute;
    1023     if ($sth->err) {
    1024       syslog "err", "Allocation of $cidr to $webvar{custid} by $authuser failed: ".
    1025         "'".$sth->errstr."'";
    1026       printAndExit("Allocation of $cidr to $webvar{custid} failed: '".$sth->errstr."'");
    1027     }
    1028     print qq(<div class="center"><div class="heading">The IP $cidr has been allocated to customer $webvar{custid}</div></div>);
    1029     syslog "notice", "$authuser allocated $cidr to $webvar{custid}";
    1030 # Notify tech@example.com
    1031     mailNotify('tech@example.com',"$disp_alloctypes{$webvar{alloctype}} allocation",
    1032         "$disp_alloctypes{$webvar{alloctype}} $cidr allocated to customer $webvar{custid}\n".
    1033         "Description: $webvar{desc}\n\nAllocated by: $authuser\n");
    1034 
    1035   } else { # end IP-from-pool allocation
    1036 
    1037     # Set $cidr here as it may not be a valid IP address elsewhere.
    1038     my $cidr = new NetAddr::IP $webvar{fullcidr};
    1039 
    1040 # Allow transactions, and make errors much easier to catch.
    1041 # Much as I would like to error-track specifically on each ->execute,
    1042 # that's a LOT of code, and some SQL blocks MUST be atomic at a
    1043 # multi-statement level.  :/
    1044     local $ip_dbh->{AutoCommit} = 0;    # These need to be local so we don't
    1045     local $ip_dbh->{RaiseError} = 1;    # step on our toes by accident.
    1046 
    1047     if ($webvar{fullcidr} eq $webvar{alloc_from}) {
    1048       # Easiest case- insert in one table, delete in the other, and go home.  More or less.
    1049       # insert into allocations values (cidr,custid,type,city,desc) and
    1050       # delete from freeblocks where cidr='cidr'
    1051       # For data safety on non-transaction DBs, we delete first.
    1052 
    1053       eval {
    1054         if ($webvar{alloctype} eq 'rr') {
    1055           $sth = $ip_dbh->prepare("update freeblocks set routed='y',city='$webvar{city}'".
    1056             " where cidr='$webvar{fullcidr}'");
    1057           $sth->execute;
    1058           $sth = $ip_dbh->prepare("insert into routed values ('$webvar{fullcidr}',".
    1059             $cidr->masklen.",'$webvar{city}')");
    1060           $sth->execute;
    1061         } else {
    1062           # common stuff for end-use, dialup, dynDSL, pools, etc, etc.
    1063 
    1064           # city has to be reset for DSL/server pools;  nominally to Sudbury.
    1065           ## Gotta rethink this;  DSL pools can be in North Bay as well.  :/
    1066           #if ($webvar{alloctype} =~ /^[sd]p$/) { $webvar{city} = 'Sudbury'; }
    1067 
    1068           $sth = $ip_dbh->prepare("delete from freeblocks where cidr='$webvar{fullcidr}'");
    1069           $sth->execute;
    1070 
    1071           $sth = $ip_dbh->prepare("insert into allocations values ('$webvar{fullcidr}',".
    1072             "'$webvar{custid}','$webvar{alloctype}','$webvar{city}','$webvar{desc}',".
    1073             "'$webvar{notes}',".$cidr->masklen.",'$webvar{circid}')");
    1074           $sth->execute;
    1075         } # routing vs non-routing netblock
    1076         $ip_dbh->commit;
    1077       };  # end of eval
    1078       if ($@) {
    1079         carp "Transaction aborted because $@";
    1080         eval { $ip_dbh->rollback; };
    1081         syslog "err", "Allocation of '$webvar{fullcidr}' to '$webvar{custid}' as ".
    1082                 "'$webvar{alloctype}' by $authuser failed: '$@'";
    1083         printAndExit("Allocation of $cidr as $disp_alloctypes{$webvar{alloctype}} failed.\n");
    1084       }
    1085 
    1086       # If we get here, the DB transaction has succeeded.
    1087       syslog "notice", "$authuser allocated '$webvar{fullcidr}' to '$webvar{custid}' as '$webvar{alloctype}'";
    1088 
    1089 # How to log SQL without munging too many error-checking wrappers in?
    1090 #      syslog "info", "
    1091 # We don't.  GRRR.
    1092 
    1093     } else { # webvar{fullcidr} != webvar{alloc_from}
    1094       # Hard case.  Allocation is smaller than free block.
    1095       my $wantmaskbits = $cidr->masklen;
    1096       my $maskbits = $alloc_from->masklen;
    1097 
    1098       my @newfreeblocks;        # Holds free blocks generated from splitting the source freeblock.
    1099 
    1100       my $i=0;
    1101       while ($maskbits++ < $wantmaskbits) {
    1102         my @subblocks = $alloc_from->split($maskbits);
    1103         $newfreeblocks[$i++] = $subblocks[1];
    1104       } # while
    1105 
    1106       # Begin SQL transaction block
    1107       eval {
    1108         # Delete old freeblocks entry
    1109         $sth = $ip_dbh->prepare("delete from freeblocks where cidr='$webvar{alloc_from}'");
    1110         $sth->execute();
    1111 
    1112         # now we have to do some magic for routing blocks
    1113         if ($webvar{alloctype} eq 'rr') {
    1114           # Insert the new freeblocks entries
    1115           # Note that non-routed blocks are assigned to <NULL>
    1116           $sth = $ip_dbh->prepare("insert into freeblocks values (?, ?, '<NULL>','n')");
    1117           foreach my $block (@newfreeblocks) {
    1118             $sth->execute("$block", $block->masklen);
    1119           }
    1120           # Insert the entry in the routed table
    1121           $sth = $ip_dbh->prepare("insert into routed values ('$cidr',".
    1122             $cidr->masklen.",'$webvar{city}')");
    1123           $sth->execute;
    1124           # Insert the (almost) same entry in the freeblocks table
    1125           $sth = $ip_dbh->prepare("insert into freeblocks values ('$cidr',".
    1126             $cidr->masklen.",'$webvar{city}','y')");
    1127           $sth->execute;
    1128 
    1129         } else { # done with alloctype == rr
    1130 
    1131           # Insert the new freeblocks entries
    1132           $sth = $ip_dbh->prepare("insert into freeblocks values (?, ?, (select city from routed where cidr >> '$cidr'),'y')");
    1133           foreach my $block (@newfreeblocks) {
    1134             $sth->execute("$block", $block->masklen);
    1135           }
    1136           # Insert the allocations entry
    1137           $sth = $ip_dbh->prepare("insert into allocations values ('$webvar{fullcidr}',".
    1138             "'$webvar{custid}','$webvar{alloctype}','$webvar{city}',".
    1139             "'$webvar{desc}','$webvar{notes}',".$cidr->masklen.",'$webvar{circid}')");
    1140           $sth->execute;
    1141         } # done with netblock alloctype != rr
    1142         $ip_dbh->commit;
    1143       }; # end eval
    1144       if ($@) {
    1145         carp "Transaction aborted because $@";
    1146         eval { $ip_dbh->rollback; };
    1147         syslog "err", "Allocation of '$webvar{fullcidr}' to '$webvar{custid}' as ".
    1148                 "'$webvar{alloctype}' by $authuser failed: '$@'";
    1149         printAndExit("Allocation of $cidr as $disp_alloctypes{$webvar{alloctype}} failed.\n");
    1150       }
    1151       syslog "notice", "$authuser allocated '$webvar{fullcidr}' to '$webvar{custid}' as '$webvar{alloctype}'";
    1152 
    1153     } # end fullcidr != alloc_from
    1154 
    1155     # Begin SQL transaction block
    1156     eval {
    1157       # special extra handling for pools.
    1158       # Note that this must be done for ANY pool allocation!
    1159       if ( my ($pooltype) = ($webvar{alloctype} =~ /^([cdsmw])p$/) ) {
    1160         # have to insert all pool IPs into poolips table as "unallocated".
    1161         $sth = $ip_dbh->prepare("insert into poolips values ('$webvar{fullcidr}',".
    1162           " ?, '6750400', '$webvar{city}', '$pooltype', 'y', '', '', '')");
    1163         my @poolip_list = $cidr->hostenum;
    1164         for (my $i=1; $i<=$#poolip_list; $i++) {
    1165           $sth->execute($poolip_list[$i]->addr);
    1166         }
    1167       } # end pool special
    1168       $ip_dbh->commit;
    1169     }; # end eval
    1170     if ($@) {
    1171       carp "Transaction aborted because $@";
    1172       eval { $ip_dbh->rollback; };
    1173       syslog "err", "Initialization of pool '$webvar{fullcidr}' by $authuser failed: '$@'";
    1174       printAndExit("$disp_alloctypes{$webvar{alloctype}} $webvar{fullcidr} not completely initialized.");
    1175     }
    1176     syslog "notice", "$disp_alloctypes{$webvar{alloctype}} '$webvar{fullcidr}' successfully initialized by $authuser";
    1177 
    1178     print qq(<div class="center"><div class="heading">The block $webvar{fullcidr} was sucessfully added as type '$webvar{alloctype}' ($disp_alloctypes{$webvar{alloctype}})</div></div>);
    1179 
    1180     if ($webvar{alloctype} eq 'cn') {
     946  return if !validateInput();
     947
     948  # $code is "success" vs "failure", $msg contains OK for a
     949  # successful netblock allocation, the IP allocated for static
     950  # IP, or the error message if an error occurred.
     951  my ($code,$msg) = allocateBlock($ip_dbh, $webvar{fullcidr}, $webvar{alloc_from},
     952        $webvar{custid}, $webvar{alloctype}, $webvar{city}, $webvar{desc}, $webvar{notes},
     953        $webvar{circid});
     954
     955  if ($code eq 'OK') {
     956    if ($webvar{alloctype} =~ /^.i$/) {
     957      print qq(<div class="center"><div class="heading">The IP $msg has been allocated to customer $webvar{custid}</div></div>);
    1181958      # Notify tech@example.com
    1182959      mailNotify('tech@example.com',"$disp_alloctypes{$webvar{alloctype}} allocation",
    1183         "$disp_alloctypes{$webvar{alloctype}} $cidr allocated to customer $webvar{custid}\n".
     960        "$disp_alloctypes{$webvar{alloctype}} $msg allocated to customer $webvar{custid}\n".
    1184961        "Description: $webvar{desc}\n\nAllocated by: $authuser\n");
    1185     }
    1186 
    1187   } # end static-IP vs netblock allocation
    1188 
    1189   printFooter();
     962    } else {
     963      print qq(<div class="center"><div class="heading">The block $webvar{fullcidr} was ).
     964        "sucessfully added as type '$webvar{alloctype}' ".
     965        "($disp_alloctypes{$webvar{alloctype}})</div></div>";
     966    }
     967    syslog "notice", "$authuser allocated '$webvar{fullcidr}' to '$webvar{custid}' as ".
     968        "'$webvar{alloctype}'";
     969  } else {
     970    syslog "err", "Allocation of '$webvar{fullcidr}' to '$webvar{custid}' as ".
     971        "'$webvar{alloctype}' by $authuser failed: '$msg'";
     972    printError("Allocation of $webvar{fullcidr} as $disp_alloctypes{$webvar{alloctype}}".
     973        " failed: $msg\n");
     974  }
     975
    1190976} # end insertAssign()
    1191977
     
    1196982sub validateInput {
    1197983  if ($webvar{city} eq '-') {
    1198     printAndExit("Please choose a city.");
     984    printError("Please choose a city.");
     985    return;
    1199986  }
    1200987  chomp $webvar{alloctype};
     
    1202989  if ($webvar{alloctype} =~ /^(ci|di|cn|mi|wi)$/) {
    1203990    if (!$webvar{custid}) {
    1204       printAndExit("Please enter a customer ID.");
     991      printError("Please enter a customer ID.");
     992      return;
    1205993    }
    1206994    if ($webvar{custid} !~ /^(?:\d{10}|\d{7}|STAFF)(?:-\d\d?)?$/) {
     
    1209997      # Crosscheck with ... er...  something.
    1210998      my $status = CustIDCK->custid_exist($webvar{custid});
    1211       printAndExit("Error verifying customer ID: ".$CustIDCK::ErrMsg)
    1212         if $CustIDCK::Error;
    1213       printAndExit("Customer ID not valid.  Make sure the Customer ID ".
    1214         "is correct.<br>\nUse STAFF for staff static IPs, and 6750400 for any other ".
    1215         "non-customer assignments.")
    1216         if !$status;
     999      if ($CustIDCK::Error) {
     1000        printError("Error verifying customer ID: ".$CustIDCK::ErrMsg);
     1001        return;
     1002      }
     1003      if (!$status) {
     1004        printError("Customer ID not valid.  Make sure the Customer ID ".
     1005          "is correct.<br>\nUse STAFF for staff static IPs, and 6750400 for any other ".
     1006          "non-customer assignments.");
     1007        return;
     1008      }
    12171009#"Please enter a valid customer ID- this must be a 7- or 10-digit number, or STAFF for
    12181010#static IPs for staff.");
    12191011    }
    12201012#    print "<!-- [ In validateInput().  Insert customer ID cross-check here. ] -->\n";
    1221   } elsif ($webvar{alloctype} =~ /^([cdsmw]p|si|dn|dy|dc|ee|rr|ii)$/){
     1013  } elsif ($webvar{alloctype} =~ /^([cdsmw]p|si|dn|dy|dc|ee|rr|in)$/){
    12221014    # All non-customer allocations MUST be entered with "our" customer ID.
    12231015    # I have Defined this as 6750400 for consistency.
     
    12261018      $webvar{custid} = "6750400";
    12271019    }
    1228     if ($webvar{alloctype} eq 'rr') {
    1229       my $flag;
    1230       foreach (@poplist) {
    1231         if (/^$webvar{city}$/) {
    1232           $flag = 'y'; last;
    1233         }
    1234       }
    1235       if (!$flag) {
    1236         printAndExit("Please choose a valid POP location for a routed netblock.  Valid ".
    1237                 "POP locations are currently:<br>\n".join (" - ", @poplist));
    1238       }
    1239     }
    12401020  } else {
    12411021    # Danger! Danger!  alloctype should ALWAYS be set by a dropdown.  Anyone
    12421022    # managing to call things in such a way as to cause this deserves a cryptic error.
    1243     printAndExit("Invalid alloctype");
    1244   }
    1245   return 0;
     1023    printError("Invalid alloctype");
     1024    return;
     1025  }
     1026
     1027  # Check POP location
     1028  my $flag;
     1029  if ($webvar{alloctype} eq 'rr') {
     1030    $flag = 'for a routed netblock';
     1031    foreach (@poplist) {
     1032      if (/^$webvar{city}$/) {
     1033        $flag = 'n';
     1034        last;
     1035      }
     1036    }
     1037  } else {
     1038    $flag = 'n';
     1039    if ($webvar{pop} =~ /^-$/) {
     1040      $flag = 'to route the block from/through';
     1041    }
     1042  }
     1043  if ($flag ne 'n') {
     1044    printError("Please choose a valid POP location $flag.  Valid ".
     1045        "POP locations are currently:<br>\n".join (" - ", @poplist));
     1046    return;
     1047  }
     1048
     1049  return 'OK';
    12461050} # end validateInput
    12471051
     
    13181122  print $html;
    13191123
    1320   printFooter();
    13211124} # edit()
    13221125
     
    13521155    eval { $ip_dbh->rollback; };
    13531156    syslog "err", "$authuser could not update block/IP '$webvar{block}': '$@'";
    1354     printAndExit("Could not update block/IP $webvar{block}: $@");
     1157    printError("Could not update block/IP $webvar{block}: $@");
     1158    return;
    13551159  }
    13561160
     
    13581162  syslog "notice", "$authuser updated $webvar{block}";
    13591163  open (HTML, "../updated.html")
    1360         or croak "Could not open updated.html :$!";
     1164        or croak "Could not open updated.html :$!";
    13611165  my $html = join('', <HTML>);
    13621166
     
    13761180  print $html;
    13771181
    1378   printFooter;
    13791182} # update()
    13801183
    13811184
    13821185# Delete an allocation.
    1383 sub remove
    1384 {
     1186sub remove {
    13851187  printHeader('');
    13861188  #show confirm screen.
     
    13921194  # Serves'em right for getting here...
    13931195  if (!defined($webvar{block})) {
    1394     printAndExit("Error 332");
     1196    printError("Error 332");
     1197    return;
    13951198  }
    13961199
     
    14661269
    14671270  print $html;
    1468   printFooter;
    14691271} # end edit()
    14701272
     
    14771279  printHeader('');
    14781280
    1479   # Enable transactions and exception-on-errors... but only for this sub
    1480   local $ip_dbh->{AutoCommit} = 0;
    1481   local $ip_dbh->{RaiseError} = 1;
    1482 
    1483   if ($webvar{alloctype} =~ /^[cdsmw]i$/) {
    1484 
    1485     eval {
    1486       $sth = $ip_dbh->prepare("select * from poolips where ip='$webvar{block}'");
    1487       $sth->execute;
    1488       my @data = $sth->fetchrow_array;
    1489       $sth = $ip_dbh->prepare("select city from allocations where cidr='$data[0]'");
    1490       $sth->execute;
    1491       @data = $sth->fetchrow_array;
    1492       $sth = $ip_dbh->prepare("update poolips set custid='6750400', available='y',".
    1493         " city='$data[0]', description='', notes='', circuitid='' where ip='$webvar{block}'");
    1494       $sth->execute;
    1495       $ip_dbh->commit;
    1496     };
    1497     if ($@) {
    1498       carp "Transaction aborted because $@";
    1499       eval { $ip_dbh->rollback; };
    1500       syslog "err", "$authuser could not deallocate static IP '$webvar{block}': '$@'";
    1501       printAndExit("Could not deallocate static IP $webvar{block}: $@");
    1502     }
     1281  my ($code,$msg) = deleteBlock($ip_dbh, $webvar{block}, $webvar{alloctype});
     1282
     1283  if ($code eq 'OK') {
    15031284    print "<div class=heading align=center>Success!  $webvar{block} deallocated.</div>\n";
    1504     syslog "notice", "$authuser deallocated static IP $webvar{block}";
    1505 
    1506   } elsif ($webvar{alloctype} eq 'mm') { # end alloctype = [cdsmw]i
    1507 
    1508     eval {
    1509       $sth = $ip_dbh->prepare("delete from masterblocks where cidr='$webvar{block}'");
    1510       $sth->execute;
    1511       $sth = $ip_dbh->prepare("delete from freeblocks where cidr='$webvar{block}'");
    1512       $sth->execute;
    1513       $ip_dbh->commit;
    1514     };
    1515     if ($@) {
    1516       carp "Transaction aborted because $@";
    1517       eval { $ip_dbh->rollback; };
    1518       syslog "err", "$authuser could not remove master block '$webvar{block}': '$@'";
    1519       printAndExit("Could not remove master block $webvar{block}: $@");
    1520     }
    1521     print "<div class=heading align=center>Success!  Master $webvar{block} removed.</div>\n";
    1522     syslog "notice", "$authuser removed master block $webvar{block}";
    1523 
    1524   } else { # end alloctype master block case
    1525 
    1526     ## This is a big block; but it HAS to be done in a chunk.  Any removal
    1527     ## of a netblock allocation may result in a larger chunk of free
    1528     ## contiguous IP space - which may in turn be combined into a single
    1529     ## netblock rather than a number of smaller netblocks.
    1530 
    1531     eval {
    1532 
    1533       my $cidr = new NetAddr::IP $webvar{block};
    1534       if ($webvar{alloctype} eq 'rr') {
    1535 
    1536         $sth = $ip_dbh->prepare("delete from routed where cidr='$webvar{block}'");
    1537         $sth->execute;
    1538         # Make sure block getting deleted is properly accounted for.
    1539         $sth = $ip_dbh->prepare("update freeblocks set routed='n',city='<NULL>'".
    1540                 " where cidr='$webvar{block}'");
    1541         $sth->execute;
    1542         # Set up query to start compacting free blocks.
    1543         $sth = $ip_dbh->prepare("select * from freeblocks where ".
    1544                 "maskbits<=".$cidr->masklen." and routed='n' order by maskbits desc");
    1545 
    1546       } else { # end alloctype routing case
    1547 
    1548         $sth = $ip_dbh->prepare("delete from allocations where cidr='$webvar{block}'");
    1549         $sth->execute;
    1550         # Special case - delete pool IPs
    1551         if ($webvar{alloctype} =~ /^[cdsmw]p$/) {
    1552           # We have to delete the IPs from the pool listing.
    1553           $sth = $ip_dbh->prepare("delete from poolips where pool='$webvar{block}'");
    1554           $sth->execute;
    1555         }
    1556 
    1557         # Set up query for compacting free blocks.
    1558         $sth = $ip_dbh->prepare("select * from freeblocks where cidr << ".
    1559                 "(select cidr from routed where cidr >> '$cidr') ".
    1560                 " and maskbits<=".$cidr->masklen." and routed='y' order by maskbits desc");
    1561 
    1562       } # end alloctype general case
    1563 
    1564 ##TEMP
    1565 ## Temporary wrapper to "properly" deallocate sIP PPPoE/DSL "netblocks" in 209.91.185.0/24
    1566 my $staticpool = new NetAddr::IP "209.91.185.0/24";
    1567 ##TEMP
    1568 if ($cidr->within($staticpool)) {
    1569 ##TEMP
    1570   # We've already deleted the block, now we have to stuff its IPs into the pool.
    1571   $sth = $ip_dbh->prepare("insert into poolips values ('209.91.185.0/24',?,'6750400','Sudbury','d','y','','','')");
    1572   $sth->execute($cidr->addr);
    1573   foreach my $ip ($cidr->hostenum) {
    1574     $sth->execute("$ip");
    1575   }
    1576   $cidr--;
    1577   $sth->execute($cidr->addr);
    1578 
    1579 ##TEMP
    1580 } else {
    1581 ##TEMP
    1582 
    1583       # Now we look for larger-or-equal-sized free blocks in the same master (routed)
    1584       # (super)block. If there aren't any, we can't combine blocks anyway.  If there
    1585       # are, we check to see if we can combine blocks.
    1586       # Execute the statement prepared in the if-else above.
    1587 
    1588       $sth->execute;
    1589 
    1590 # NetAddr::IP->compact() attempts to produce the smallest inclusive block
    1591 # from the caller and the passed terms.
    1592 # EG:  if you call $cidr->compact($ip1,$ip2,$ip3) when $cidr, $ip1, $ip2,
    1593 #       and $ip3 are consecutive /27's starting on .0 (.0-.31, .32-.63,
    1594 #       .64-.95, and .96-.128), you will get an array containing a single
    1595 #       /25 as element 0 (.0-.127).  Order is not important;  you could have
    1596 #       $cidr=.32/27, $ip1=.96/27, $ip2=.0/27, and $ip3=.64/27.
    1597 
    1598       my (@together, @combinelist);
    1599       my $i=0;
    1600       while (my @data = $sth->fetchrow_array) {
    1601         my $testIP = new NetAddr::IP $data[0];
    1602         @together = $testIP->compact($cidr);
    1603         my $num = @together;
    1604         if ($num == 1) {
    1605           $cidr = $together[0];
    1606           $combinelist[$i++] = $testIP;
    1607         }
    1608       }
    1609 
    1610       # Clear old freeblocks entries - if any.  $i==0 if not.
    1611       if ($i>0) {
    1612         $sth = $ip_dbh->prepare("delete from freeblocks where cidr=?");
    1613         foreach my $block (@combinelist) {
    1614           $sth->execute("$block");
    1615         }
    1616       }
    1617 
    1618       # insert "new" freeblocks entry
    1619       if ($webvar{alloctype} eq 'rr') {
    1620         $sth = $ip_dbh->prepare("insert into freeblocks values ('$cidr',".$cidr->masklen.
    1621                 ",'<NULL>','n')");
    1622       } else {
    1623         $sth = $ip_dbh->prepare("insert into freeblocks values ('$cidr',".$cidr->masklen.
    1624                 ",(select city from routed where cidr >>= '$cidr'),'y')");
    1625       }
    1626       $sth->execute;
    1627 
    1628 ##TEMP
    1629 }
    1630 ##TEMP
    1631 
    1632       # If we got here, we've succeeded.  Whew!
    1633       $ip_dbh->commit;
    1634     }; # end eval
    1635     if ($@) {
    1636       carp "Transaction aborted because $@";
    1637       eval { $ip_dbh->rollback; };
    1638       syslog "err", "$authuser could not deallocate netblock '$webvar{block}': '$@'";
    1639       printAndExit("Could not deallocate netblock $webvar{block}: $@");
    1640     }
    1641     print "<div class=heading align=center>Success!  $webvar{block} deleted.</div>\n";
    16421285    syslog "notice", "$authuser deallocated '$webvar{alloctype}'-type netblock $webvar{block}";
    1643 
    1644   } # end alloctype != netblock
    1645 
    1646   printFooter;
     1286  } else {
     1287    if ($webvar{alloctype} =~ /^.i$/) {
     1288      syslog "err", "$authuser could not deallocate static IP '$webvar{block}': '$msg'";
     1289      printError("Could not deallocate static IP $webvar{block}: $msg");
     1290    } else {
     1291      syslog "err", "$authuser could not deallocate netblock '$webvar{block}': '$msg'";
     1292      printError("Could not deallocate netblock $webvar{block}: $msg");
     1293    }
     1294  }
     1295
    16471296} # finalDelete
    16481297
  • branches/stable/index.shtml

    r4 r125  
    11<!--#include file="header.inc"-->
    22<!--#include virtual="/ip/cgi-bin/main.cgi?action=index" -->
    3 <!--#include file="footer.inc"-->
Note: See TracChangeset for help on using the changeset viewer.