1 package FS::svc_broadband;
4 use vars qw(@ISA $conf);
5 use FS::Record qw( qsearchs qsearch dbh );
9 use FS::part_svc_router;
12 @ISA = qw( FS::svc_Common );
14 $FS::UID::callback{'FS::svc_broadband'} = sub {
20 FS::svc_broadband - Object methods for svc_broadband records
24 use FS::svc_broadband;
26 $record = new FS::svc_broadband \%hash;
27 $record = new FS::svc_broadband { 'column' => 'value' };
29 $error = $record->insert;
31 $error = $new_record->replace($old_record);
33 $error = $record->delete;
35 $error = $record->check;
37 $error = $record->suspend;
39 $error = $record->unsuspend;
41 $error = $record->cancel;
45 An FS::svc_broadband object represents a 'broadband' Internet connection, such
46 as a DSL, cable modem, or fixed wireless link. These services are assumed to
47 have the following properties:
49 FS::svc_broadband inherits from FS::svc_Common. The following fields are
54 =item svcnum - primary key
56 =item blocknum - see FS::addr_block
59 speed_up - maximum upload speed, in bits per second. If set to zero, upload
60 speed will be unlimited. Exports that do traffic shaping should handle this
61 correctly, and not blindly set the upload speed to zero and kill the customer's
65 speed_down - maximum download speed, as above
67 =item ip_addr - the customer's IP address. If the customer needs more than one
68 IP address, set this to the address of the customer's router. As a result, the
69 customer's router will have the same address for both its internal and external
70 interfaces thus saving address space. This has been found to work on most NAT
81 Creates a new svc_broadband. To add the record to the database, see
84 Note that this stores the hash reference, not a distinct copy of the hash it
85 points to. You can ask the object for a copy with the I<hash> method.
91 'name' => 'Broadband',
92 'name_plural' => 'Broadband services',
93 'longname_plural' => 'Fixed (username-less) broadband services',
94 'display_weight' => 50,
95 'cancel_weight' => 70,
97 'description' => 'Descriptive label for this particular device.',
98 'speed_down' => 'Maximum download speed for this service in Kbps. 0 denotes unlimited.',
99 'speed_up' => 'Maximum upload speed for this service in Kbps. 0 denotes unlimited.',
100 'ip_addr' => 'IP address. Leave blank for automatic assignment.',
101 'blocknum' => { 'label' => 'Address block',
103 'select_table' => 'addr_block',
104 'select_key' => 'blocknum',
105 'select_label' => 'cidr',
106 'disable_inventory' => 1,
112 sub table { 'svc_broadband'; }
114 sub table_dupcheck_fields { ( 'mac_addr' ); }
116 =item search_sql STRING
118 Class method which returns an SQL fragment to search for the given string.
123 my( $class, $string ) = @_;
124 if ( $string =~ /^(\d{1,3}\.){3}\d{1,3}$/ ) {
125 $class->search_sql_field('ip_addr', $string );
126 }elsif ( $string =~ /^([a-fA-F0-9]{12})$/ ) {
127 $class->search_sql_field('mac_addr', uc($string));
128 }elsif ( $string =~ /^(([a-fA-F0-9]{1,2}:){5}([a-fA-F0-9]{1,2}))$/ ) {
129 $class->search_sql_field('mac_addr', uc("$2$3$4$5$6$7") );
137 Returns the IP address.
146 =item insert [ , OPTION => VALUE ... ]
148 Adds this record to the database. If there is an error, returns the error,
149 otherwise returns false.
151 The additional fields pkgnum and svcpart (see FS::cust_svc) should be
152 defined. An FS::cust_svc record will be created and inserted.
154 Currently available options are: I<depend_jobnum>
156 If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
157 jobnums), all provisioning jobs will have a dependancy on the supplied
158 jobnum(s) (they will not run until the specific job(s) complete(s)).
162 # Standard FS::svc_Common::insert
166 Delete this record from the database.
170 # Standard FS::svc_Common::delete
172 =item replace OLD_RECORD
174 Replaces the OLD_RECORD with this one in the database. If there is an error,
175 returns the error, otherwise returns false.
179 # Standard FS::svc_Common::replace
183 Called by the suspend method of FS::cust_pkg (see FS::cust_pkg).
187 Called by the unsuspend method of FS::cust_pkg (see FS::cust_pkg).
191 Called by the cancel method of FS::cust_pkg (see FS::cust_pkg).
195 Checks all fields to make sure this is a valid broadband service. If there is
196 an error, returns the error, otherwise returns false. Called by the insert
203 my $x = $self->setfixed;
205 return $x unless ref($x);
208 $self->ut_numbern('svcnum')
209 || $self->ut_numbern('blocknum')
210 || $self->ut_textn('description')
211 || $self->ut_number('speed_up')
212 || $self->ut_number('speed_down')
213 || $self->ut_ipn('ip_addr')
214 || $self->ut_hexn('mac_addr')
215 || $self->ut_hexn('auth_key')
216 || $self->ut_coordn('latitude', -90, 90)
217 || $self->ut_coordn('longitude', -180, 180)
218 || $self->ut_sfloatn('altitude')
219 || $self->ut_textn('vlan_profile')
221 return $error if $error;
223 if($self->speed_up < 0) { return 'speed_up must be positive'; }
224 if($self->speed_down < 0) { return 'speed_down must be positive'; }
226 my $cust_svc = $self->svcnum
227 ? qsearchs('cust_svc', { 'svcnum' => $self->svcnum } )
231 $cust_pkg = $cust_svc->cust_pkg;
233 $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $self->pkgnum } );
234 return "Invalid pkgnum" unless $cust_pkg;
237 if ($self->blocknum) {
238 $error = $self->ut_foreign_key('blocknum', 'addr_block', 'blocknum');
239 return $error if $error;
242 if ($cust_pkg && $self->blocknum) {
243 my $addr_agentnum = $self->addr_block->agentnum;
244 if ($addr_agentnum && $addr_agentnum != $cust_pkg->cust_main->agentnum) {
245 return "Address block does not service this customer";
249 $error = $self->_check_ip_addr;
250 return $error if $error;
258 if (not($self->ip_addr) or $self->ip_addr eq '0.0.0.0') {
260 return '' if $conf->exists('svc_broadband-allow_null_ip_addr'); #&& !$self->blocknum
262 return "Must supply either address or block"
263 unless $self->blocknum;
264 my $next_addr = $self->addr_block->next_free_addr;
266 $self->ip_addr($next_addr->addr);
268 return "No free addresses in addr_block (blocknum: ".$self->blocknum.")";
273 if (not($self->blocknum)) {
274 return "Must supply either address or block"
275 unless ($self->ip_addr and $self->ip_addr ne '0.0.0.0');
276 my @block = grep { $_->NetAddr->contains($self->NetAddr) }
277 map { $_->addr_block }
278 $self->allowed_routers;
279 if (scalar(@block)) {
280 $self->blocknum($block[0]->blocknum);
282 return "Address not with available block.";
286 # This should catch errors in the ip_addr. If it doesn't,
287 # they'll almost certainly not map into the block anyway.
288 my $self_addr = $self->NetAddr; #netmask is /32
289 return ('Cannot parse address: ' . $self->ip_addr) unless $self_addr;
291 my $block_addr = $self->addr_block->NetAddr;
292 unless ($block_addr->contains($self_addr)) {
293 return 'blocknum '.$self->blocknum.' does not contain address '.$self->ip_addr;
296 my $router = $self->addr_block->router
297 or return 'Cannot assign address from unallocated block:'.$self->addr_block->blocknum;
298 if(grep { $_->routernum == $router->routernum} $self->allowed_routers) {
301 return 'Router '.$router->routernum.' cannot provide svcpart '.$self->svcpart;
307 sub _check_duplicate {
310 return "MAC already in use"
311 if ( $self->mac_addr &&
312 scalar( qsearch( 'svc_broadband', { 'mac_addr', $self->mac_addr } ) )
321 Returns a NetAddr::IP object containing the IP address of this service. The netmask
328 new NetAddr::IP ($self->ip_addr);
333 Returns the FS::addr_block record (i.e. the address block) for this broadband service.
339 qsearchs('addr_block', { blocknum => $self->blocknum });
344 =item allowed_routers
346 Returns a list of allowed FS::router objects.
350 sub allowed_routers {
352 map { $_->router } qsearch('part_svc_router', { svcpart => $self->svcpart });
357 The business with sb_field has been 'fixed', in a manner of speaking.
359 allowed_routers isn't agent virtualized because part_svc isn't agent
364 FS::svc_Common, FS::Record, FS::addr_block,
365 FS::part_svc, schema.html from the base documentation.