1 package FS::svc_IP_Mixin;
4 use base 'FS::IP_Mixin';
5 use FS::Record qw(qsearchs qsearch);
10 Returns the address block assigned to this service.
14 Returns the router assigned to this service, if there is one.
18 #addr_block and router methods provided by FS::IP_Mixin
22 Returns the address as a L<NetAddr::IP> object. Use C<$svc->NetAddr->addr>
23 to put it into canonical string form.
29 NetAddr::IP->new($self->ip_addr);
34 Wrapper for set/get on the IP address field.
40 my $ip_field = $self->table_info->{'ip_field'}
43 $self->set($ip_field, @_);
45 $self->get($ip_field);
51 Returns a list of L<FS::router> objects allowed on this service.
57 my $svcpart = $self->svcnum ? $self->cust_svc->svcpart : $self->svcpart;
58 my @r = map { $_->router }
59 qsearch('part_svc_router', { svcpart => $svcpart });
61 if ( $self->cust_main ) {
62 my $agentnum = $self->cust_main->agentnum;
63 return grep { !$_->agentnum or $_->agentnum == $agentnum } @r;
71 Wrapper for C<ip_check> which also checks the validity of the router.
77 my $error = $self->ip_check;
78 return $error if $error;
79 if ( my $router = $self->router ) {
80 if ( grep { $_->routernum eq $router->routernum } $self->allowed_routers ) {
83 return 'Router '.$router->routername.' not available for this service';
90 my ($class, $block, $exclude) = @_;
91 my $ip_field = $class->table_info->{'ip_field'}
93 # if the service doesn't have an ip_field, then it has no IP addresses
96 my %hash = ( $ip_field => { op => '!=', value => '' } );
97 #$hash{'blocknum'} = $block->blocknum if $block;
98 $hash{'svcnum'} = { op => '!=', value => $exclude->svcnum } if ref $exclude;
99 map { $_->NetAddr->addr } qsearch($class->table, \%hash);
103 my ($class, $addr, $exclude) = @_;
104 my $ip_field = $class->table_info->{'ip_field'}
107 my $svc = qsearchs($class->table, { $ip_field => $addr })
110 return '' if ( ref $exclude and $exclude->svcnum == $svc->svcnum );
112 my $cust_svc = $svc->cust_svc;
114 my @label = $cust_svc->label;
115 # "svc_foo 1234 (Service Desc)"
116 # this should be enough to identify it without leaking customer
117 # names across agents
118 "$label[2] $label[3] ($label[0])";
120 join(' ', $class->table, $svc->svcnum, '(unlinked service)');
124 =item attached_router
126 Returns the L<FS::router> attached via this service (as opposed to the one
127 this service is connected through), that is, a router whose "svcnum" field
128 equals this service's primary key.
130 If the 'router_routernum' pseudo-field is set, returns that router instead.
134 sub attached_router {
136 if ( length($self->get('router_routernum') )) {
137 return FS::router->by_key($self->router_routernum);
139 qsearchs('router', { 'svcnum' => $self->svcnum });
145 Returns the address block (L<FS::addr_block>) assigned to the attached_router,
148 If the 'router_blocknum' pseudo-field is set, returns that block instead.
154 if ( length($self->get('router_blocknum')) ) {
155 return FS::addr_block->by_key($self->router_blocknum);
157 my $router = $self->attached_router or return '';
158 my ($block) = $router->addr_block;
173 Returns RADIUS reply items that are relevant across all exports and
174 necessary for the IP address configuration of the service. Currently, that
175 means "Framed-Route" if there's an attached router.
182 my ($block) = $self->attached_block;
184 # block routed over dynamic IP: "192.168.100.0/29 0.0.0.0 1"
186 # block routed over fixed IP: "192.168.100.0/29 192.168.100.1 1"
187 # (the "1" at the end is the route metric)
188 $reply{'Framed-Route'} =
190 ($self->ip_addr || '0.0.0.0') . ' 1';
196 my ($new, $old) = @_;
197 # this modifies $old, not $new, which is a slight abuse of replace_check,
198 # but there's no way to ensure that replace_old gets called...
200 # ensure that router_routernum and router_blocknum are set to their
201 # current values, so that exports remember the service's attached router
202 # and block even after they've been replaced
203 my $router = $old->attached_router;
204 my $block = $old->attached_block;
205 $old->set('router_routernum', $router ? $router->routernum : 0);
206 $old->set('router_blocknum', $block ? $block->blocknum : 0);
207 my $err_or_ref = $new->NEXT::replace_check($old) || '';
208 # because NEXT::replace_check($old) ends up trying to AUTOLOAD replace_check
209 # which is dumb, but easily worked around
210 ref($err_or_ref) ? '' : $err_or_ref;