procs
3 TopicsWhat are good or bad uses of irule Procs
I want to provide global debugging control over irules I have deployed so that I can target the logging of debug information for specific events, hosts, uri, vs, pool, etc, across these irules. I plan to use a proc that will log a message to the LTM log if the current set of debug targets are met. The targets will be set as static variables. This proc will be called multiple times in a variety of irules I have written to log debug information. I am concerned that using a Proc will introduce a performance issue. Please advise287Views0likes1CommentNested Procs in an iRule
Since iRules don't support the expr ceil(args) function, I'm trying to implement a TCL version that I found at https://searchcode.com/codesearch/view/16742115/ This implement uses multiple reusable procs. When I format my iRule with these procs, the recusion doesn't seem to process correctly and I'm wondering if there are any restrictions here? The below code results in this error on my system (11.4.1 HF3) 01070151:3: Rule [/Common/math] error: /Common/math:37: error: [undefined procedure: isFloat][isFloat $x] /Common/math:37: error: [undefined procedure: isInt][isInt $x] /Common/math:59: error: [undefined procedure: checkFloat][checkFloat $x] /Common/math:71: error: [undefined procedure: checkNumber][checkNumber $a] /Common/math:72: error: [undefined procedure: iszero][iszero $a] /Common/math:109: error: [undefined procedure: bits][bits $delta] /Common/math:131: error: [undefined procedure: normalize][normalize $number] /Common/math:145: error: [undefined procedure: opp][opp $try] These have been copied from https://searchcode.com/codesearch/view/16742115/ returns 1 if x is a BigFloat, 0 elsewhere proc isFloat {x} { a BigFloat is a list of : "F" mantissa exponent delta if {[llength $x]!=4} { return 0 } the marker is the letter "F" if {[string equal [lindex $x 0] F]} { return 1 } return 0 } checks that n is a BigInt (a number create by math::bignum::fromstr) proc isInt {n} { if {[llength $n]>1} { return 0 } if {[string is digit $n]} { return 1 } return 1 } checks if each number is either a BigFloat or a BigInt arguments : each argument is the name of a variable to be checked proc checkNumber {x} { if {![isFloat $x] && ![isInt $x]} { error "input is not an integer, nor a BigFloat" } } checks each variable to be a BigFloat arguments : each argument is the name of a variable to be checked proc checkFloat {number} { if {![isFloat $number]} { error "BigFloat expected" } } returns 1 if x is null, 0 otherwise proc iszero {x} { if {[isInt $x]} { return [expr {$x==0}] } checkFloat $x now we do some interval rounding : if a number's interval englobs 0, it is considered to be equal to zero foreach {dummy integer exp delta} $x {break} if {$delta>=abs($integer)} {return 1} return 0 } returns -A (the opposite) proc opp {a} { checkNumber $a if {[iszero $a]} { return $a } if {[isInt $a]} { return [expr {-$a}] } recursive call lset a 1 [expr {-[lindex $a 1]}] return $a } bits : computes the number of bits of an integer, approx. proc bits {int} { set l [string length [set int [expr {abs($int)}]]] int<10**l -> log_2(int)=l*log_2(10) set l [expr {int($l*log(10)/log(2))+1}] if {$int>>$l!=0} { error "bad result: $l bits" } while {($int>>($l-1))==0} { incr l -1 } return $l } normalizes a number : Delta (accuracy of the BigFloat) has to be limited, because the memory use increase quickly when we do some computations, as the Mantissa and Delta increase together The solution : limit the size of Delta to 16 bits proc normalize {number} { checkFloat $number foreach {dummy integer exp delta} $number {break} set l [bits $delta] if {$l>16} { incr l -16 $l holds the supplementary size (in bits) now we can shift right by $l bits always round upper the Delta set delta [expr {$delta>>$l}] incr delta set integer [expr {$integer>>$l}] incr exp $l } return [list F $integer $exp $delta] } returns the integer part of a BigFloat, as a BigInt the result is the same one you would have if you had called [expr {ceil($x)}] Case C1631422 proc ceil {number} { checkFloat $number set number [normalize $number] if {[iszero $number]} { return 0 } foreach {dummy integer exp delta} $number {break} if {$exp>=0} { error "not enough precision to perform rounding (ceil)" } saving the sign ... set sign [expr {$integer<0}] set integer [expr {abs($integer)}] integer part set try [expr {$integer>>(-$exp)}] if {$sign} { return [opp $try] } fractional part if {($try<<(-$exp))!=$integer} { return [incr try] } return $try } These have been copied from https://searchcode.com/codesearch/view/16742115/ returns 1 if x is a BigFloat, 0 elsewhere proc isFloat {x} { a BigFloat is a list of : "F" mantissa exponent delta if {[llength $x]!=4} { return 0 } the marker is the letter "F" if {[string equal [lindex $x 0] F]} { return 1 } return 0 } checks that n is a BigInt (a number create by math::bignum::fromstr) proc isInt {n} { if {[llength $n]>1} { return 0 } if {[string is digit $n]} { return 1 } return 1 } checks if each number is either a BigFloat or a BigInt arguments : each argument is the name of a variable to be checked proc checkNumber {x} { if {![isFloat $x] && ![isInt $x]} { error "input is not an integer, nor a BigFloat" } } checks each variable to be a BigFloat arguments : each argument is the name of a variable to be checked proc checkFloat {number} { if {![isFloat $number]} { error "BigFloat expected" } } returns 1 if x is null, 0 otherwise proc iszero {x} { if {[isInt $x]} { return [expr {$x==0}] } checkFloat $x now we do some interval rounding : if a number's interval englobs 0, it is considered to be equal to zero foreach {dummy integer exp delta} $x {break} if {$delta>=abs($integer)} {return 1} return 0 } returns -A (the opposite) proc opp {a} { checkNumber $a if {[iszero $a]} { return $a } if {[isInt $a]} { return [expr {-$a}] } recursive call lset a 1 [expr {-[lindex $a 1]}] return $a } bits : computes the number of bits of an integer, approx. proc bits {int} { set l [string length [set int [expr {abs($int)}]]] int<10**l -> log_2(int)=l*log_2(10) set l [expr {int($l*log(10)/log(2))+1}] if {$int>>$l!=0} { error "bad result: $l bits" } while {($int>>($l-1))==0} { incr l -1 } return $l } normalizes a number : Delta (accuracy of the BigFloat) has to be limited, because the memory use increase quickly when we do some computations, as the Mantissa and Delta increase together The solution : limit the size of Delta to 16 bits proc normalize {number} { checkFloat $number foreach {dummy integer exp delta} $number {break} set l [bits $delta] if {$l>16} { incr l -16 $l holds the supplementary size (in bits) now we can shift right by $l bits always round upper the Delta set delta [expr {$delta>>$l}] incr delta set integer [expr {$integer>>$l}] incr exp $l } return [list F $integer $exp $delta] } returns the integer part of a BigFloat, as a BigInt the result is the same one you would have if you had called [expr {ceil($x)}] Case C1631422 proc ceil {number} { checkFloat $number set number [normalize $number] if {[iszero $number]} { return 0 } foreach {dummy integer exp delta} $number {break} if {$exp>=0} { error "not enough precision to perform rounding (ceil)" } saving the sign ... set sign [expr {$integer<0}] set integer [expr {abs($integer)}] integer part set try [expr {$integer>>(-$exp)}] if {$sign} { return [opp $try] } fractional part if {($try<<(-$exp))!=$integer} { return [incr try] } return $try }264Views0likes1CommentQ&A Highlights - Phone Number Normalization
DevCentraler Mike posted a question today about the best way to approach normalizing phone numbers from Active Directory. Turns out not everyone is a an of normalizing them all in AD, so Mike reached out for suggestions. Regex is always a popular go-to tool for tasks like this, but that's rarely the best approach in iRules. Basically, the phone number can exist in many forms when it's slurped in for evaluation 123.456.7890 123-456-7890 123 456 7890 (123) 456-7890 And on, and on. Current King of the Hill and F5er Kevin replied back with a snazzy and incredibly simple string map. Rather than move everything to a standard format with dashes, dots, parantheses, or some combination of some or all of the above, Kevin's solution just removes them all and just focuses on the numbers themselves. Again, super simple: set num1 "123.456.7890" set num2 "123-456-7890" set num3 "123 456 7890" set num4 "(123) 456-7890" log local0. [string map {"." "" " " "" "-" "" "(" "" ")" ""} $num1] log local0. [string map {"." "" " " "" "-" "" "(" "" ")" ""} $num2] log local0. [string map {"." "" " " "" "-" "" "(" "" ")" ""} $num3] log local0. [string map {"." "" " " "" "-" "" "(" "" ")" ""} $num4] #output 1234567890 1234567890 1234567890 1234567890 The string map is basically just looking for the dot, the space, the dash, or the parentheses and mapping it to a null character. Pretty slick. I had another solution I cooked up as well, that also removes the non-numeric characters, but will do it for all non-numeric characters, not just the ones specified: %proc justNumbers {pn} { set nums "0 1 2 3 4 5 6 7 8 9" set newNumber "" foreach x [split $pn ""] { if { !([lsearch $nums $x] == -1) } { append newNumber $x } } return $newNumber } % set x1 "1-800-632-6223" 1-800-632-6223 % set x2 "1.800.632.6223" 1.800.632.6223 % set x3 "1(800)632-6223" 1(800)632-6223 % justNumbers $x1 18006326223 % justNumbers $x2 18006326223 % justNumbers $x3 18006326223 Both work, just different approaches. Kevin's approach, however, is fast. Very fast. % time { string map {"." "" " " "" "-" "" "(" "" ")" ""} $x3 } 100000 1.10809 microseconds per iteration % time { justNumbers $x3 } 100000 9.95664 microseconds per iteration Like nearly 89% faster! So if you have a specific list of characters to remove from a string, phone number or otherwise, string map is your baby. But if they are unknown, the proc approach is there for the taking. Check out the original Q&A discussion, and make sure you vote up Mike's question and Kevin's spectacular response.230Views0likes0Comments