X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fsvc_broadband.pm;h=26659d52a791d17046c2680e87e13177d4c1dacb;hb=36e4318e1ccec27ae76a3d1505718a3d47af67c9;hp=a327f46e22b00753b101f985da0d8ebb6169ce1a;hpb=d64984bbfe839fbe71830862a6841380425f3dbe;p=freeside.git diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm index a327f46e2..26659d52a 100755 --- a/FS/FS/svc_broadband.pm +++ b/FS/FS/svc_broadband.pm @@ -101,15 +101,15 @@ sub table_info { 'description' => 'Descriptive label for this particular device', 'speed_down' => 'Maximum download speed for this service in Kbps. 0 denotes unlimited.', 'speed_up' => 'Maximum upload speed for this service in Kbps. 0 denotes unlimited.', - #'ip_addr' => 'IP address. Leave blank for automatic assignment.', - #'blocknum' => - #{ 'label' => 'Address block', - # 'type' => 'select', - # 'select_table' => 'addr_block', - # 'select_key' => 'blocknum', - # 'select_label' => 'cidr', - # 'disable_inventory' => 1, - # }, + 'ip_addr' => 'IP address. Leave blank for automatic assignment.', + 'blocknum' => + { 'label' => 'Address block', + 'type' => 'select', + 'select_table' => 'addr_block', + 'select_key' => 'blocknum', + 'select_label' => 'cidr', + 'disable_inventory' => 1, + }, 'plan_id' => 'Service Plan Id', 'performance_profile' => 'Peformance Profile', 'authkey' => 'Authentication key', @@ -135,7 +135,7 @@ sub table_info { sub table { 'svc_broadband'; } -sub table_dupcheck_fields { ( 'mac_addr' ); } +sub table_dupcheck_fields { ( 'ip_addr', 'mac_addr' ); } =item search HASHREF @@ -245,6 +245,12 @@ sub search { push @where, "svcpart = $1"; } + #exportnum + if ( $params->{'exportnum'} =~ /^(\d+)$/ ) { + push @from, 'LEFT JOIN export_svc USING ( svcpart )'; + push @where, "exportnum = $1"; + } + #ip_addr if ( $params->{'ip_addr'} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/ ) { push @where, "ip_addr = '$1'"; @@ -406,7 +412,13 @@ sub check { } my $agentnum = $cust_pkg->cust_main->agentnum if $cust_pkg; - if ($self->routernum) { + if ( $conf->exists('auto_router') and $self->ip_addr and !$self->routernum ) { + # assign_router is guaranteed to provide a router that's legal + # for this agent and svcpart + my $error = $self->_check_ip_addr || $self->assign_router; + return $error if $error; + } + elsif ($self->routernum) { return "Router ".$self->routernum." does not provide this service" unless qsearchs('part_svc_router', { svcpart => $svcpart, @@ -415,15 +427,22 @@ sub check { my $router = $self->router; return "Router ".$self->routernum." does not serve this customer" - if $router->agentnum and $router->agentnum != $agentnum; + if $router->agentnum and $agentnum and $router->agentnum != $agentnum; - if ( $router->auto_addr ) { - my $error = $self->assign_ip_addr; - return $error if $error; + if ( $router->manual_addr ) { + $self->blocknum(''); } else { - $self->blocknum(''); + my $addr_block = $self->addr_block; + if ( $self->ip_addr eq '' + and not ( $addr_block and $addr_block->manual_flag ) ) { + my $error = $self->assign_ip_addr; + return $error if $error; + } } + + my $error = $self->_check_ip_addr; + return $error if $error; } # if $self->routernum if ( $cust_pkg && ! $self->latitude && ! $self->longitude ) { @@ -437,15 +456,12 @@ sub check { } } - $error = $self->_check_ip_addr; - return $error if $error; - $self->SUPER::check; } =item assign_ip_addr -Assign an address block matching the selected router, and the selected block +Assign an IP address matching the selected router, and the selected block if there is one. =cut @@ -455,7 +471,7 @@ sub assign_ip_addr { my @blocks; my $ip_addr; - if ( $self->blocknum and $self->addr_block->routernum == $self->routernum ) { + if ( $self->addr_block and $self->addr_block->routernum == $self->routernum ) { # simple case: user chose a block, find an address in that block # (this overrides an existing IP address if it's not in the block) @blocks = ($self->addr_block); @@ -466,6 +482,7 @@ sub assign_ip_addr { else { return ''; } +#warn "assigning ip address in blocks\n".join("\n",map{$_->cidr} @blocks)."\n"; foreach my $block ( @blocks ) { if ( $self->ip_addr and $block->NetAddr->contains($self->NetAddr) ) { @@ -473,15 +490,36 @@ sub assign_ip_addr { return ''; } $ip_addr = $block->next_free_addr; - last if $ip_addr; - } - if ( $ip_addr ) { - $self->set(ip_addr => $ip_addr->addr); - return ''; + if ( $ip_addr ) { + $self->set(ip_addr => $ip_addr->addr); + $self->set(blocknum => $block->blocknum); + return ''; + } } - else { - return 'No IP address available on this router'; + return 'No IP address available on this router'; +} + +=item assign_router + +Assign an address block and router matching the selected IP address. +Does nothing if IP address is null. + +=cut + +sub assign_router { + my $self = shift; + return '' if !$self->ip_addr; + #warn "assigning router/block for ".$self->ip_addr."\n"; + foreach my $router ($self->allowed_routers) { + foreach my $block ($router->addr_block) { + if ( $block->NetAddr->contains($self->NetAddr) ) { + $self->blocknum($block->blocknum); + $self->routernum($block->routernum); + return ''; + } + } } + return $self->ip_addr.' is not in an allowed block.'; } sub _check_ip_addr { @@ -491,6 +529,15 @@ sub _check_ip_addr { return '' if $conf->exists('svc_broadband-allow_null_ip_addr'); return 'IP address required'; } + else { + return 'Cannot parse address: '.$self->ip_addr unless $self->NetAddr; + } + + if ( $self->addr_block + and not $self->addr_block->NetAddr->contains($self->NetAddr) ) { + return 'Address '.$self->ip_addr.' not in block '.$self->addr_block->cidr; + } + # if (my $dup = qsearchs('svc_broadband', { # ip_addr => $self->ip_addr, # svcnum => {op=>'!=', value => $self->svcnum} @@ -502,11 +549,18 @@ sub _check_ip_addr { sub _check_duplicate { my $self = shift; - - return "MAC already in use" - if ( $self->mac_addr && - scalar( qsearch( 'svc_broadband', { 'mac_addr', $self->mac_addr } ) ) - ); + # Not a reliable check because the table isn't locked, but + # that's why we have a unique index. This is just to give a + # friendlier error message. + my @dup; + @dup = $self->find_duplicates('global', 'ip_addr'); + if ( @dup ) { + return "IP address in use (svcnum ".$dup[0]->svcnum.")"; + } + @dup = $self->find_duplicates('global', 'mac_addr'); + if ( @dup ) { + return "MAC address in use (svcnum ".$dup[0]->svcnum.")"; + } ''; } @@ -555,28 +609,69 @@ Returns a list of allowed FS::router objects. sub allowed_routers { my $self = shift; my $svcpart = $self->svcnum ? $self->cust_svc->svcpart : $self->svcpart; - map { $_->router } qsearch('part_svc_router', - { svcpart => $self->cust_svc->svcpart }); + my @r = map { $_->router } qsearch('part_svc_router', + { svcpart => $svcpart }); + if ( $self->cust_main ) { + my $agentnum = $self->cust_main->agentnum; + return grep { !$_->agentnum or $_->agentnum == $agentnum } @r; + } + else { + return @r; + } } =back +=item mac_addr_formatted CASE DELIMITER + +Format the MAC address (for use by exports). If CASE starts with "l" +(for "lowercase"), it's returned in lowercase. DELIMITER is inserted +between octets. + +=cut + +sub mac_addr_formatted { + my $self = shift; + my ($case, $delim) = @_; + my $addr = $self->mac_addr; + $addr = lc($addr) if $case =~ /^l/i; + join( $delim || '', $addr =~ /../g ); +} + #class method sub _upgrade_data { my $class = shift; + local($FS::svc_Common::noexport_hack) = 1; + # set routernum to addr_block.routernum foreach my $self (qsearch('svc_broadband', { blocknum => {op => '!=', value => ''}, routernum => '' })) { my $addr_block = $self->addr_block; - if ( my $routernum = $addr_block->routernum ) { + if ( !$addr_block ) { + # super paranoid mode + warn "WARNING: svcnum ".$self->svcnum." is assigned to addr_block ".$self->blocknum.", which does not exist; skipped.\n"; + next; + } + my $ip_addr = $self->ip_addr; + my $routernum = $addr_block->routernum; + if ( $routernum ) { $self->set(routernum => $routernum); - my $error = $self->replace; - die "error assigning routernum $routernum to service ".$self->svcnum. - ":\n$error\n" + my $error = $self->check; + # sanity check: don't allow this to change IP address or block + # (other than setting blocknum to null for a non-auto-assigned router) + if ( $self->ip_addr ne $ip_addr + or ($self->blocknum and $self->blocknum != $addr_block->blocknum)) { + warn "WARNING: Upgrading service ".$self->svcnum." would change its block/address; skipped.\n"; + next; + } + + $error ||= $self->replace; + warn "WARNING: error assigning routernum $routernum to service ".$self->svcnum. + ":\n$error; skipped\n" if $error; } else { @@ -584,9 +679,43 @@ sub _upgrade_data { ": no routernum in address block ".$addr_block->cidr.", skipped\n"; } } + + # assign blocknums to services that should have them + my @all_blocks = qsearch('addr_block', { }); + SVC: foreach my $self ( + qsearch({ + 'select' => 'svc_broadband.*', + 'table' => 'svc_broadband', + 'addl_from' => 'JOIN router USING (routernum)', + 'hashref' => {}, + 'extra_sql' => 'WHERE svc_broadband.blocknum IS NULL '. + 'AND router.manual_addr IS NULL', + }) + ) { + + next SVC if $self->ip_addr eq ''; + my $NetAddr = $self->NetAddr; + # inefficient, but should only need to run once + foreach my $block (@all_blocks) { + if ($block->NetAddr->contains($NetAddr)) { + $self->set(blocknum => $block->blocknum); + my $error = $self->replace; + warn "WARNING: error assigning blocknum ".$block->blocknum. + " to service ".$self->svcnum."\n$error; skipped\n" + if $error; + next SVC; + } + } + warn "WARNING: no block found containing ".$NetAddr->addr." for service ". + $self->svcnum; + #next SVC; + } + ''; } +=back + =head1 BUGS The business with sb_field has been 'fixed', in a manner of speaking.