Horizontal Scale BIG-IP Device Service Cluster (DSC) with Software Defined Networking (SDN) enabled Hardware.
Problem this snippet solves:
Using iApp/iRules technology, this Proof of Concept (POC) iApp shows how to integrate the F5 TMOS programmable platform with a Software Defined Networking (SDN) switch.
Code :
#TMSH-VERSION: 11.6.0 cli admin-partitions { update-partition Common } sys application template /Common/f5.sdn_scale { actions { definition { html-help { } implementation { set ::controller_name [ format "/Common/%s_controller" $tmsh::app_name ] set ::openflow_controller { # IRULE_OPENFLOW_CONTROLLER # OpenFlow 1.3 will be supported in the next version } set ::json_controller { # IRULE_JSON_CONTROLLER proc json_value { list } { set key [ lindex $list 0] set value [lindex $list 1] return "\"$key\":\"$value\"" } proc json_nest { key } { return "\"$key\":\{" } proc json_array { key } { return "\"$key\":\[" } when HTTP_REQUEST { set cluster_members [ class lookup "clusterMembers" sdn_scale_data ] set monitor_vlan [class lookup "monitorVlan" sdn_scale_data ] set dag_rule [class startsearch sdn_scale_rules] set counter_rule 0 set data "\{" append data [ call json_value { type F5SdnScale } ] append data "," append data [ call json_value { version 2 } ] append data "," append data "\"monitorVlan\":\"$monitor_vlan\"" append data "," append data "\"clusterMembers\":$cluster_members" append data "," append data [call json_array rules ] set delim "" while {[class anymore sdn_scale_rules $dag_rule]}{ set entry [class nextelement sdn_scale_rules $dag_rule] log local0. "entry is $entry" append data "$delim\{" set id "" lappend id id lappend id [lindex $entry 0] append data [call json_value $id ] append data "," append data [ call json_nest [lindex [lindex [lindex $entry 1] 0] 0] ] append data [call json_value [ lindex [lindex [lindex [lindex $entry 1] 0] 1] 0] ] append data "," append data [call json_value [ lindex [lindex [lindex [lindex $entry 1] 0] 1] 1] ] append data "\}" append data "," append data [ call json_nest [lindex [lindex [lindex $entry 1] 0] 2] ] append data [call json_value [ lindex [lindex [lindex [lindex $entry 1] 0] 3] 0] ] append data "," append data [call json_value [ lindex [lindex [lindex [lindex $entry 1] 0] 3] 1] ] append data "\}" append data "\}" set delim "," } append data "\], \"commit\":\"true\"\}" class donesearch sdn_scale_rules $dag_rule HTTP::respond 200 content $data } } proc get_net_id { net } { foreach { ip mask } [ split $net / ] break set zeros [expr 32 - ${mask}] set ones ${mask} set bm [string repeat 1 $ones] append bm [string repeat 0 $zeros] binary scan [binary format B32 $bm] H8 maskh set octets [split $ip .] binary scan [binary format c4 $octets] H8 iph return $iph$maskh } proc uuid {prefix net vlan} { set seed [ get_net_id $net ] set vlanseed [ format "%05d" $vlan ] append uuid $prefix append uuid -[string range $vlanseed 0 4] append uuid [string range $seed 0 3] append uuid -[string range $seed 4 15] return $uuid } proc getConfPrefix {virtual type} { set prefix none if { $type == "incr" } { set prefix [ readConfPrefix $virtual] } if { $prefix == "none" } { set prefix [ genConfPrefix ] } storeConfPrefix $virtual $prefix return $prefix } proc readConfPrefix { virtual } { set prefix [lindex \ [lindex \ [lindex \ [ tmsh::list /ltm virtual $virtual metadata ] \ 3 ] \ 1 ] \ 0] return $prefix } proc genConfPrefix {} { set prefix [format %2.2x [clock seconds]] append prefix -[string range [format %2.2x [clock clicks]] 0 3] append prefix -[string range [format %2.2x [clock clicks]] 2 5] append prefix -[string range [format %2.2x [clock clicks]] 4 8] return $prefix } proc storeConfPrefix {virtual prefix } { tmsh::create /ltm virtual $virtual metadata\ replace-all-with \{ $prefix \} } proc mac_incr { mac } { set bytes [ split $mac ":" ] set pos 5 set next_byte 1 while { $next_byte && [expr $pos + 1]} { set next_byte 0 set byte [lindex $bytes $pos] scan $byte "%x" dec incr dec set byte [format "%x" $dec] if { $byte == 100 } { set bytes [lreplace $bytes $pos $pos "00"] set next_byte 1 incr pos -1 } elseif { [string length $byte] == 1 } { set bytes [lreplace $bytes $pos $pos "0$byte"] set next_byte 0 } else { set bytes [lreplace $bytes $pos $pos "$byte"] set next_byte 0 } } set mac "" for {set pos 0 } {$pos < 6} {incr pos} { if { $pos == 0 } { set mac "$mac[lindex $bytes $pos]" } else { set mac "$mac:[lindex $bytes $pos]" } } return "$mac" } proc hex_to_ip {hex} { set bin [binary format I [expr {$hex}]] binary scan $bin c4 octets foreach octet $octets { lappend result [expr {$octet & 0xFF}] } return [join $result .] } proc ip_to_hex {ip } { set octets [split $ip .] binary scan [binary format c4 $octets] H8 x return 0x$x } proc net_incr {network counter } { set address [ lindex [ split $network / ] 0 ] set mask [ lindex [ split $network / ] 1] return [ hex_to_ip [ expr { [ip_to_hex $address ] + $counter } ]]/$mask } proc ip_incr {address counter} { return [ hex_to_ip [ expr { [ip_to_hex $address ] + $counter } ]] } proc split_net { net depth } { set depth [ expr { $depth -1 } ] set cdir [ lindex [split $net "/"] 1] set net [ lindex [split $net "/"] 0] set cdir [ expr { $cdir + 1} ] set lower "$net/$cdir" set higher [hex_to_ip [ expr { [ ip_to_hex $net ] | (2 << (31 -$cdir )) }]] set higher "$higher/$cdir" if { $depth > 0 && $cdir < 28 } { set lower [ split_net $lower $depth] set higher [ split_net $higher $depth] } return "$lower $higher" } proc gen_dag {node_mac} { set prefix [getConfPrefix $::controller_name $::disaggregation__update__type] foreach row $::disaggregation__subscriber { array set columns [lindex $row 0] lappend subnets $columns(net) } foreach row $::networking__net { array set network [lindex $row 0] # splitting up each of the subscriber networks as per disaggregation depth foreach subnet $subnets { set counter 0 foreach net [split_net $subnet $::disaggregation__depth] { incr counter # node1 is the default node, no DAG needed set mac [ lindex $node_mac [expr { $counter % [ llength $node_mac ] } ]] if { !(( $mac == [lindex $node_mac 0] ) \ && $::disaggregation__optimiser ) } { if { $network(dag) != "ha" } { set vlan $network(vlan_vid) set dump "{match {{ vlan_vid $vlan } \ { $network(dag) $net }} action \ {{ dl_src $mac } { port NORMAL }}}" tmsh::create /ltm data-group \ internal /Common/sdn_scale_rules records \ add \{ [ uuid $prefix $net $vlan ] \ \{ data \"$dump\" \} \} type string } } } } } } proc gen_self { traffic_group counter } { set clusterMember "" set monitorVlan "" foreach row $::networking__net { array set network [lindex $row 0] set vlan_name [ string replace $network(vlan) 0 7 ] set address [ net_incr $network(address) $counter ] if { $network(dag) == "ha" } { foreach { ip mask } [ split $address / ] break set clusterMember $ip set monitorVlan $network(vlan_vid) } tmsh::create /net self /Common/$traffic_group"_"$vlan_name \ \{ address $address \ traffic-group $traffic_group vlan $network(vlan) allow-service all \} } return $clusterMember/$monitorVlan } proc gen_net { base_mac } { set counter 1 set clusterMembers "" set dl_addresses "" foreach row $::cluster__active { array set columns [lindex $row 0] #generating the traffic groups set traffic_group sdn_scale_node_$counter tmsh::create /cm traffic-group /Common/$traffic_group \ \{ ha-order \{ $columns(nodes) $::cluster__standby__device \} mac $base_mac\} set clusterMember [ gen_self $traffic_group $counter ] foreach { clusterMemberIp monitorVlan } [ split $clusterMember / ] break lappend clusterMembers $clusterMemberIp lappend dl_addresses $base_mac set base_mac [ mac_incr $base_mac ] incr counter } storeClusterData $clusterMembers $monitorVlan return $dl_addresses } proc storeClusterData {clusterMembers monitorVlan} { set clusterMembersJSON "" set prefix "\[" set suffix "\]" set delim "" append clusterMembersJSON $prefix foreach clusterMember $clusterMembers { append clusterMembersJSON $delim\"\\\"$clusterMember\"\\\" set delim "," } append clusterMembersJSON $suffix tmsh::create /ltm data-group \ internal /Common/sdn_scale_data records \ add \{ clusterMembers \ \{ data \"$clusterMembersJSON\" \} \} type string tmsh::create /ltm data-group \ internal /Common/sdn_scale_data records \ add \{ monitorVlan \ \{ data \"$monitorVlan\" \} \} type string puts $clusterMembersJSON } proc gen_contr {} { set name [ format "/Common/%s_controller" $tmsh::app_name ] switch $::controller__instance__type { "openflow" { # currently not implemented } "json" { tmsh::create ltm rule $name \ [tmsh::expand_macro $::json_controller -debuginclusive $::debug__level ] set ip $::controller__instance__address set port $::controller__instance__port set vlan $::controller__instance__vlan tmsh::create /ltm virtual $name \ destination $ip:$port \ mirror enabled \ profiles replace-all-with \{ tcp http \} \ rules \{ $name \} \ vlans replace-all-with \{ $vlan \} vlans-enabled } } } gen_dag [ gen_net $::networking__vmac__base] gen_contr } macro { } presentation { section cluster { table active { choice nodes display "xlarge" tcl { tmsh::run_proc f5.app_utils:get_items cm device } } row standby { choice device display "xlarge" tcl { tmsh::run_proc f5.app_utils:get_items cm device } } } section networking { table net { choice vlan tcl { tmsh::run_proc f5.app_utils:get_items net vlan } string vlan_vid required display "small" string address required choice dag display "large" {"Source Address"=> "nw_src", "Destination Address" => "nw_dst", "None/HA" => "ha" } } row vmac { string base required default "02:01:00:f5:00:01" } } section controller { row instance { choice type { "Directflow" => "json"} choice vlan tcl { tmsh::run_proc f5.app_utils:get_items net vlan } string address required string port required } } section disaggregation { table subscriber { string net } choice depth default "3" { "1","2","3","4","5","6","7","8","9","10"} choice optimiser default "yes" { "yes" => "1", "no" => "0" } choice idle default "30" { "30","120"} row update { choice type default "full" {"Incremental" => "incremental", "Full" => "full" } } } section debug { choice level default "0" { "0","1","2" } } text { cluster "Device Service Cluster" cluster.active "Active Nodes" cluster.active.nodes "" cluster.standby "Standby Node" cluster.standby.device "" networking "Cluster Networking" networking.net "Dataplane" networking.net.vlan "VLAN" networking.net.vlan_vid "VLAN ID" networking.net.address "Self IP Network" networking.net.dag "Hash" networking.vmac "Virtual MAC Address" networking.vmac.base "Start Address" controller "Cluster Controller" controller.instance "Select Type" controller.instance.type "Protocol" controller.instance.vlan "VLAN" controller.instance.address "Address" controller.instance.port "Port" disaggregation "Disaggregation" disaggregation.subscriber "Subscriber" disaggregation.subscriber.net "Networks" disaggregation.depth "DAG depth" disaggregation.optimiser "Optimise Ruleset" disaggregation.idle "Idle Timeout" disaggregation.update "Update" disaggregation.update.type "Select Type" debug "Debugging" debug.level "Log Level" } } role-acl none run-as none } } description none ignore-verification false requires-bigip-version-max none requires-bigip-version-min none requires-modules { ltm } signing-key none tmpl-checksum none tmpl-signature none }
Tested this on version:
12.0Published Jan 25, 2016
Version 1.0koenning_107182
Nimbostratus
Joined May 23, 2007
koenning_107182
Nimbostratus
Joined May 23, 2007
No CommentsBe the first to comment