File Coverage

blib/lib/Farly/Remove/Address.pm
Criterion Covered Total %
statement 101 112 90.1
branch 20 34 58.8
condition n/a
subroutine 15 15 100.0
pod 3 4 75.0
total 139 165 84.2


line stmt bran cond sub pod time code
1             package Farly::Remove::Address;
2            
3 1     1   789 use 5.008008;
  1         4  
  1         41  
4 1     1   7 use strict;
  1         2  
  1         37  
5 1     1   6 use warnings;
  1         2  
  1         33  
6 1     1   7 use Carp;
  1         3  
  1         1636  
7            
8             our $VERSION = '0.26';
9            
10             sub new {
11 1     1 1 13 my ( $class, $container ) = @_;
12            
13 1 50       4 confess "firewall configuration container object required"
14             unless ( defined($container) );
15            
16 1 50       12 confess "Farly::Object::List object required"
17             unless ( $container->isa("Farly::Object::List") );
18            
19 1         5 my $self = {
20             FW => $container,
21             REMOVE => Farly::Object::List->new(),
22             };
23 1         4 bless $self, $class;
24            
25 1         2 return $self;
26             }
27            
28 6     6 0 45 sub fw { return $_[0]->{FW} }
29            
30             # remove can be called many times
31             # all objects in {REMOVE} need to be removed from {FW}
32             sub remove {
33 2     2 1 6 my ( $self, $ip ) = @_;
34            
35 2 50       8 confess "ip not defined" unless defined($ip);
36            
37 2 50       19 confess "Farly::IPv4::Address address required"
38             unless defined( $ip->isa('Farly::IPv4::Address') );
39            
40             #print "\nsearching for references to ", $ip->as_string, "...\n";
41            
42 2         10 my $garbage_list = $self->_address_search($ip);
43            
44 2         11 my $objects_for_cleanup = $self->_collect_garbage($garbage_list);
45            
46 2 50       11 if ( $objects_for_cleanup->size() != 0 ) {
47            
48             #print "\nstoring removed objects in reverse order\n\n";
49 2         12 $self->_add_reversed_list($objects_for_cleanup);
50 2         10 $self->_cleanup();
51             }
52             }
53            
54 5     5 1 37 sub result { return $_[0]->{REMOVE} }
55            
56             # all objects being removed will have been marked with the
57             # 'REMOVE' property. Update the $self->{CONFIG} container to
58             # exclude all objects which are being removed. This allows
59             # the remove() method to be called multiple times for the
60             # same configuration.
61             sub _cleanup {
62 2     2   5 my ($self) = @_;
63            
64 2         12 my $new_list = Farly::Object::List->new();
65            
66 2         10 foreach my $object ( $self->fw->iter() ) {
67            
68             #print $object->dump(),"\n";
69 129 100       279 if ( !$object->has_defined('REMOVE') ) {
70 125         254 $new_list->add($object);
71             }
72             }
73            
74 2         17 $self->{FW} = $new_list;
75             }
76            
77             sub _address_search {
78 2     2   3 my ( $self, $ip ) = @_;
79            
80 2         10 my $fw = $self->{FW};
81            
82 2         12 my $search = Farly::Object->new();
83 2         8 my $search_result = Farly::Object::List->new();
84            
85 2         9 $search->set( "OBJECT", $ip );
86 2         9 $fw->contained_by( $search, $search_result );
87 2         15 $search->delete_key("OBJECT");
88            
89 2         12 $search->set( "SRC_IP", $ip );
90 2         9 $fw->contained_by( $search, $search_result );
91 2         12 $search->delete_key("SRC_IP");
92            
93 2         6 $search->set( "DST_IP", $ip );
94 2         7 $fw->contained_by( $search, $search_result );
95            
96 2         27 return $search_result;
97             }
98            
99             sub _collect_garbage {
100 2     2   7 my ( $self, $garbage_list ) = @_;
101            
102 2         5 my $fw = $self->{CONFIG};
103            
104 2         10 my $agg = Farly::Object::Aggregate->new( $self->fw );
105 2         12 $agg->groupby( 'ENTRY', 'ID' );
106            
107 2         12 my $NAME = Farly::Value::String->new('NAME');
108 2         8 my $GROUP = Farly::Value::String->new('GROUP');
109 2         11 my $RULE = Farly::Value::String->new('RULE');
110 2         7 my $OBJECT = Farly::Value::String->new('OBJECT');
111 2         9 my $INTERFACE = Farly::Value::String->new('INTERFACE');
112 2         10 my $ROUTE = Farly::Value::String->new('ROUTE');
113            
114 2         4 my @stack;
115 2         8 my $remove = Farly::Object::List->new();
116            
117 2         9 push @stack, $garbage_list->iter();
118            
119 2         7 while (@stack) {
120            
121 4         11 my $object = pop @stack;
122            
123 4 100       16 if ( $object->get('ENTRY')->equals($GROUP) ) {
    100          
    50          
    0          
    0          
    0          
124            
125             # convert the $object to a reference object
126 1         7 my $ref_obj = $self->_create_reference($object);
127            
128             #it's a group, check the size
129 1         7 my $actual = $agg->matches($ref_obj);
130            
131 1 50       6 if ( !defined $actual ) {
132 0         0 confess "error ", $object->dump(), " actual not found";
133             }
134            
135             # if the size of the group is 1 all references to the
136             # group must be removed first
137 1 50       5 if ( $actual->size == 1 ) {
138            
139             # if the group can be removed no members of that group
140             # should be in $remove, i.e. the group has already been
141             # emptied out so take all other group member objects out
142             # of remove
143 0         0 $remove = $self->_remove_copy( $remove, $ref_obj );
144            
145 0         0 $object->set( 'REMOVE', Farly::Value::String->new('GROUP') );
146 0         0 $remove->add($object);
147            
148             # each referring object must be checked to see if it can be removed
149             # all references to 'object' will be in @remove after 'object'
150            
151 0         0 my @result = $self->_reference_search($ref_obj);
152 0         0 push @stack, @result;
153             }
154             else {
155            
156             # group size > 1
157            
158             # create a new ::Set, minus the group member $object to be removed
159 1         6 my $new_set = $self->_remove_copy( $actual, $object );
160            
161             # update the index to reflect that $object is removed
162             # because more objects could be removed from the group later on
163 1         5 $agg->update( $ref_obj, $new_set );
164            
165 1         7 $object->set( 'REMOVE', Farly::Value::String->new('OBJECT') );
166 1         5 $remove->add($object);
167             }
168            
169             }
170             elsif ( $object->get('ENTRY')->equals($OBJECT) ) {
171            
172             # set the object to be removed
173 2         12 $object->set( 'REMOVE', Farly::Value::String->new('OBJECT') );
174 2         14 $remove->add($object);
175            
176             # reformat the object into a reference object
177 2         12 my $ref_obj = $self->_create_reference($object);
178            
179             # find everything that references the removed object
180 2         11 my @result = $self->_reference_search($ref_obj);
181 2         30 push @stack, @result;
182            
183             }
184             elsif ( $object->get('ENTRY')->equals($RULE) ) {
185            
186             # rules which refer to the Address directly can be removed
187             # immediately
188 1         8 $object->set( 'REMOVE', Farly::Value::String->new('RULE') );
189 1         5 $remove->add($object);
190            
191             }
192             elsif ( $object->get('ENTRY')->equals($NAME) ) {
193 0         0 next;
194             }
195             elsif ( $object->get('ENTRY')->equals($INTERFACE) ) {
196 0         0 next;
197             }
198             elsif ( $object->get('ENTRY')->equals($ROUTE) ) {
199 0         0 next;
200             }
201             else {
202 0         0 warn "\nunknown object:\n", $object->dump();
203 0         0 next;
204             }
205             }
206            
207 2         99 return $remove;
208             }
209            
210             # convert an object into a reference object
211             sub _create_reference {
212 3     3   6 my ( $self, $object ) = @_;
213            
214 3         18 my $ref = Farly::Object::Ref->new();
215 3         12 $ref->set( 'ENTRY', $object->get('ENTRY') );
216 3         12 $ref->set( 'ID', $object->get('ID') );
217            
218 3         6 return $ref;
219             }
220            
221             # find every object which refers to $search
222             sub _reference_search {
223 2     2   5 my ( $self, $search ) = @_;
224            
225 2         5 my @search_result;
226            
227 2         11 foreach my $object ( $self->fw->iter ) {
228 128         316 foreach my $property ( $object->get_keys ) {
229 769 100       1771 if ( $object->get($property)->equals($search) ) {
230 1         8 push @search_result, $object;
231             }
232             }
233             }
234            
235 2         33 return @search_result;
236             }
237            
238             # Copies the objects in $set into a new ::Set, except for the objects
239             # that match $remove, which are not copied.
240             sub _remove_copy {
241 1     1   3 my ( $self, $set, $remove ) = @_;
242            
243 1         4 my $r = Farly::Object::List->new();
244            
245 1         6 foreach my $object ( $set->iter ) {
246 4 100       10 if ( !$object->matches($remove) ) {
247 3         9 $r->add($object);
248             }
249             }
250            
251 1         4 return $r;
252             }
253            
254             # reverse the order of the remove list
255             # objects to remove must be processed last in first out order
256             # because they where pushed on the @remove array
257             sub _add_reversed_list {
258 2     2   6 my ( $self, $remove ) = @_;
259            
260 2         7 for ( my $i = $remove->size() - 1 ; $i >= 0 ; $i-- ) {
261 4 100       15 $remove->[$i]->delete_key('LINE') if $remove->[$i]->has_defined('LINE');
262 4         27 $self->result->add( $remove->[$i] );
263             }
264             }
265            
266             1;
267             __END__