File Coverage

lib/POE/Filter/DHCPd/Lease.pm
Criterion Covered Total %
statement 60 62 96.7
branch 19 26 73.0
condition 3 3 100.0
subroutine 14 15 93.3
pod 5 5 100.0
total 101 111 90.9


line stmt bran cond sub pod time code
1             package POE::Filter::DHCPd::Lease;
2              
3             =head1 NAME
4              
5             POE::Filter::DHCPd::Lease - parses leases from isc dhcpd leases file
6              
7             =head1 VERSION
8              
9             0.0703
10              
11             =cut
12              
13             our $VERSION = '0.0703';
14              
15 2     2   3692 use strict;
  2         4  
  2         72  
16 2     2   10 use warnings;
  2         4  
  2         66  
17 2     2   21 use base qw/POE::Filter/;
  2         4  
  2         2259  
18 2     2   4128 use Time::Local;
  2         4567  
  2         223  
19 2     2   20 use constant BUFFER => 0;
  2         4  
  2         171  
20 2     2   11 use constant LEASE => 1;
  2         5  
  2         90  
21 2     2   34 use v5.10;
  2         7  
  2         1244  
22              
23             our $DATE = qr# (\d{4})/(\d\d)/(\d\d) \s (\d\d):(\d\d):(\d\d) #mx;
24             our $START = qr#^ lease \s ([\d\.]+) \s \{ #mx;
25             our $END = qr# } [\n\r]+ #mx;
26             our $PARSER = qr / (?: (?starts) \s\d+\s (?.+?)
27             | (?ends) \s\d+\s (?.+?)
28             | ^\s*(?binding) \s state \s (?\S+)
29             | ^\s*(?next) \s binding \s state \s (?\S+)
30             | hardware \s (?ethernet) \s (?\S+)
31             | option \s agent.(?remote-id) \s (?.+?)
32             | option \s agent.(?circuit-id) \s (?.+?)
33             | client-(?hostname) \s "(?[^"]+)"
34             ) /mx;
35              
36             =head1 METHODS
37              
38             =head2 new
39              
40             my $filter = POE::Filter::DHCPd::Lease->new;
41              
42             =cut
43              
44             sub new {
45 1     1 1 13 my $class = shift;
46 1         5 return bless [ q(), undef ], $class;
47             }
48              
49             =head2 get_one_start
50              
51             $self->get_one_start($stream);
52              
53             C<$stream> is an array-ref of data, that will eventually be parsed into a
54             qualified lease, returned by L or L.
55              
56             =cut
57              
58             sub get_one_start {
59 24     24 1 1815 my $self = shift;
60 24         26 my $data = shift; # array-ref of data
61              
62 24         54 $self->[BUFFER] .= join '', @$data;
63 24         44 return;
64             }
65              
66             =head2 get_one
67              
68             $leases = $self->get_one;
69              
70             C<$leases> is an array-ref, containing zero or one leases.
71              
72             starts => epoch value
73             ends => epoch value
74             binding => "active" or "free"
75             hw_ethernet => 12 chars, without ":"
76             hostname => the client hostname
77             circuit_id => circuit id from relay agent (option 82)
78             remote_id => remote id from relay agent (option 82)
79              
80             =cut
81              
82             sub get_one {
83 28     28 1 7028 my $self = shift;
84             # look for as many lines as we can find in the current buffer
85 28         30 while(1) {
86 56         58 my $string;
87             # look for lines with \r\n endings
88 56 100       422 if($self->[BUFFER] =~ /^(.*?\x0d?\x0a)/s) {
89 32         65 my $length = length $1;
90 32         83 $string = substr($self->[BUFFER],0,$length,'');
91             }
92              
93 56 100       144 return [] unless $string;
94              
95 32 100 100     183 if(!$self->[LEASE] and $string =~ /$START/) {
    100          
96 4         22 $self->[LEASE] = { ip => $1 };
97             } elsif ($self->[LEASE]) {
98 24 100       382 if ($string =~ /$PARSER;/) {
    100          
99 2     2   2048 $self->[LEASE]{$+{name}} = $+{value};
  2         1199  
  2         1040  
  16         207  
100             } elsif($string =~ /.*?$END/) {
101 4         15 return $self->_done();
102             }
103             }
104              
105             }
106              
107 0         0 return [];
108             }
109              
110             sub _done {
111 4     4   6 my $self = shift;
112              
113 4         546 my $lease = delete $self->[LEASE];
114              
115 4         8 for my $k (qw/starts ends/) {
116 8 50       286 next unless($lease->{$k});
117 8 50       307 if(my @values = $lease->{$k} =~ $DATE) {
118 8         20 $values[1]--; # decrease month
119 8         25 $lease->{$k} = timelocal(reverse @values);
120             }
121             }
122              
123 4 50       210 if(my $mac = _mac(delete $lease->{'ethernet'})) {
124 4         7 $lease->{'hw_ethernet'} = $mac;
125             }
126             # compatibility with old parser output
127 4 50       11 $lease->{'circuit_id'} = delete $lease->{'circuit-id'} if ($lease->{'circuit-id'});
128 4 50       8 $lease->{'remote_id'} = delete $lease->{'remote-id'} if ($lease->{'remote-id'});
129              
130 4         18 return [ $lease ];
131              
132             }
133              
134             sub _mac {
135 4 50   4   11 my $str = shift or return;
136              
137 4         19 $str = join "", map { sprintf "%02s", $_ } split /:/, $str;
  24         82  
138 4         14 $str =~ tr/[0-9a-fA-F]//cd;
139              
140 4 50       22 return length $str == 12 ? lc($str) : undef;
141             }
142              
143             =head2 get
144              
145             See L.
146              
147             =head2 put
148              
149             Returns an empty string. Should not be used.
150              
151             =cut
152              
153             sub put {
154 0     0 1 0 return q();
155             }
156              
157             =head2 get_pending
158              
159             my $buffer = $self->get_pending;
160              
161             Returns any data left in the buffer.
162              
163             =cut
164              
165             sub get_pending {
166 2     2 1 857 return shift->[BUFFER];
167             }
168              
169             =head1 AUTHOR
170              
171             Jan Henning Thorsen, C<< >>
172              
173             =head1 COPYRIGHT & LICENSE
174              
175             Copyright 2007 Jan Henning Thorsen, all rights reserved.
176              
177             This program is free software; you can redistribute it and/or modify it
178             under the same terms as Perl itself.
179              
180             =cut
181              
182             1;