1package Netdot::Model::Interface;
2
3use base 'Netdot::Model';
4use warnings;
5use strict;
6
7my $MAC  = Netdot->get_mac_regex();
8my $logger = Netdot->log->get_logger('Netdot::Model::Device');
9
10my %SPEED_MAP = ('1536000'     => 'T1',
11		 '1544000'     => 'T1',
12		 '3072000'     => 'Dual T1',
13		 '3088000'     => 'Dual T1',
14		 '44210000'    => 'T3',
15		 '44736000'    => 'T3',
16		 '45045000'    => 'DS3',
17		 '46359642'    => 'DS3',
18		 '149760000'   => 'ATM on OC-3',
19		 '155000000'   => 'OC-3',
20		 '155519000'   => 'OC-3',
21		 '155520000'   => 'OC-3',
22		 '599040000'   => 'ATM on OC-12',
23		 '622000000'   => 'OC-12',
24		 '622080000'   => 'OC-12',
25    );
26
27#Be sure to return 1
281;
29
30=head1 NAME
31
32Netdot::Model::Interface
33
34=head1 CLASS METHODS
35=cut
36
37################################################################
38
39=head2 insert - Insert Interface object
40
41    We override the insert method for extra functionality
42
43  Arguments:
44    Hash ref with Interface  fields
45  Returns:
46    New Interface object
47  Examples:
48
49=cut
50
51sub insert {
52    my ($class, $argv) = @_;
53    $class->isa_class_method('insert');
54
55    # Set some defaults
56    $argv->{speed}       ||= 0;
57    $argv->{doc_status}  ||= 'manual';
58
59    $argv->{snmp_managed} = $class->config->get('IF_SNMP')
60	unless defined $argv->{snmp_managed};
61
62    $argv->{overwrite_descr} = $class->config->get('IF_OVERWRITE_DESCR')
63	unless defined $argv->{overwrite_descr};
64
65    $argv->{monitored} = 0 unless defined $argv->{monitored};
66
67    $argv->{auto_dns} = $class->config->get('UPDATE_DEVICE_IP_NAMES')
68	unless defined $argv->{auto_dns};
69
70    my $unknown_status = (MonitorStatus->search(name=>"Unknown"))[0];
71    $argv->{monitorstatus} = ($unknown_status)? $unknown_status->id : undef;
72
73    return $class->SUPER::insert( $argv );
74}
75
76################################################################
77
78=head2 - find_duplex_mismatches - Finds pairs of interfaces with duplex and/or speed mismatch
79
80  Arguments:
81    None
82  Returns:
83    Array of arrayrefs containing pairs of interface id's
84  Examples:
85    my @list = Interface->find_duplex_mismatches();
86=cut
87
88sub find_duplex_mismatches {
89    my ($class) = @_;
90    $class->isa_class_method('find_duplex_mismatches');
91    my $dbh = $class->db_Main();
92    my $mismatches = $dbh->selectall_arrayref("SELECT  i.id, r.id
93                                               FROM    interface i, interface r
94                                               WHERE   i.id<=r.id
95                                                 AND   i.neighbor=r.id
96                                                 AND   i.oper_status='up'
97                                                 AND   r.oper_status='up'
98                                                 AND   i.oper_duplex!=''
99                                                 AND   r.oper_duplex!=''
100                                                 AND   i.oper_duplex!='unknown'
101                                                 AND   r.oper_duplex!='unknown'
102                                                 AND   i.oper_duplex!=r.oper_duplex");
103
104    if ( $mismatches ){
105	my @pairs = @$mismatches;
106	#
107	# Ignore devices that incorrectly report their settings
108	my @results;
109	if ( my $ignored_list = $class->config->get('IGNORE_DUPLEX') ){
110	    my %ignored;
111	    foreach my $id ( @$ignored_list){
112		$ignored{$id} = 1;
113	    }
114	    foreach my $pair ( @pairs ){
115		my $match = 0;
116		foreach my $ifaceid ( @$pair ){
117		    my $iface = Interface->retrieve($ifaceid)
118			|| $class->throw_fatal("Model::Interface::find_duplex_mismatches: Cannot retrieve Interface id $ifaceid");
119		    if ( $iface->device && $iface->device->product
120			 && $iface->device->product->sysobjectid
121			 && exists $ignored{$iface->device->product->sysobjectid} ){
122			$match = 1;
123			last;
124		    }
125		}
126		push @results, $pair unless $match;
127	    }
128	}else{
129	    return \@pairs;
130	}
131	return \@results;
132    }else{
133	return;
134    }
135}
136
137
138################################################################
139
140=head2 - find_vlan_mismatches
141
142    Use topology information to determine if VLAN memmbership
143    of connected interfaces does not correspond
144
145  Arguments:
146    None
147  Returns:
148    Hasref
149  Examples:
150    my $v = Interface->find_vlan_mismatches();
151=cut
152
153sub find_vlan_mismatches {
154    my ($class) = @_;
155    my $dbh = $class->db_Main;
156    my $rows = $dbh->selectall_arrayref("
157    SELECT i1.id, i2.id, v1.vid, v2.vid
158    FROM   interface i1, interface i2,
159	   interfacevlan iv1, interfacevlan iv2,
160	   vlan v1, vlan v2
161    WHERE  i1.neighbor=i2.id
162    AND    i1.id < i2.id
163    AND    iv1.interface=i1.id AND iv2.interface=i2.id
164    AND    iv1.vlan=v1.id AND iv2.vlan=v2.id");
165
166    my %x; my %y; my %lks;
167
168    foreach my $row ( @$rows ){
169	my ($i1, $i2, $v1, $v2) = @$row;
170	$lks{$i1} = $i2;
171	$lks{$i2} = $i1;
172	$x{$i1}{$v1} = 1;
173	$y{$i2}{$v2} = 1;
174    }
175
176    my %res; my %seen;
177    foreach my $i ( keys %x ){
178	next if $seen{$i};
179	my $n = $lks{$i};
180	$seen{$i} = 1; $seen{$n} = 1;
181	my @l1 = sort { $a <=> $b } keys %{$x{$i}};
182	my @l2 = sort { $a <=> $b } keys %{$y{$n}};
183	if ( scalar(@l1) == 1 && scalar(@l2) == 1 ){
184	    # Assume that one vlan on each side
185	    # means that they are both the native vlan
186	    next;
187	}
188	my $vlx = join ', ', @l1;
189	my $vly = join ', ', @l2;
190	if ( $vlx ne $vly ){
191	    my $i_name = $class->retrieve($i)->get_label;
192	    my $n_name = $class->retrieve($n)->get_label;
193	    $res{$i}{name}    = $i_name;
194	    $res{$i}{vlans}   = $vlx;
195	    $res{$i}{n_id}    = $n;
196	    $res{$i}{n_name}  = $n_name;
197	    $res{$i}{n_vlans} = $vly;
198	}
199    }
200    return \%res;
201}
202
203
204#################################################################
205
206=head2 - dev_name_number - Hash all interfaces by device, name and number
207
208  Arguments:
209    None
210  Returns:
211    Hash ref of hash refs
212  Examples:
213    my $map = Interface->dev_name_number();
214
215=cut
216
217sub dev_name_number {
218    my ($class) = @_;
219    $class->isa_class_method('dev_name_number');
220
221    # Build the SQL query
222    $logger->debug(sub{ "Interface::dev_name_number: Retrieving all interfaces" });
223
224    my $dbh = $class->db_Main;
225    my $sth = $dbh->prepare_cached("SELECT i.id, i.number, i.name, d.id
226                                      FROM device d, interface i
227                                     WHERE i.device=d.id");
228    $sth->execute();
229    my $aref = $sth->fetchall_arrayref;
230
231    # Build the hash
232    my %map;
233    foreach my $row ( @$aref ){
234	my ($iid, $inum, $iname, $did) = @$row;
235	$map{$did}{number}{$inum} = $iid if defined $inum;
236	$map{$did}{name}{$iname}  = $iid if defined $iname;
237    }
238    $logger->debug(sub{ "Interface::dev_name_number ...done" });
239
240    return \%map;
241
242}
243
244
245=head1 OBJECT METHODS
246=cut
247
248################################################################
249
250=head2 delete - Delete object
251
252    We override the delete method for extra functionality
253
254  Arguments:
255    None
256  Returns:
257    True if sucessful
258  Examples:
259    $interface->delete();
260
261=cut
262
263sub delete {
264    my $self = shift;
265    $self->isa_object_method('delete');
266
267    foreach my $neighbor ( $self->neighbors ){
268	$neighbor->SUPER::update({neighbor=>undef, neighbor_fixed=>0, neighbor_missed=>0});
269    }
270
271    return $self->SUPER::delete();
272}
273
274############################################################################
275
276=head2 add_neighbor
277
278  Arguments:
279    Hash with following key/value pairs:
280    id        - Neighbor's Interface id
281    score     - Optional score obtained from Topology discovery code (for logging)
282    fixed     - (bool) Whether relationship should be removed by automated processes
283  Returns:
284    True if neighbor was successfully added
285  Example:
286    $interface->add_neighbor($id);
287
288=cut
289
290sub add_neighbor{
291    my ($self, %argv) = @_;
292    $self->isa_object_method('add_neighbor');
293    my $nid   = $argv{id}    || $self->throw_fatal("Model::Interface::add_neighbor: Missing required argument: id");
294    my $score = $argv{score} || 'n/a';
295    my $fixed = $argv{fixed} || 0;
296
297    if ( $nid == $self->id ){
298	$self->throw_user(sprintf("%s: interface cannot be neighbor of itself", $self->get_label));
299    }
300
301    my $neighbor = Interface->retrieve($nid)
302	|| $self->throw_fatal("Model::Interface::add_neighbor: Cannot retrieve Interface id $nid");
303
304    if ( $self->neighbor && $neighbor->neighbor
305	 && $self->neighbor->id == $neighbor->id
306	 && $neighbor->neighbor->id == $self->id ){
307
308	return 1;
309    }
310
311    $logger->debug(sub{sprintf("Adding new neighbors: %s <=> %s, score: %s",
312			       $self->get_label, $neighbor->get_label, $score)});
313
314    if ( $self->neighbor && $self->neighbor_fixed ){
315	$self->throw_user(sprintf("%s has been manually linked to %s",
316				  $self->get_label, $self->neighbor->get_label));
317
318    }elsif ( $neighbor->neighbor && $neighbor->neighbor_fixed ) {
319	$self->throw_user(sprintf("%s has been manually linked to %s",
320				  $neighbor->get_label, $neighbor->neighbor->get_label));
321
322    }else{
323	# Make sure all neighbor relationships are cleared before going on
324	$self->remove_neighbor();
325	$neighbor->remove_neighbor();
326
327	$self->SUPER::update({neighbor        => $neighbor->id,
328			      neighbor_fixed  => $fixed,
329			      neighbor_missed => 0});
330
331	$neighbor->SUPER::update({neighbor        => $self->id,
332				  neighbor_fixed  => $fixed,
333				  neighbor_missed => 0});
334
335	$logger->info(sprintf("Added new neighbors: %s <=> %s, score: %s",
336			      $self->get_label, $neighbor->get_label, $score));
337	return 1;
338    }
339}
340
341
342############################################################################
343
344=head2 remove_neighbor
345
346  Arguments:
347    None
348  Returns:
349    See update method
350  Example:
351    $interface->remove_neighbor();
352
353=cut
354
355sub remove_neighbor{
356    my ($self) = @_;
357
358    my %args = (
359	neighbor        => undef,
360	neighbor_fixed  => 0,
361	neighbor_missed => 0
362	);
363
364    # Unset neighbor field in all interfaces that have
365    # me as their neighbor
366    map { $_->SUPER::update(\%args) } $self->neighbors;
367
368    # Unset my own neighbor field
369    return $self->SUPER::update(\%args);
370}
371
372############################################################################
373
374=head2 update - Update Interface
375
376  Arguments:
377    Hash ref with Interface fields
378  Returns:
379    See Class::DBI::update()
380  Example:
381    $interface->update( \%data );
382
383=cut
384
385sub update {
386    my ($self, $argv) = @_;
387    $self->isa_object_method('update');
388    my $class = ref($self);
389
390    if ( exists $argv->{neighbor} ){
391	if ( !$argv->{neighbor} ){
392	    $self->remove_neighbor();
393	}else{
394	    $self->add_neighbor(id    => $argv->{neighbor},
395				fixed => $argv->{neighbor_fixed});
396	}
397    }
398    delete $argv->{neighbor};
399    return $self->SUPER::update($argv);
400}
401
402############################################################################
403
404=head2 snmp_update - Insert/Update Interface using SNMP info
405
406  Arguments:
407    Hash with the following keys:
408    column        - Any of Interface table columns
409    snmp_info     - Hash ref with SNMP info about interface
410    add_subnets   - Whether to add subnets automatically
411    subs_inherit  - Whether subnets should inherit info from the Device
412    stp_instances - Hash ref with device STP info
413  Returns:
414    Interface object
415  Examples:
416    # Instance call
417    $if->snmp_update(snmp_info    => $info->{interface}->{$newif},
418		     add_subnets  => $add_subnets,
419		     subs_inherit => $subs_inherit,
420		     );
421    # Class call
422    my $new = Ipblock->snmp_update(%args, snmp_info=>$info, ...);
423
424=cut
425
426sub snmp_update {
427    my ($self, %args) = @_;
428    my $info  = $args{snmp_info};
429    my %iftmp = (doc_status => 'snmp');
430
431    # Table column values can be arguments or keys in the snmp_info hash
432    foreach my $field ( $self->meta_data->get_column_names ){
433	$iftmp{$field} = $args{$field}   if exists $args{$field};
434	$iftmp{$field} = $info->{$field} if exists $info->{$field};
435    }
436
437    ############################################
438    # Update PhysAddr
439    if ( $info->{physaddr} && (my $addr = PhysAddr->validate($info->{physaddr})) ){
440	my $physaddr = PhysAddr->search(address=>$addr)->first;
441	if ( $physaddr ){
442	    $physaddr->update({last_seen=>$self->timestamp, static=>1});
443	}else{
444	    eval {
445		$physaddr = PhysAddr->insert({address=>$addr, static=>1});
446	    };
447	    if ( my $e = $@ ){
448		$logger->debug(sub{"$e"});
449	    }
450	}
451	$iftmp{physaddr} = $physaddr->id if $physaddr;
452    }else{
453	$iftmp{physaddr} = undef;
454    }
455
456
457    ############################################
458    # Insert/Update
459    my $class;
460    if ( $class = ref($self) ){
461
462	# Check if description can be overwritten
463	delete $iftmp{description} if !($self->overwrite_descr) ;
464
465	my $r = $self->update( \%iftmp );
466	my $d = $self->device->get_label;
467	if ( $r && $self->number && $self->name ){
468	    $logger->debug(sub{ sprintf("%s: Interface %s (%s) updated",
469					$d, $self->number, $self->name) });
470	}
471    }else{
472	$class = $self;
473	$self = $class->insert(\%iftmp);
474	my $d = $self->device->get_label;
475	$logger->info(sprintf("%s: Interface %s (%s) updated",
476			      $d, $self->number, $self->name));
477
478    }
479    my $label = $self->get_label;
480
481    ##############################################
482    # Update VLANs
483    #
484    # Get our current vlan memberships
485    # InterfaceVlan objects
486    #
487    if ( exists $info->{vlans} ){
488	my %oldvlans;
489	map { $oldvlans{$_->id} = $_ } $self->vlans();
490
491	# InterfaceVlan STP fields and their methods
492	my %IVFIELDS = ( stp_des_bridge => 'i_stp_bridge',
493			 stp_des_port   => 'i_stp_port',
494			 stp_state      => 'i_stp_state',
495	    );
496
497	foreach my $newvlan ( keys %{ $info->{vlans} } ){
498	    my $vid   = $info->{vlans}->{$newvlan}->{vid} || $newvlan;
499	    next if $vid == 0;
500	    my $vname = $info->{vlans}->{$newvlan}->{vname};
501	    my $vo;
502	    my %vdata;
503	    $vdata{vid}   = $vid;
504	    $vdata{name}  = $vname if defined $vname;
505	    if ( $vo = Vlan->search(vid => $vid)->first ){
506		# update if name wasn't set (ignore default vlan 1)
507		if ( !defined $vo->name && defined $vdata{name} && $vo->vid ne "1" ){
508		    my $r = $vo->update(\%vdata);
509		    $logger->debug(sub{ sprintf("%s: VLAN %s name updated: %s",
510						$label, $vo->vid, $vo->name) })
511			if $r;
512		}
513	    }else{
514		# create
515		$vo = Vlan->insert(\%vdata);
516		$logger->info(sprintf("%s: Inserted VLAN %s", $label, $vo->vid));
517	    }
518	    # Now verify membership
519	    #
520	    my %ivtmp = ( interface => $self->id, vlan => $vo->id );
521	    my $iv;
522	    if  ( $iv = InterfaceVlan->search( \%ivtmp )->first ){
523		delete $oldvlans{$iv->id};
524	    }else {
525		# insert
526		$iv = InterfaceVlan->insert( \%ivtmp );
527		$logger->debug(sub{sprintf("%s: Assigned Interface %s (%s) to VLAN %s",
528					   $label, $self->number, $self->name, $vo->vid)});
529	    }
530
531	    # Insert STP information for this interface on this vlan
532	    my $stpinst = $info->{vlans}->{$newvlan}->{stp_instance};
533	    unless ( defined $stpinst ){
534		$logger->debug(sub{sprintf("%s: VLAN %s not mapped to any STP instance",
535					   $label, $newvlan)});
536		next;
537	    }
538
539	    my $instobj;
540	    # In theory, this happens after the STP instances have been updated on this device
541	    $instobj = STPInstance->search(device=>$self->device, number=>$stpinst)->first;
542	    unless ( $instobj ){
543		$logger->warn("$label: Cannot find STP instance $stpinst");
544		next;
545	    }
546	    my %uargs;
547	    foreach my $field ( keys %IVFIELDS ){
548		my $method = $IVFIELDS{$field};
549		if ( exists $args{stp_instances}->{$stpinst}->{$method} &&
550		     (my $v = $args{stp_instances}->{$stpinst}->{$method}->{$info->{number}}) ){
551		    $uargs{$field} = $v;
552		}
553	    }
554	    if ( %uargs ){
555		$iv->update({stp_instance=>$instobj, %uargs});
556		$logger->debug(sub{ sprintf("%s: Updated STP info on VLAN %s",
557					    $label, $vo->vid) });
558	    }
559	}
560	# Remove each vlan membership that no longer exists
561	#
562	foreach my $oldvlan ( keys %oldvlans ) {
563	    my $iv = $oldvlans{$oldvlan};
564	    $logger->debug(sub{sprintf("%s: membership with VLAN %s no longer exists.  Removing.",
565				   $label, $iv->vlan->vid)});
566	    $iv->delete();
567	}
568    }
569
570    ################################################################
571    # Update IPs
572    #
573    if ( exists( $info->{ips} ) ) {
574
575	# For Subnet->vlan assignments
576	my $vlan = 0;
577	my @ivs  = $self->vlans;
578	$vlan = $ivs[0]->vlan if ( scalar(@ivs) == 1 );
579
580	my $name = $self->name;
581
582	# For layer3 switches with virtual VLAN interfaces
583	if ( !$vlan && $self->device->ipforwarding ){
584	    my $vid;
585
586	    if ( $name && $name =~ /Vlan(\d+)/o ){
587		# This works mostly for Cisco Catalyst stuff
588		$vid = $1;
589	    }elsif ( $self->type eq '135' # See IF-MIB
590		     && $name && $name =~ /\.(\d+)$/o ){
591		# This works for Juniper stuff or anything
592		# with sub-interfaces. It assumes that
593		# the sub-interface number matches the VLAN id
594		$vid = $1;
595	    }
596	    $vlan = Vlan->search(vid=>$vid)->first;
597	}
598
599	foreach my $newip ( keys %{ $info->{ips} } ){
600	    if ( my $address = $info->{ips}->{$newip}->{address} ){
601		my %iargs   =  (address      => $address,
602				version      => $info->{ips}->{$newip}->{version},
603				subnet       => $info->{ips}->{$newip}->{subnet},
604				add_subnets  => $args{add_subnets},
605				subs_inherit => $args{subs_inherit},
606		    );
607		$iargs{vlan} = $vlan if $vlan;
608		if ( $self->ignore_ip ){
609		    $logger->debug(sub{sprintf("%s: Ignoring IP information", $label)});
610		}else{
611		    $self->update_ip(%iargs);
612		}
613	    }
614	}
615    }
616    return $self;
617}
618
619############################################################################
620
621=head2 update_ip - Update IP adddress for this interface
622
623  Arguments:
624    Hash with the following keys:
625    address      - Dotted quad ip address
626    version      - 4 or 6
627    subnet       - Subnet CIDR
628    add_subnets  - Flag.  Add subnet if necessary (only for routers)
629    subs_inherit - Flag.  Have subnet inherit some Device information
630    vlan         - Vlan ID (for Subnet to Vlan mapping)
631
632  Returns:
633    Updated Ipblock object
634  Example:
635
636=cut
637
638sub update_ip {
639    my ($self, %args) = @_;
640    $self->isa_object_method('update_ip');
641
642    my $address = $args{address};
643    my $version = $args{version};
644    $self->throw_fatal("Model::Interface::update_ip: Missing required arguments: address, version")
645	unless ( $address && $version );
646
647    my $label = $self->get_label;
648
649    # Do not bother with loopbacks
650    if ( Ipblock->is_loopback($address) ){
651	$logger->debug(sub{"$label: IP $address is a loopback. Skipping."});
652	return;
653    }
654
655    if ( $args{subnet} ){
656	$logger->debug(sub{sprintf("%s: Subnet configured in interface is %s",
657				   $label, $args{subnet})});
658    }
659
660    # We might have to add a subnet
661    if ( $args{add_subnets} && (my $subnet = $args{subnet}) ){
662	my ($subnetaddr, $subnetprefix);
663	if ( $subnet =~ /^(.+)\/(\d+)$/ ){
664	    ($subnetaddr, $subnetprefix) = ($1, $2);
665	}else{
666	    $self->throw_fatal("Model::Interface::update_ip: Invalid subnet: $subnet");
667	}
668
669	$logger->debug(sprintf("%s: Adding or updating subnet %s/%d",
670			       $label, $subnetaddr, $subnetprefix));
671
672	# Make sure we compare the same formatting
673	my $subnet_netaddr = Ipblock->netaddr(address=>$subnetaddr, prefix=>$subnetprefix);
674	my $address_netaddr = Ipblock->netaddr(address=>$address);
675
676	if ( $subnet_netaddr->addr ne $address_netaddr->addr ||
677	     ($version == 4 && $subnetprefix == 31) ){
678	    my %iargs;
679	    $iargs{status} = 'Subnet' ;
680
681	    # If we have a VLAN, make the relationship
682	    $iargs{vlan} = $args{vlan} if defined $args{vlan};
683
684	    if ( my $subnetobj = Ipblock->search(address => $subnetaddr,
685						 version => $version,
686						 prefix  => $subnetprefix)->first ){
687
688		$logger->debug(sub{ sprintf("%s: Block %s already exists",
689					    $label, $subnetobj->get_label)} );
690
691		# Skip validation for speed, since the block already exists
692		$iargs{validate} = 0;
693
694		# Add description from interface if not set
695		$iargs{description} = $self->description
696		    if ( !defined $subnetobj->description || $subnetobj->description eq "" );
697
698		$iargs{last_seen} = $self->timestamp;
699
700		$subnetobj->update(\%iargs); # Makes sure that the status is set to subnet
701
702	    }else{
703		$logger->debug(sub{ sprintf("Subnet %s/%s does not exist.  Inserting.",
704					    $subnetaddr, $subnetprefix) });
705
706		$iargs{address}     = $subnet_netaddr->addr;
707		$iargs{prefix}      = $subnet_netaddr->masklen;
708		$iargs{version}     = $version;
709		$iargs{description} = $self->description;
710		$iargs{first_seen}  = $self->timestamp;
711		$iargs{last_seen}   = $iargs{first_seen};
712
713		# Check if subnet should inherit device info
714		if ( $args{subs_inherit} ){
715		    $iargs{owner}   = $self->device->owner;
716		    $iargs{used_by} = $self->device->used_by;
717		}
718
719		# Ipblock validation might throw an exception
720		my $newblock;
721		eval {
722		    $newblock = Ipblock->insert(\%iargs);
723		};
724		if ( my $e = $@ ){
725		    $logger->error(sprintf("%s: Could not insert Subnet %s/%s: %s",
726					   $label, $subnet_netaddr->addr, $subnet_netaddr->masklen, $e));
727		}else{
728		    $logger->info(sprintf("%s: Created Subnet %s/%s",
729					  $label, $subnetaddr, $subnetprefix));
730		}
731	    }
732	}
733    }
734
735    # Now work on the address itself
736    my $prefix  = ($version == 4)  ? 32 : 128;
737    my $ipobj;
738    if ( $ipobj = Ipblock->search(address => $address,
739				  prefix  => $prefix,
740				  version => $version)->first ){
741
742	# update
743	$logger->debug(sub{ sprintf("%s: IP %s/%s exists. Updating",
744				    $label, $address, $prefix) });
745
746	# Notice that this is basically to confirm that the IP belongs
747	# to this interface.
748	# Therefore, it's very unlikely that the object won't pass
749	# validation, so we skip it to speed things up.
750	my %args = (interface => $self, validate => 0, last_seen=>$self->timestamp);
751	if ( !($ipobj->status) ||
752	     ($ipobj->status->name ne 'Static' && $ipobj->status->name ne 'Dynamic') ){
753	    $args{status} = 'Static';
754	}
755	$ipobj->update(\%args);
756    }else {
757	# Create a new IP
758	# This could also go wrong, but we don't want to bail out
759	eval {
760	    $ipobj = Ipblock->insert({address => $address, prefix    => $prefix,
761				      status  => "Static", interface => $self,
762				      version => $version,
763				     });
764	};
765	if ( my $e = $@ ){
766	    $logger->warn(sprintf("%s: Could not insert IP %s: %s",
767				   $label, $address, $e));
768	    return;
769	}else{
770	    $logger->info(sprintf("%s: Inserted new IP %s", $label, $ipobj->address));
771	    my $version = $ipobj->version;
772	}
773    }
774    return $ipobj;
775}
776
777############################################################################
778
779=head2 speed_pretty - Convert ifSpeed to something more readable
780
781  Arguments:
782    None
783  Returns:
784    Human readable speed string or n/a
785
786=cut
787
788sub speed_pretty {
789    my ($self) = @_;
790    $self->isa_object_method('speed_pretty');
791    my $speed = $self->speed;
792
793    if ( exists $SPEED_MAP{$speed} ){
794	return $SPEED_MAP{$speed};
795    }else{
796	# ifHighSpeed (already translated to bps)
797	my $fmt = "%d bps";
798	if ( $speed > 9999999999999 ){
799	    $fmt = "%d Tbps";
800	    $speed /= 1000000000000;
801	} elsif ( $speed > 999999999999 ){
802	    $fmt = "%.1f Tbps";
803	    $speed /= 1000000000000.0;
804	} elsif ( $speed > 9999999999 ){
805	    $fmt = "%d Gbps";
806	    $speed /= 1000000000;
807	} elsif ( $speed > 999999999 ){
808	    $fmt = "%.1f Gbps";
809	    $speed /= 1000000000.0;
810	} elsif ( $speed > 9999999 ){
811	    $fmt = "%d Mbps";
812	    $speed /= 1000000;
813	} elsif ( $speed > 999999 ){
814	    $fmt = "%d Mbps";
815	    $speed /= 1000000.0;
816	} elsif ( $speed > 99999 ){
817	    $fmt = "%d Kbps";
818	    $speed /= 100000;
819	} elsif ( $speed > 9999 ){
820	    $fmt = "%d Kbps";
821	    $speed /= 100000.0;
822	}
823	return sprintf($fmt, $speed);
824    }
825}
826
827############################################################################
828
829=head2 get_label
830
831    Override get_label from base class
832
833=cut
834
835sub get_label{
836    my ($self) = @_;
837    $self->isa_object_method('get_label');
838    return unless ( $self->id && $self->device );
839    my $name = $self->name || $self->number;
840    my $label = sprintf("%s [%s]", $self->device->get_label, $name);
841    return $label;
842}
843
844=head1 AUTHOR
845
846Carlos Vicente, C<< <cvicente at ns.uoregon.edu> >>
847
848=head1 COPYRIGHT & LICENSE
849
850Copyright 2012 University of Oregon, all rights reserved.
851
852This program is free software; you can redistribute it and/or modify
853it under the terms of the GNU General Public License as published by
854the Free Software Foundation; either version 2 of the License, or
855(at your option) any later version.
856
857This program is distributed in the hope that it will be useful, but
858WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
859or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
860License for more details.
861
862You should have received a copy of the GNU General Public License
863along with this program; if not, write to the Free Software Foundation,
864Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
865
866=cut
867
868