X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fsvc_broadband.pm;h=df5a438d6ed5cbfbc979eb0d44b6c11abb805ffa;hb=311711639b41a5b307243d99c1aefceadcc97548;hp=18086016b1049609c0a09f595e929c5cf5423beb;hpb=71d596e226c4bc1b6a2522edadb99c63b448e031;p=freeside.git diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm index 18086016b..df5a438d6 100755 --- a/FS/FS/svc_broadband.pm +++ b/FS/FS/svc_broadband.pm @@ -1,9 +1,14 @@ package FS::svc_broadband; +use base qw( + FS::svc_Radius_Mixin + FS::svc_Tower_Mixin + FS::svc_MAC_Mixin + FS::svc_Common + ); use strict; use vars qw(@ISA $conf); -use base qw(FS::svc_Radius_Mixin FS::svc_Tower_Mixin FS::svc_Common); { no warnings 'redefine'; use NetAddr::IP; } use FS::Record qw( qsearchs qsearch dbh ); use FS::svc_Common; @@ -91,9 +96,9 @@ points to. You can ask the object for a copy with the I method. sub table_info { { - 'name' => 'Broadband', - 'name_plural' => 'Broadband services', - 'longname_plural' => 'Fixed (username-less) broadband services', + 'name' => 'Wireless broadband', + 'name_plural' => 'Wireless broadband services', + 'longname_plural' => 'Fixed wireless broadband services', 'display_weight' => 50, 'cancel_weight' => 70, 'ip_field' => 'ip_addr', @@ -136,7 +141,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 @@ -246,6 +251,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'"; @@ -271,7 +282,7 @@ sub search { ), 'extra_sql' => $extra_sql, 'addl_from' => $addl_from, - 'order_by' => "ORDER BY ".($params->{'order_by'} || 'svcnum'), + 'order_by' => ($params->{'order_by'} || 'ORDER BY svcnum'), 'count_query' => $count_query, } ); } @@ -286,10 +297,14 @@ sub search_sql { my( $class, $string ) = @_; if ( $string =~ /^(\d{1,3}\.){3}\d{1,3}$/ ) { $class->search_sql_field('ip_addr', $string ); - }elsif ( $string =~ /^([a-fA-F0-9]{12})$/ ) { + } elsif ( $string =~ /^([A-F0-9]{12})$/i ) { $class->search_sql_field('mac_addr', uc($string)); - }elsif ( $string =~ /^(([a-fA-F0-9]{1,2}:){5}([a-fA-F0-9]{1,2}))$/ ) { - $class->search_sql_field('mac_addr', uc("$2$3$4$5$6$7") ); + } elsif ( $string =~ /^(([A-F0-9]{2}:){5}([A-F0-9]{2}))$/i ) { + $string =~ s/://g; + $class->search_sql_field('mac_addr', uc($string) ); + } elsif ( $string =~ /^(\d+)$/ ) { + my $table = $class->table; + "$table.svcnum = $1"; } else { '1 = 0'; #false } @@ -297,13 +312,18 @@ sub search_sql { =item label -Returns the IP address. +Returns the IP address, MAC address and description. =cut sub label { my $self = shift; - $self->ip_addr; + my $label = 'IP:'. ($self->ip_addr || 'Unknown'); + $label .= ', MAC:'. $self->mac_addr + if $self->mac_addr; + $label .= ' ('. $self->description. ')' + if $self->description; + return $label; } =item insert [ , OPTION => VALUE ... ] @@ -365,7 +385,7 @@ sub check { # remove delimiters my $mac_addr = uc($self->get('mac_addr')); - $mac_addr =~ s/[-: ]//g; + $mac_addr =~ s/[\W_]//g; $self->set('mac_addr', $mac_addr); my $error = @@ -405,7 +425,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, @@ -414,18 +440,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 ) { + if ( $router->manual_addr ) { + $self->blocknum(''); + } + else { my $addr_block = $self->addr_block; - unless ( $addr_block and $addr_block->manual_flag ) { + if ( $self->ip_addr eq '' + and not ( $addr_block and $addr_block->manual_flag ) ) { my $error = $self->assign_ip_addr; return $error if $error; } } - else { - $self->blocknum(''); - } + + my $error = $self->_check_ip_addr; + return $error if $error; } # if $self->routernum if ( $cust_pkg && ! $self->latitude && ! $self->longitude ) { @@ -439,15 +469,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 @@ -457,7 +484,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); @@ -468,6 +495,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) ) { @@ -475,15 +503,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 { @@ -493,6 +542,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} @@ -504,11 +562,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.")"; + } ''; } @@ -557,29 +622,20 @@ 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; @@ -592,6 +648,11 @@ sub _upgrade_data { routernum => '' })) { my $addr_block = $self->addr_block; + 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 ) { @@ -601,13 +662,13 @@ sub _upgrade_data { # (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)) { - die "Upgrading service ".$self->svcnum." would change its block/address.\n\nCheck your router and address block configuration.\n"; + warn "WARNING: Upgrading service ".$self->svcnum." would change its block/address; skipped.\n"; next; } $error ||= $self->replace; - die "error assigning routernum $routernum to service ".$self->svcnum. - ":\n$error\n" + warn "WARNING: error assigning routernum $routernum to service ".$self->svcnum. + ":\n$error; skipped\n" if $error; } else { @@ -615,6 +676,38 @@ 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; + } + ''; }