1 package FS::svc_IP_Mixin;
2 use base 'FS::IP_Mixin';
6 use FS::Record qw(qsearchs qsearch);
11 Returns the address block assigned to this service.
15 Returns the router assigned to this service, if there is one.
19 #addr_block and router methods provided by FS::IP_Mixin
23 Returns the address as a L<NetAddr::IP> object. Use C<$svc->NetAddr->addr>
24 to put it into canonical string form.
30 NetAddr::IP->new($self->ip_addr);
35 Wrapper for set/get on the IP address field.
41 my $ip_field = $self->table_info->{'ip_field'}
44 $self->set($ip_field, @_);
46 $self->get($ip_field);
52 Returns a list of L<FS::router> objects allowed on this service.
58 my $svcpart = $self->svcnum ? $self->cust_svc->svcpart : $self->svcpart;
59 my @r = map { $_->router }
60 qsearch('part_svc_router', { svcpart => $svcpart });
62 if ( $self->cust_main ) {
63 my $agentnum = $self->cust_main->agentnum;
64 return grep { !$_->agentnum or $_->agentnum == $agentnum } @r;
72 Wrapper for C<ip_check> which also checks the validity of the router.
78 my $error = $self->ip_check;
79 return $error if $error;
80 if ( my $router = $self->router ) {
81 if ( grep { $_->routernum eq $router->routernum } $self->allowed_routers ) {
84 return 'Router '.$router->routername.' not available for this service';
91 my ($class, $block, $exclude) = @_;
92 my $ip_field = $class->table_info->{'ip_field'}
94 # if the service doesn't have an ip_field, then it has no IP addresses
97 my %hash = ( $ip_field => { op => '!=', value => '' } );
98 #$hash{'blocknum'} = $block->blocknum if $block;
99 $hash{'svcnum'} = { op => '!=', value => $exclude->svcnum } if ref $exclude;
100 map { my $na = $_->NetAddr; $na ? $na->addr : () }
102 table => $class->table,
104 extra_sql => " AND $ip_field != '0e0'",
109 my ($class, $addr, $exclude) = @_;
110 my $ip_field = $class->table_info->{'ip_field'}
113 my $svc = qsearchs($class->table, { $ip_field => $addr })
116 return '' if ( ref $exclude and $exclude->svcnum == $svc->svcnum );
118 my $cust_svc = $svc->cust_svc;
120 my @label = $cust_svc->label;
121 # "svc_foo 1234 (Service Desc)"
122 # this should be enough to identify it without leaking customer
123 # names across agents
124 "$label[2] $label[3] ($label[0])";
126 join(' ', $class->table, $svc->svcnum, '(unlinked service)');
130 =item attached_router
132 Returns the L<FS::router> attached via this service (as opposed to the one
133 this service is connected through), that is, a router whose "svcnum" field
134 equals this service's primary key.
136 If the 'router_routernum' pseudo-field is set, returns that router instead.
140 sub attached_router {
142 if ( length($self->get('router_routernum') )) {
143 return FS::router->by_key($self->router_routernum);
145 qsearchs('router', { 'svcnum' => $self->svcnum });
151 Returns the address block (L<FS::addr_block>) assigned to the attached_router,
154 If the 'router_blocknum' pseudo-field is set, returns that block instead.
160 if ( length($self->get('router_blocknum')) ) {
161 return FS::addr_block->by_key($self->router_blocknum);
163 my $router = $self->attached_router or return '';
164 my ($block) = $router->addr_block;
179 Returns RADIUS reply items that are relevant across all exports and
180 necessary for the IP address configuration of the service. Currently, that
181 means "Framed-Route" if there's an attached router.
188 if ( my $block = $self->attached_block ) {
189 # block routed over dynamic IP: "192.168.100.0/29 0.0.0.0 1"
191 # block routed over fixed IP: "192.168.100.0/29 192.168.100.1 1"
192 # (the "1" at the end is the route metric)
193 $reply{'Framed-Route'} = $block->cidr . ' ' .
194 ($self->ip_addr || '0.0.0.0') . ' 1';
196 $reply{'Motorola-Canopy-Gateway'} = $block->ip_gateway
197 if FS::Conf->new->exists('radius-canopy');
204 my ($new, $old) = @_;
205 # this modifies $old, not $new, which is a slight abuse of replace_check,
206 # but there's no way to ensure that replace_old gets called...
208 # ensure that router_routernum and router_blocknum are set to their
209 # current values, so that exports remember the service's attached router
210 # and block even after they've been replaced
211 my $router = $old->attached_router;
212 my $block = $old->attached_block;
213 $old->set('router_routernum', $router ? $router->routernum : 0);
214 $old->set('router_blocknum', $block ? $block->blocknum : 0);
215 my $err_or_ref = $new->NEXT::replace_check($old) || '';
216 # because NEXT::replace_check($old) ends up trying to AUTOLOAD replace_check
217 # which is dumb, but easily worked around
218 ref($err_or_ref) ? '' : $err_or_ref;