Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Break the foreach loop on count on 50 and then insert a new foreach loop

by iamnewbie (Novice)
on Nov 12, 2015 at 07:06 UTC ( [id://1147539]=perlquestion: print w/replies, xml ) Need Help??

iamnewbie has asked for the wisdom of the Perl Monks concerning the following question:

Please see my code below. Now what i want to do is i have $port which has more then 75 ports/ips So my command is getting failed when i try to join all my addresses in one foreach loop

So can you propose me a solution where i can change sub addRules to only do about 50 addresses at a time. The foreach would have to have another loop inside of it to do the 50 at a time instead of the current join of all the addresses on

I am a beginner in Perl so your responses with be highly appreciated

###################################################################### +######### # Name: addRules() # Parameters: hash: # protocol - Network protocol (ipv6 or ipv4) # ports - List of Net::Port objects. # addresses - List of addresses of this type. # domains - List of iptables domains # Return: 0 failure, 1 success # Description: # For a given set of ports add accept rules for all the specified a +ddresses # across all the specified net protocols and domains. ###################################################################### +######### sub addRules { my %args = @_; my $protocol = $args{protocol}; my $ports = $args{ports}; my $addresses = $args{addresses}; my $domain = $args{domain}; my $appendCmd = "iptablesAdm append"; $appendCmd .= " --type=rule"; $appendCmd .= " --table=filter"; $appendCmd .= " --chain=INPUT"; $appendCmd .= " --protocol=${protocol}"; $appendCmd .= " --domain=${domain}"; $appendCmd .= " --persist=yes"; Dbug->debug("Base Insert CMD: ${appendCmd}"); # # Loop over the ports and create a rule for each of the addresses +with each port: foreach my $port (@$ports) { my $portNum = $port->port(); my $transport = $port->transportStr(); Dbug->log("Inserting rules for ${portNum} addresses @{$address +es}"); # # Build up the match string portion of the iptables command: my $matchString = "-m state"; $matchString .= " --state NEW"; $matchString .= " -m ${transport}"; $matchString .= " --protocol ${transport}"; $matchString .= " --dport ${portNum}"; $matchString .= " -s " . join(',', @$addresses); $matchString .= " -j ACCEPT"; # # Build the full command: my $cmd = "${appendCmd} --match=\"${matchString}\""; # # Run the command: Dbug->log("\tCMD: ${cmd}\n"); system($cmd) && do { Dbug->error("Rule insert failed!"); Dbug->error("CMD: ${cmd}"); return 0; }; } # # As a side effect of getting the rules we will display what they +are right # now, and we would like to see what things look like after adding + this # rule: printRules('protocol' => $protocol, 'domain' => $domain); return 1; }
  • Comment on Break the foreach loop on count on 50 and then insert a new foreach loop
  • Download Code

Replies are listed 'Best First'.
Re: Break the foreach loop on count on 50 and then insert a new foreach loop
by Discipulus (Canon) on Nov 12, 2015 at 09:10 UTC
    Hello iamnewbie and welcome to the monastery and to wonderful world of Perl

    Without enter in the merit of what are you trying to do, the basic question is: how can i interupt a loop every n iteration? use a counter. you can profit of the redo function and more generally speaking of the loop control. Search also ModernPerl for loop control (is here..).

    Follow what this oneliner does:
    perl -e "@list=(1..7);$counter=0; foreach $it (@list){if ($counter >= +3){print qq(\n\n);$counter = 0; redo}print qq($it\n); $counter++}" 1 2 3 4 5 6 7
    Your case is more complicated because there is there also the port to be associated. But you'll do on your own.

    That said i think is not the best strategy: a subroutine will be better and easier: the sub receive as input the port and address list, if that list is bigger than 50, it compose the command shifting the first 50 elements and recall itself with the same port and the remaining list, like (just a draft)
    sub proces_no_more_than_50 { my($port,@addresses)=@_; if ($#addresses >= 50) { &execute( $port, map {shift @addresses} 1..50 ); } else{ &execute($port, @addresses); undef @addresses;} &proces_no_more_than_50($port,@addresses) if defined $addresses[0]; }


    HtH
    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

      Another possibility (also untested):

      use constant MAX => 50; sub process_up_to_MAX_at_a_time { my ($port, # comment @addresses, # ... ) = @_; while (@addresses > MAX) { my @sub_group = splice @addresses, 0, MAX; process_0_to_MAX($port, @sub_group); } process_0_to_MAX($port, @addresses); } sub process_0_to_MAX { ... }
      See splice. process_up_to_MAX_at_a_time() could also be made purely recursive... if you're into that sort of thing.

      BTW: Beware of comparisons like  $#addresses >= 50 when checking array size: it's an off-by-one pitfall. It seems ok in the  proces_no_more_than_50() example, but I find direct comparisons against an array (e.g.,  @addresses > 50) to be more intuitive and less error-prone in general. Also note that assignment to  $[ (but don't do that!) changes the value of  $#array (see perlvar). Evaluating  @array in scalar context always yields the number of elements in the array. (The  $[ special variable is deprecated as of Perl version 5.12; see the latest perlvar for its behavior after version 5.16. (Update: Better yet, check your local  perldoc perlvar for its behavior on your system — and then don't touch it.))


      Give a man a fish:  <%-{-{-{-<

        wise advices and good code AnomalousMonk! as usual

        iamnewbie be sure to understand the off-by-one pitfall explained by AnomalousMonk.

        L*
        There are no rules, there are no thumbs..
        Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

        I had fixed my code with below resolution

        # Build up the match string portion of the iptables command: my @addresses2 = @$addresses; # Before Splice take backup in +to another variable while (scalar @addresses2) { my $matchString = "-m state"; $matchString .= " --state NEW"; $matchString .= " -m ${transport}"; $matchString .= " --protocol ${transport}"; $matchString .= " --dport ${portNum}"; $matchString .= " -s " . join(',', splice(@addresses2 +, 0, 50)); $matchString .= " -j ACCEPT"; # # Build the full command: my $cmd = "${appendCmd} --match=\"${matchString}\"";

        Hi @AnomalousMonk Between i couldn't really understand what you mean here with your code can you please explain.

      @Discipulus Please see what i did with the code and it works fine now but the problem is that it is executing one address at a time so it takes more time then it should take so is there a way i can make my new code more effiecient?

      See my new code below

      sub addRules { my %args = @_; my $protocol = $args{protocol}; my $ports = $args{ports}; my $addresses = $args{addresses}; my $domain = $args{domain}; my $appendCmd = "iptablesAdm append"; $appendCmd .= " --type=rule"; $appendCmd .= " --table=filter"; $appendCmd .= " --chain=INPUT"; $appendCmd .= " --protocol=${protocol}"; $appendCmd .= " --domain=${domain}"; $appendCmd .= " --persist=yes"; Dbug->debug("Base Insert CMD: ${appendCmd}"); # # Loop over the ports and create a rule for each of the addresses +with each port: foreach my $port (@$ports) { my $portNum = $port->port(); my $transport = $port->transportStr(); Dbug->log("Inserting rules for ${portNum} addresses @{$address +es}"); # # Build up the match string portion of the iptables command: foreach my $addr (@$addresses) { my $matchString = "-m state"; $matchString .= " --state NEW"; $matchString .= " -m ${transport}"; $matchString .= " --protocol ${transport}"; $matchString .= " --dport ${portNum}"; $matchString .= " -s $addr"; $matchString .= " -j ACCEPT"; # # Build the full command: my $cmd = "${appendCmd} --match=\"${matchString}\""; # # Run the command: Dbug->log("\tCMD: ${cmd}\n"); system($cmd) && do { Dbug->error("Rule insert failed!"); Dbug->error("CMD: ${cmd}"); return 0; }; } } # # As a side effect of getting the rules we will display what they +are right # now, and we would like to see what things look like after adding + this # rule: printRules('protocol' => $protocol, 'domain' => $domain); return 1; }
Re: Break the foreach loop on count on 50 and then insert a new foreach loop
by choroba (Cardinal) on Nov 12, 2015 at 08:33 UTC
Re: Break the foreach loop on count on 50 and then insert a new foreach loop
by ctilmes (Vicar) on Nov 12, 2015 at 13:31 UTC
    BTW, for this type of thing:
    my $appendCmd = "iptablesAdm append"; $appendCmd .= " --type=rule"; $appendCmd .= " --table=filter"; $appendCmd .= " --chain=INPUT"; $appendCmd .= " --protocol=${protocol}"; $appendCmd .= " --domain=${domain}"; $appendCmd .= " --persist=yes";
    you can just do this:
    my $appendCmd = "iptablesAdm append" . " --type=rule" . " --table=filter" . " --chain=INPUT" . " --protocol=${protocol}" . " --domain=${domain}" . " --persist=yes";
Re: Break the foreach loop on count on 50 and then insert a new foreach loop
by Lennotoecom (Pilgrim) on Nov 12, 2015 at 14:31 UTC
    so you basically want to apply some rules into iptables?
    please just simply provide a small part of your original data e.g. list of ips
    and a single line of the desired output

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1147539]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (4)
As of 2024-04-25 08:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found