1 package FS::svc_broadband;
4 use vars qw(@ISA $conf);
5 use FS::Record qw( qsearchs qsearch dbh );
11 @ISA = qw( FS::svc_Common );
13 $FS::UID::callback{'FS::svc_broadband'} = sub {
19 FS::svc_broadband - Object methods for svc_broadband records
23 use FS::svc_broadband;
25 $record = new FS::svc_broadband \%hash;
26 $record = new FS::svc_broadband { 'column' => 'value' };
28 $error = $record->insert;
30 $error = $new_record->replace($old_record);
32 $error = $record->delete;
34 $error = $record->check;
36 $error = $record->suspend;
38 $error = $record->unsuspend;
40 $error = $record->cancel;
44 An FS::svc_broadband object represents a 'broadband' Internet connection, such
45 as a DSL, cable modem, or fixed wireless link. These services are assumed to
46 have the following properties:
48 FS::svc_broadband inherits from FS::svc_Common. The following fields are
53 =item svcnum - primary key
55 =item blocknum - see FS::addr_block
58 speed_up - maximum upload speed, in bits per second. If set to zero, upload
59 speed will be unlimited. Exports that do traffic shaping should handle this
60 correctly, and not blindly set the upload speed to zero and kill the customer's
64 speed_down - maximum download speed, as above
66 =item ip_addr - the customer's IP address. If the customer needs more than one
67 IP address, set this to the address of the customer's router. As a result, the
68 customer's router will have the same address for both its internal and external
69 interfaces thus saving address space. This has been found to work on most NAT
80 Creates a new svc_broadband. To add the record to the database, see
83 Note that this stores the hash reference, not a distinct copy of the hash it
84 points to. You can ask the object for a copy with the I<hash> method.
90 'name' => 'Broadband',
91 'name_plural' => 'Broadband services',
92 'longname_plural' => 'Fixed (username-less) broadband services',
93 'display_weight' => 50,
94 'cancel_weight' => 70,
96 'description' => 'Descriptive label for this particular device.',
97 'speed_down' => 'Maximum download speed for this service in Kbps. 0 denotes unlimited.',
98 'speed_up' => 'Maximum upload speed for this service in Kbps. 0 denotes unlimited.',
99 'ip_addr' => 'IP address. Leave blank for automatic assignment.',
100 'blocknum' => { 'label' => 'Address block',
102 'select_table' => 'addr_block',
103 'select_key' => 'blocknum',
104 'select_label' => 'cidr',
105 'disable_inventory' => 1,
111 sub table { 'svc_broadband'; }
113 =item search_sql STRING
115 Class method which returns an SQL fragment to search for the given string.
120 my( $class, $string ) = @_;
121 if ( $string =~ /^(\d{1,3}\.){3}\d{1,3}$/ ) {
122 $class->search_sql_field('ip_addr', $string );
123 }elsif ( $string =~ /^([a-fA-F0-9]{12})$/ ) {
124 $class->search_sql_field('mac_addr', uc($string));
125 }elsif ( $string =~ /^(([a-fA-F0-9]{1,2}:){5}([a-fA-F0-9]{1,2}))$/ ) {
126 $class->search_sql_field('mac_addr', uc("$2$3$4$5$6$7") );
134 Returns the IP address.
143 =item insert [ , OPTION => VALUE ... ]
145 Adds this record to the database. If there is an error, returns the error,
146 otherwise returns false.
148 The additional fields pkgnum and svcpart (see FS::cust_svc) should be
149 defined. An FS::cust_svc record will be created and inserted.
151 Currently available options are: I<depend_jobnum>
153 If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
154 jobnums), all provisioning jobs will have a dependancy on the supplied
155 jobnum(s) (they will not run until the specific job(s) complete(s)).
159 # Standard FS::svc_Common::insert
163 Delete this record from the database.
167 # Standard FS::svc_Common::delete
169 =item replace OLD_RECORD
171 Replaces the OLD_RECORD with this one in the database. If there is an error,
172 returns the error, otherwise returns false.
176 # Standard FS::svc_Common::replace
180 Called by the suspend method of FS::cust_pkg (see FS::cust_pkg).
184 Called by the unsuspend method of FS::cust_pkg (see FS::cust_pkg).
188 Called by the cancel method of FS::cust_pkg (see FS::cust_pkg).
192 Checks all fields to make sure this is a valid broadband service. If there is
193 an error, returns the error, otherwise returns false. Called by the insert
200 my $x = $self->setfixed;
202 return $x unless ref($x);
205 $self->ut_numbern('svcnum')
206 || $self->ut_numbern('blocknum')
207 || $self->ut_textn('description')
208 || $self->ut_number('speed_up')
209 || $self->ut_number('speed_down')
210 || $self->ut_ipn('ip_addr')
211 || $self->ut_hexn('mac_addr')
212 || $self->ut_hexn('auth_key')
213 || $self->ut_coordn('latitude', -90, 90)
214 || $self->ut_coordn('longitude', -180, 180)
215 || $self->ut_sfloatn('altitude')
216 || $self->ut_textn('vlan_profile')
218 return $error if $error;
220 #redundant, but prevents further problems until column constraint in place
221 return "MAC already in use"
222 if scalar( qsearch( 'svc_broadband', { 'mac_addr', $self->mac_addr } ) );
224 if($self->speed_up < 0) { return 'speed_up must be positive'; }
225 if($self->speed_down < 0) { return 'speed_down must be positive'; }
227 my $cust_svc = $self->svcnum
228 ? qsearchs('cust_svc', { 'svcnum' => $self->svcnum } )
232 $cust_pkg = $cust_svc->cust_pkg;
234 $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $self->pkgnum } );
235 return "Invalid pkgnum" unless $cust_pkg;
238 if ($self->blocknum) {
239 $error = $self->ut_foreign_key('blocknum', 'addr_block', 'blocknum');
240 return $error if $error;
243 if ($cust_pkg && $self->blocknum) {
244 my $addr_agentnum = $self->addr_block->agentnum;
245 if ($addr_agentnum && $addr_agentnum != $cust_pkg->cust_main->agentnum) {
246 return "Address block does not service this customer";
250 if (not($self->ip_addr) or $self->ip_addr eq '0.0.0.0') {
251 return "Must supply either address or block"
252 unless $self->blocknum;
253 my $next_addr = $self->addr_block->next_free_addr;
255 $self->ip_addr($next_addr->addr);
257 return "No free addresses in addr_block (blocknum: ".$self->blocknum.")";
261 if (not($self->blocknum)) {
262 return "Must supply either address or block"
263 unless ($self->ip_addr and $self->ip_addr ne '0.0.0.0');
264 my @block = grep { $_->NetAddr->contains($self->NetAddr) }
265 map { $_->addr_block }
266 $self->allowed_routers;
267 if (scalar(@block)) {
268 $self->blocknum($block[0]->blocknum);
270 return "Address not with available block.";
274 # This should catch errors in the ip_addr. If it doesn't,
275 # they'll almost certainly not map into the block anyway.
276 my $self_addr = $self->NetAddr; #netmask is /32
277 return ('Cannot parse address: ' . $self->ip_addr) unless $self_addr;
279 my $block_addr = $self->addr_block->NetAddr;
280 unless ($block_addr->contains($self_addr)) {
281 return 'blocknum '.$self->blocknum.' does not contain address '.$self->ip_addr;
284 my $router = $self->addr_block->router
285 or return 'Cannot assign address from unallocated block:'.$self->addr_block->blocknum;
286 if(grep { $_->routernum == $router->routernum} $self->allowed_routers) {
289 return 'Router '.$router->routernum.' cannot provide svcpart '.$self->svcpart;
297 Returns a NetAddr::IP object containing the IP address of this service. The netmask
304 new NetAddr::IP ($self->ip_addr);
309 Returns the FS::addr_block record (i.e. the address block) for this broadband service.
315 qsearchs('addr_block', { blocknum => $self->blocknum });
320 =item allowed_routers
322 Returns a list of allowed FS::router objects.
326 sub allowed_routers {
328 map { $_->router } qsearch('part_svc_router', { svcpart => $self->svcpart });
333 The business with sb_field has been 'fixed', in a manner of speaking.
335 allowed_routers isn't agent virtualized because part_svc isn't agent
340 FS::svc_Common, FS::Record, FS::addr_block,
341 FS::part_svc, schema.html from the base documentation.