package FS::svc_broadband;
-use base qw(FS::svc_Radius_Mixin FS::svc_Tower_Mixin FS::svc_Common);
+use base qw(
+ FS::svc_Radius_Mixin
+ FS::svc_Tower_Mixin
+ FS::svc_IP_Mixin
+ FS::svc_Common
+ );
use strict;
use vars qw($conf);
'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',
sub table { 'svc_broadband'; }
-sub table_dupcheck_fields { ( 'mac_addr' ); }
+sub table_dupcheck_fields { ( 'ip_addr', 'mac_addr' ); }
=item search HASHREF
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'";
}
my $agentnum = $cust_pkg->cust_main->agentnum if $cust_pkg;
- if ($self->routernum) {
- return "Router ".$self->routernum." does not provide this service"
- unless qsearchs('part_svc_router', {
- svcpart => $svcpart,
- routernum => $self->routernum
- });
-
- my $router = $self->router;
- return "Router ".$self->routernum." does not serve this customer"
- if $router->agentnum and $router->agentnum != $agentnum;
-
- if ( $router->auto_addr ) {
- my $error = $self->assign_ip_addr;
- return $error if $error;
- }
- else {
- $self->blocknum('');
- }
- } # if $self->routernum
+ # assign IP address / router / block
+ $error = $self->svc_ip_check;
+ return $error if $error;
+ if ( !$self->ip_addr
+ and !$conf->exists('svc_broadband-allow_null_ip_addr') ) {
+ return 'IP address is required';
+ }
if ( $cust_pkg && ! $self->latitude && ! $self->longitude ) {
my $l = $cust_pkg->cust_location_or_main;
}
}
- $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
-if there is one.
-
-=cut
-
-sub assign_ip_addr {
- my $self = shift;
- my @blocks;
- my $ip_addr;
-
- if ( $self->blocknum 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);
- }
- elsif ( $self->routernum ) {
- @blocks = $self->router->auto_addr_block;
- }
- else {
- return '';
- }
-
- foreach my $block ( @blocks ) {
- if ( $self->ip_addr and $block->NetAddr->contains($self->NetAddr) ) {
- # don't change anything
- return '';
- }
- $ip_addr = $block->next_free_addr;
- last if $ip_addr;
- }
- if ( $ip_addr ) {
- $self->set(ip_addr => $ip_addr->addr);
- return '';
- }
- else {
- return 'No IP address available on this router';
- }
-}
-
-sub _check_ip_addr {
- my $self = shift;
-
- if (not($self->ip_addr) or $self->ip_addr eq '0.0.0.0') {
- return '' if $conf->exists('svc_broadband-allow_null_ip_addr');
- return 'IP address required';
- }
-# if (my $dup = qsearchs('svc_broadband', {
-# ip_addr => $self->ip_addr,
-# svcnum => {op=>'!=', value => $self->svcnum}
-# }) ) {
-# return 'IP address conflicts with svcnum '.$dup->svcnum;
-# }
- '';
-}
-
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', 'mac_addr');
+ if ( @dup ) {
+ return "MAC address in use (svcnum ".$dup[0]->svcnum.")";
+ }
'';
}
+=item mac_addr_formatted CASE DELIMITER
-=item NetAddr
-
-Returns a NetAddr::IP object containing the IP address of this service. The netmask
-is /32.
-
-=cut
-
-sub NetAddr {
- my $self = shift;
- new NetAddr::IP ($self->ip_addr);
-}
-
-=item addr_block
-
-Returns the FS::addr_block record (i.e. the address block) for this broadband service.
+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 addr_block {
+sub mac_addr_formatted {
my $self = shift;
- qsearchs('addr_block', { blocknum => $self->blocknum });
+ my ($case, $delim) = @_;
+ my $addr = $self->mac_addr;
+ $addr = lc($addr) if $case =~ /^l/i;
+ join( $delim || '', $addr =~ /../g );
}
-=item router
-
-Returns the FS::router record for this service.
-
-=cut
-
-sub router {
- my $self = shift;
- qsearchs('router', { routernum => $self->routernum });
-}
-
-=item allowed_routers
-
-Returns a list of allowed FS::router objects.
-
-=cut
-
-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 });
-}
-
-=back
-
-
#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 {
": 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.