diff --git a/opentrafficshaper/plugins/configmanager.pm b/opentrafficshaper/plugins/configmanager.pm index 9f0f7a6fcbf74e344cf9358cf2a0ce81abfce81f..a5576859bd34719a590eaf8aef0c2934d7a8266e 100644 --- a/opentrafficshaper/plugins/configmanager.pm +++ b/opentrafficshaper/plugins/configmanager.pm @@ -55,7 +55,6 @@ our (@ISA,@EXPORT,@EXPORT_OK); isGroupIDValid createTrafficClass - changeTrafficClass getTrafficClass getTrafficClasses getInterfaceTrafficClass @@ -67,7 +66,11 @@ our (@ISA,@EXPORT,@EXPORT_OK); createInterface createInterfaceClass createInterfaceGroup - getEffectiveInterfaceTrafficClass + changeInterfaceTrafficClass + getEffectiveInterfaceTrafficClass2 + isInterfaceTrafficClassValid + setInterfaceTrafficClassShaperState + unsetInterfaceTrafficClassShaperState createLimit @@ -176,6 +179,19 @@ sub POOL_PERSISTENT_ATTRIBUTES { ) } +# Class attributes that can be changed (overridden) +sub CLASS_CHANGE_ATTRIBUTES { + qw( + CIR Limit + ) +} + +# Class attributes that can be overidden +sub CLASS_OVERRIDE_CHANGESET_ATTRIBUTES { + qw( + CIR Limit + ) +} # Mandatory pool member attributes sub POOLMEMBER_REQUIRED_ATTRIBUTES { @@ -488,8 +504,12 @@ sub plugin_init $globals->{'PoolOverrides'} = { }; $globals->{'PoolOverrideIDCounter'} = 1; + $globals->{'InterfaceTrafficClasses'} = { }; + $globals->{'InterfaceTrafficClassCounter'} = 1; + $globals->{'PoolChangeQueue'} = { }; $globals->{'PoolMemberChangeQueue'} = { }; + $globals->{'InterfaceTrafficClassChangeQueue'} = { }; # If we have global config, use it my $gconfig = { }; @@ -882,6 +902,8 @@ sub plugin_init } } +# TODO - loop and queue init interfaces? + # Check if we have a state file if (defined(my $statefile = $system->{'file.config'}->{'system'}->{'statefile'})) { $config->{'statefile'} = $statefile; @@ -1033,6 +1055,40 @@ sub _session_tick $globals->{'LastCleanup'} = $now; } + # Loop through interface traffic classes + while (my ($interfaceTrafficClassID, $interfaceTrafficClass) = each(%{$globals->{'InterfaceTrafficClassChangeQueue'}})) { + my $shaperState = getInterfaceTrafficClassShaperState($interfaceTrafficClassID); + + # Traffic class has been changed + if ($interfaceTrafficClass->{'Status'} == CFGM_CHANGED) { + # If the shaper is live we can go ahead + if ($shaperState & SHAPER_LIVE) { + $logger->log(LOG_NOTICE,"[CONFIGMANAGER] Interface traffic class [%s] has been modified, sending to shaper", + $interfaceTrafficClassID + ); + $kernel->post('shaper' => 'class_change' => $interfaceTrafficClassID); + # Set pending online + setInterfaceTrafficClassShaperState($interfaceTrafficClassID,SHAPER_PENDING); + $interfaceTrafficClass->{'Status'} = CFGM_ONLINE; + # Remove from queue + delete($globals->{'InterfaceTrafficClassChangeQueue'}->{$interfaceTrafficClassID}); + + } else { + $logger->log(LOG_ERR,"[CONFIGMANAGER] Interface traffic class [%s] has UNKNOWN state '%s'", + $interfaceTrafficClassID, + $shaperState + ); + } + + } else { + $logger->log(LOG_ERR,"[CONFIGMANAGER] Interface traffic class [%s] has UNKNOWN status '%s'", + $interfaceTrafficClassID, + $interfaceTrafficClass->{'Status'} + ); + } + } + + # Loop through pool change queue while (my ($pid, $pool) = each(%{$globals->{'PoolChangeQueue'}})) { my $shaperState = getPoolShaperState($pool->{'ID'}); @@ -1672,22 +1728,55 @@ sub getInterfaceTrafficClass if (!isInterfaceIDValid($interfaceID)) { return; } - # Check if t raffic class ID is valid - if (!isTrafficClassIDValid($trafficClassID)) { + # Check if traffic class ID is valid + if (!defined($trafficClassID = isNumber($trafficClassID,ISNUMBER_ALLOW_ZERO))) { + return; + } + if ($trafficClassID && !isTrafficClassIDValid($trafficClassID)) { return; } - my $class = dclone($globals->{'Interfaces'}->{$interfaceID}->{'TrafficClasses'}->{$trafficClassID}); + my $interfaceTrafficClass = dclone($globals->{'Interfaces'}->{$interfaceID}->{'TrafficClasses'}->{$trafficClassID}); + + # Check if the traffic class ID is not 0 + if ($trafficClassID) { + $interfaceTrafficClass->{'Name'} = $globals->{'TrafficClasses'}->{$trafficClassID}->{'Name'}; + # If if it 0, this is a root class + } else { + $interfaceTrafficClass->{'Name'} = "Root Class"; + } - $class->{'Name'} = $globals->{'TrafficClasses'}->{$trafficClassID}; + delete($interfaceTrafficClass->{'.applied_overrides'}); - return $class; + return $interfaceTrafficClass; +} + + + +# Function to get a interface traffic class +sub getInterfaceTrafficClass2 +{ + my $interfaceTrafficClassID = shift; + + + # Check if this interface ID is valid + if (!isInterfaceTrafficClassIDValid2($interfaceTrafficClassID)) { + return; + } + + my $interfaceTrafficClass = dclone($globals->{'InterfaceTrafficClasses'}->{$interfaceTrafficClassID}); + + $interfaceTrafficClass->{'Name'} = $globals->{'TrafficClasses'}->{$interfaceTrafficClass->{'TrafficClassID'}}; + + delete($interfaceTrafficClass->{'.applied_overrides'}); + + return $interfaceTrafficClass; } # Function to check if traffic class is valid -sub isInterfaceTrafficClassIDValid +sub isInterfaceTrafficClassValid { my ($interfaceID,$trafficClassID) = @_; @@ -1700,7 +1789,228 @@ sub isInterfaceTrafficClassIDValid return; } - return $trafficClassID; + return $globals->{'Interfaces'}->{$interfaceID}->{'TrafficClasses'}->{$trafficClassID}->{'ID'}; +} + + + +# Function to check the interface traffic class ID is valid +sub isInterfaceTrafficClassIDValid2 +{ + my $interfaceTrafficClassID = shift; + + + if ( + !defined($interfaceTrafficClassID) || + !defined($globals->{'InterfaceTrafficClasses'}->{$interfaceTrafficClassID}) + ) { + return; + } + + return $interfaceTrafficClassID; +} + + + +# Function to create an interface class +sub createInterfaceTrafficClass +{ + my $interfaceTrafficClassData = shift; + + + my $interfaceTrafficClass; + + # Check if InterfaceID is valid + if (!defined($interfaceTrafficClass->{'InterfaceID'} = isInterfaceIDValid($interfaceTrafficClassData->{'InterfaceID'}))) { + $logger->log(LOG_WARN,"[CONFIGMANAGER] Failed to add interface traffic class as InterfaceID is invalid"); + return; + } + + # Check if traffic class ID is valid + my $interfaceTrafficClassID; + if (!defined($interfaceTrafficClassID = isNumber($interfaceTrafficClassData->{'TrafficClassID'},ISNUMBER_ALLOW_ZERO))) { + $logger->log(LOG_WARN,"[CONFIGMANAGER] Cannot process class change as there is no 'TrafficClassID' attribute"); + return; + } + if ($interfaceTrafficClassID && !isTrafficClassIDValid($interfaceTrafficClassData->{'TrafficClassID'})) { + $logger->log(LOG_WARN,"[CONFIGMANAGER] Cannot process class change as 'TrafficClassID' attribute is invalid"); + return; + } + $interfaceTrafficClass->{'TrafficClassID'} = $interfaceTrafficClassID; + + # Check CIR is valid + if (!defined($interfaceTrafficClass->{'CIR'} = isNumber($interfaceTrafficClassData->{'CIR'}))) { + $logger->log(LOG_WARN,"[CONFIGMANAGER] Failed to add interface as CIR is invalid"); + return; + } + + # Check Limit is valid + if (!defined($interfaceTrafficClass->{'Limit'} = isNumber($interfaceTrafficClassData->{'Limit'}))) { + $logger->log(LOG_WARN,"[CONFIGMANAGER] Failed to add interface as Limit is invalid"); + return; + } + + # Set ID + $interfaceTrafficClass->{'ID'} = $globals->{'InterfaceTrafficClassCounter'}++; + + # Set status + $interfaceTrafficClass->{'Status'} = CFGM_NEW; + + # Add interface + $globals->{'Interfaces'}->{$interfaceTrafficClass->{'InterfaceID'}}->{'TrafficClasses'} + ->{$interfaceTrafficClass->{'TrafficClassID'}} = $interfaceTrafficClass; + + # Link to interface traffic classes + $globals->{'InterfaceTrafficClasses'}->{$interfaceTrafficClass->{'ID'}} = $interfaceTrafficClass; + + # TODO: Hack, this should set NOTLIVE & NEW and have the shaper create as per note in plugin_init section + # Set status on this interface traffic class + setInterfaceTrafficClassShaperState($interfaceTrafficClass->{'ID'},SHAPER_LIVE); + $interfaceTrafficClass->{'Status'} = CFGM_ONLINE; + + return $interfaceTrafficClass->{'TrafficClassID'}; +} + + + +# Function to change a traffic class +sub changeInterfaceTrafficClass +{ + my $interfaceTrafficClassData = shift; + + + # Check interface exists first + my $interfaceID; + if (!defined($interfaceID = isInterfaceIDValid($interfaceTrafficClassData->{'InterfaceID'}))) { + $logger->log(LOG_WARN,"[CONFIGMANAGER] Cannot process interface class change as there is no 'InterfaceID' attribute"); + return; + } + + # Check if traffic class ID is valid + my $trafficClassID; + if (!defined($trafficClassID = isNumber($interfaceTrafficClassData->{'TrafficClassID'},ISNUMBER_ALLOW_ZERO))) { + $logger->log(LOG_WARN,"[CONFIGMANAGER] Cannot process class change as there is no 'TrafficClassID' attribute"); + return; + } + if ($trafficClassID && !isTrafficClassIDValid($interfaceTrafficClassData->{'TrafficClassID'})) { + $logger->log(LOG_WARN,"[CONFIGMANAGER] Cannot process class change as 'TrafficClassID' attribute is invalid"); + return; + } + + my $interfaceTrafficClass = $globals->{'Interfaces'}->{$interfaceID}->{'TrafficClasses'}->{$trafficClassID}; + + my $changes = getHashChanges($interfaceTrafficClass,$interfaceTrafficClassData,[CLASS_CHANGE_ATTRIBUTES]); + + # Bump up changes + $globals->{'StateChanged'}++; + + # Flag changed + $interfaceTrafficClass->{'Status'} = CFGM_CHANGED; + + # XXX - hack our override in + $interfaceTrafficClass->{'.applied_overrides'}->{'change'} = $changes; + + # Add to change queue + $globals->{'InterfaceTrafficClassChangeQueue'}->{$interfaceTrafficClass->{'ID'}} = $interfaceTrafficClass; + + # Return what was changed + return dclone($changes); +} + + + +# Function to return a class with any items changed as per class overrides +sub getEffectiveInterfaceTrafficClass2 +{ + my $interfaceTrafficClassID = shift; + + + my $interfaceTrafficClass; + if (!defined($interfaceTrafficClass = getInterfaceTrafficClass2($interfaceTrafficClassID))) { + return; + } + + my $realInterfaceTrafficClass = $globals->{'InterfaceTrafficClasses'}->{$interfaceTrafficClassID}; + + # If we have applied class overrides, check out what changes there may be + if (defined(my $appliedClassOverrides = $realInterfaceTrafficClass->{'.applied_overrides'})) { + my $interfaceTrafficClassOverrideSet; + + # Loop with class overrides in ascending fashion, least matches to most + foreach my $interfaceTrafficClassID ( + sort { $appliedClassOverrides->{$a} <=> $appliedClassOverrides->{$b} } keys %{$appliedClassOverrides} + ) { + my $interfaceTrafficClassOverride = $appliedClassOverrides->{$interfaceTrafficClassID}; + + # Loop with attributes and create our override set + foreach my $attr (CLASS_OVERRIDE_CHANGESET_ATTRIBUTES) { + # Set class override set attribute if the class override has defined it + if (defined($interfaceTrafficClassOverride->{$attr}) && $interfaceTrafficClassOverride->{$attr} ne "") { + $interfaceTrafficClassOverrideSet->{$attr} = $interfaceTrafficClassOverride->{$attr}; + } + } + } + # Set class overrides on pool + if (defined($interfaceTrafficClassOverrideSet)) { + foreach my $attr (keys %{$interfaceTrafficClassOverrideSet}) { + $interfaceTrafficClass->{$attr} = $interfaceTrafficClassOverrideSet->{$attr}; + } + } + } + + return $interfaceTrafficClass; +} + + + +# Function to set interface traffic class shaper state +sub setInterfaceTrafficClassShaperState +{ + my ($interfaceTrafficClassID,$state) = @_; + + + # Check interface traffic class exists first + if (!isInterfaceTrafficClassIDValid2($interfaceTrafficClassID)) { + return; + } + + $globals->{'InterfacesTrafficClasses'}->{$interfaceTrafficClassID}->{'.shaper_state'} |= $state; + + return $globals->{'InterfacesTrafficClasses'}->{$interfaceTrafficClassID}->{'.shaper_state'}; +} + + + +# Function to unset interface traffic class shaper state +sub unsetInterfaceTrafficClassShaperState +{ + my ($interfaceTrafficClassID,$state) = @_; + + + # Check interface traffic class exists first + if (!isInterfaceTrafficClassIDValid2($interfaceTrafficClassID)) { + return; + } + + $globals->{'InterfacesTrafficClasses'}->{$interfaceTrafficClassID}->{'.shaper_state'} &= ~$state; + + return $globals->{'InterfacesTrafficClasses'}->{$interfaceTrafficClassID}->{'.shaper_state'}; +} + + + +# Function to get shaper state for a interface traffic class +sub getInterfaceTrafficClassShaperState +{ + my $interfaceTrafficClassID = shift; + + + # Check interface traffic class exists first + if (!isInterfaceTrafficClassIDValid2($interfaceTrafficClassID)) { + return; + } + + return $globals->{'InterfacesTrafficClasses'}->{$interfaceTrafficClassID}->{'.shaper_state'}; } @@ -1795,6 +2105,14 @@ sub createInterface # Add interface $globals->{'Interfaces'}->{$interface->{'ID'}} = $interface; + # Create interface main traffic class + createInterfaceTrafficClass({ + 'InterfaceID' => $interface->{'ID'}, + 'TrafficClassID' => 0, + 'CIR' => $interfaceData->{'Limit'}, + 'Limit' => $interfaceData->{'Limit'}, + }); + return $interface->{'ID'}; } @@ -1856,141 +2174,6 @@ sub getInterfaceDefaultPool -# Function to create an interface class -sub createInterfaceTrafficClass -{ - my $trafficClassData = shift; - - - my $trafficClass; - - # Check if InterfaceID is valid - if (!defined($trafficClass->{'InterfaceID'} = isInterfaceIDValid($trafficClassData->{'InterfaceID'}))) { - $logger->log(LOG_NOTICE,"[CONFIGMANAGER] Failed to add interface traffic class as InterfaceID is invalid"); - return; - } - - # Check if TrafficClass is valid - if (!defined($trafficClass->{'TrafficClassID'} = isTrafficClassIDValid($trafficClassData->{'TrafficClassID'}))) { - $logger->log(LOG_NOTICE,"[CONFIGMANAGER] Failed to add interface traffic class as TrafficClassID is invalid"); - return; - } - - # Check CIR is valid - if (!defined($trafficClass->{'CIR'} = isNumber($trafficClassData->{'CIR'}))) { - $logger->log(LOG_NOTICE,"[CONFIGMANAGER] Failed to add interface as CIR is invalid"); - return; - } - - # Check Limit is valid - if (!defined($trafficClass->{'Limit'} = isNumber($trafficClassData->{'Limit'}))) { - $logger->log(LOG_NOTICE,"[CONFIGMANAGER] Failed to add interface as Limit is invalid"); - return; - } - - # Add interface - $globals->{'Interfaces'}->{$trafficClass->{'InterfaceID'}}->{'TrafficClasses'} - ->{$trafficClass->{'TrafficClassID'}} = $trafficClass; - - return $trafficClass->{'TrafficClassID'}; -} - - - -# Function to change a traffic class -sub changeInterfaceTrafficClass -{ - my $classData = shift; - - - # Check interface exists first - if (!isInterfaceIDValid($classData->{'InterfaceID'})) { - $logger->log(LOG_WARN,"[CONFIGMANAGER] Cannot process class change as there is no 'InterfaceID' attribute"); - return; - } - - if ( - !defined(isNumber($classData->{'TrafficClassID'},ISNUMBER_ALLOW_ZERO)) || - ($classData->{'TrafficClassID'} && !isTrafficClassIDValid($classData->{'TrafficClassID'})) - ) { - $logger->log(LOG_WARN,"[CONFIGMANAGER] Cannot process class change as there is no 'TrafficClassID' attribute"); - return; - } - - my $interfaceClass = getInterfaceTrafficClass($classData->{'InterfaceID'},$classData->{'TrafficClassID'}); - - my $changes = getHashChanges($interfaceClass,$classData,[CLASS_CHANGE_ATTRIBUTES]); - # Make changes... - foreach my $item (keys %{$changes}) { - $interfaceClass->{$item} = $changes->{$item}; - } - - # Bump up changes - $globals->{'StateChanged'}++; - -# FIXME - Add to change queue? -# - Create getEffectiveTrafficClass -# $kernel->post('shaper' => 'class_change' => $classData->{'InterfaceID'} => $classData->{'TrafficClassID'}); - - - # Return what was changed - return dclone($changes); -} - - - -# FIXME -# Function to return a class with any items changed as per class overrides -sub getEffectiveInterfaceTrafficClass -{ - my ($interfaceID,$trafficClassID) = @_; - - - # Check everything exists first - if (!isInterfaceIDValid($interfaceID)) { - return; - } - - if (!isTrafficClassIDValid($trafficClassID)) { - return; - } - - my $class = $globals->{'Interfaces'}->{$interfaceID}->{'TrafficClasses'}->{$trafficClassID}; - - # FIXME -# # If we have applied class overrides, check out what changes there may be -# if (defined(my $appliedClassOverrides = $class->{'.applied_overrides'})) { -# my $classOverrideSet; -# -# # Loop with class overrides in ascending fashion, least matches to most -# foreach my $poid ( sort { $appliedClassOverrides->{$a} <=> $appliedClassOverrides->{$b} } keys %{$appliedClassOverrides}) { -# my $classOverride = $classOverrides->{$poid}; -# -# # Loop with attributes and create our override set -# foreach my $attr (CLASS_OVERRIDE_CHANGESET_ATTRIBUTES) { -# # Set class override set attribute if the class override has defined it -# if (defined($classOverride->{$attr}) && $classOverride->{$attr} ne "") { -# $classOverrideSet->{$attr} = $classOverride->{$attr}; -# } -# } -# } -# -# # Set class overrides on pool -# if (defined($classOverrideSet)) { -# foreach my $attr (keys %{$classOverrideSet}) { -# $class->{$attr} = $classOverrideSet->{$attr}; -# } -# } -# } - - $class->{'InterfaceID'} = $interfaceID; - $class->{'TrafficClassID'} = $trafficClassID; - - return $class; -} - - - # Function to create an interface group sub createInterfaceGroup { diff --git a/opentrafficshaper/plugins/tc/tc.pm b/opentrafficshaper/plugins/tc/tc.pm index 93857ae36576337b418846cf387726c7a3f9a3c3..9e5b6f7780a9653047e79f6450862bcbf8544591 100644 --- a/opentrafficshaper/plugins/tc/tc.pm +++ b/opentrafficshaper/plugins/tc/tc.pm @@ -59,7 +59,10 @@ use opentrafficshaper::plugins::configmanager qw( getInterface getInterfaces getInterfaceDefaultPool - getEffectiveInterfaceTrafficClass + getEffectiveInterfaceTrafficClass2 + isInterfaceTrafficClassValid + setInterfaceTrafficClassShaperState + unsetInterfaceTrafficClassShaperState ); @@ -247,6 +250,62 @@ sub _session_stop } + +# Event handler for changing a class +sub _session_class_change +{ + my ($kernel, $interfaceTrafficClassID) = @_[KERNEL, ARG0, ARG1]; + + + # Grab our effective class + my $effectiveInterfaceTrafficClass = getEffectiveInterfaceTrafficClass2($interfaceTrafficClassID); + + # Grab interface ID + my $interfaceID = $effectiveInterfaceTrafficClass->{'InterfaceID'}; + # Grab interface from config manager + my $interface = getInterface($interfaceID); + + # Grab traffic class ID + my $trafficClassID = $effectiveInterfaceTrafficClass->{'TrafficClassID'}; + + $logger->log(LOG_INFO,"[TC] Processing interface class changes for '%s' traffic class ID '%s'", + $interface->{'Device'}, + $trafficClassID + ); + + # Grab tc interface + my $tcInterface = $globals->{'Interfaces'}->{$interfaceID}; + # Grab interface traffic class + my $interfaceTrafficClass = $tcInterface->{'TrafficClasses'}->{$trafficClassID}; + + # Grab the traffic class + my $majorTcClass = $tcInterface->{'TcClass'}; + my $minorTcClass = $interfaceTrafficClass->{"TcClass"}; + + # Generate changeset + my $changeSet = TC::ChangeSet->new(); + + # If we're a normal class we are treated differently than if we're a main/root class below (interface main speed) + if ($minorTcClass > 1) { + _tc_class_change($changeSet,$interfaceID,$majorTcClass,"",$minorTcClass, + $effectiveInterfaceTrafficClass->{'CIR'}, + $effectiveInterfaceTrafficClass->{'Limit'} + ); + # XXX: This will be the actual interface, we set limit and burst to the same + } else { + _tc_class_change($changeSet,$interfaceID,TC_ROOT_CLASS,"",$minorTcClass,$effectiveInterfaceTrafficClass->{'Limit'}); + } + + # Post changeset + $kernel->post("_tc" => "queue" => $changeSet); + + # Mark as live + unsetInterfaceTrafficClassShaperState($interfaceTrafficClassID,SHAPER_NOTLIVE|SHAPER_PENDING); + setInterfaceTrafficClassShaperState($interfaceTrafficClassID,SHAPER_LIVE); +} + + + # Event handler for adding a pool sub _session_pool_add { @@ -272,8 +331,8 @@ sub _session_pool_add my $changeSet = TC::ChangeSet->new(); # Grab some things we need from the main pool - my $txInterface = getPoolTxInterface($pool->{'ID'}); - my $rxInterface = getPoolRxInterface($pool->{'ID'}); + my $txInterfaceID = getPoolTxInterface($pool->{'ID'}); + my $rxInterfaceID = getPoolRxInterface($pool->{'ID'}); # Grab effective config my $trafficClassID = $effectivePool->{'TrafficClassID'}; @@ -284,28 +343,28 @@ sub _session_pool_add my $trafficPriority = getTrafficClassPriority($effectivePool->{'TrafficClassID'}); # Get the Tx traffic classes TC class - my $tcClass_TxTrafficClass = _getTcClassFromTrafficClassID($txInterface,$trafficClassID); + my $tcClass_TxTrafficClass = _getTcClassFromTrafficClassID($txInterfaceID,$trafficClassID); # Generate our pools Tx TC class - my $tcClass_TxPool = _reserveTcClassByPoolID($txInterface,$pool->{'ID'}); + my $tcClass_TxPool = _reserveMinorTcClassByPoolID($txInterfaceID,$pool->{'ID'}); # Add the main Tx TC class for this pool - _tc_class_add($changeSet,$txInterface,TC_ROOT_CLASS,$tcClass_TxTrafficClass,$tcClass_TxPool,$txCIR, + _tc_class_add($changeSet,$txInterfaceID,TC_ROOT_CLASS,$tcClass_TxTrafficClass,$tcClass_TxPool,$txCIR, $txLimit,$trafficPriority ); # Add Tx TC optimizations - _tc_class_optimize($changeSet,$txInterface,$tcClass_TxPool,$txCIR); + _tc_class_optimize($changeSet,$txInterfaceID,$tcClass_TxPool,$txCIR); # Set Tx TC class setPoolAttribute($pool->{'ID'},'tc.txclass',$tcClass_TxPool); # Get the Rx traffic classes TC class - my $tcClass_RxTrafficClass = _getTcClassFromTrafficClassID($rxInterface,$trafficClassID); + my $tcClass_RxTrafficClass = _getTcClassFromTrafficClassID($rxInterfaceID,$trafficClassID); # Generate our pools Rx TC class - my $tcClass_RxPool = _reserveTcClassByPoolID($rxInterface,$pool->{'ID'}); + my $tcClass_RxPool = _reserveMinorTcClassByPoolID($rxInterfaceID,$pool->{'ID'}); # Add the main Rx TC class for this pool - _tc_class_add($changeSet,$rxInterface,TC_ROOT_CLASS,$tcClass_RxTrafficClass,$tcClass_RxPool,$rxCIR, + _tc_class_add($changeSet,$rxInterfaceID,TC_ROOT_CLASS,$tcClass_RxTrafficClass,$tcClass_RxPool,$rxCIR, $rxLimit,$trafficPriority ); # Add Rx TC optimizations - _tc_class_optimize($changeSet,$rxInterface,$tcClass_RxPool,$rxCIR); + _tc_class_optimize($changeSet,$rxInterfaceID,$tcClass_RxPool,$rxCIR); # Set Rx TC setPoolAttribute($pool->{'ID'},'tc.rxclass',$tcClass_RxPool); @@ -356,8 +415,8 @@ sub _session_pool_remove ); # Grab our interfaces - my $txInterface = getPoolTxInterface($pool->{'ID'}); - my $rxInterface = getPoolRxInterface($pool->{'ID'}); + my $txInterfaceID = getPoolTxInterface($pool->{'ID'}); + my $rxInterfaceID = getPoolRxInterface($pool->{'ID'}); # Grab the traffic class from the pool my $txPoolTcClass = getPoolAttribute($pool->{'ID'},'tc.txclass'); my $rxPoolTcClass = getPoolAttribute($pool->{'ID'},'tc.rxclass'); @@ -365,19 +424,22 @@ sub _session_pool_remove # Grab current class ID my $trafficClassID = getPoolAttribute($pool->{'ID'},'shaper.live.ClassID'); # Grab our minor classes - my $txTrafficClassTcClass = _getTcClassFromTrafficClassID($txInterface,$trafficClassID); - my $rxTrafficClassTcClass = _getTcClassFromTrafficClassID($rxInterface,$trafficClassID); + my $txTrafficClassTcClass = _getTcClassFromTrafficClassID($txInterfaceID,$trafficClassID); + my $rxTrafficClassTcClass = _getTcClassFromTrafficClassID($rxInterfaceID,$trafficClassID); + + my $txInterface = getInterface($txInterfaceID); + my $rxInterface = getInterface($rxInterfaceID); # Clear up the class $changeSet->add([ '/sbin/tc','class','del', - 'dev',$txInterface, + 'dev',$txInterface->{'Device'}, 'parent',"1:$txTrafficClassTcClass", 'classid',"1:$txPoolTcClass", ]); $changeSet->add([ '/sbin/tc','class','del', - 'dev',$rxInterface, + 'dev',$rxInterface->{'Device'}, 'parent',"1:$rxTrafficClassTcClass", 'classid',"1:$rxPoolTcClass", ]); @@ -424,8 +486,8 @@ sub _session_pool_change my $effectivePool = getEffectivePool($pool->{'ID'}); # Grab our interfaces - my $txInterface = getPoolTxInterface($pool->{'ID'}); - my $rxInterface = getPoolRxInterface($pool->{'ID'}); + my $txInterfaceID = getPoolTxInterface($pool->{'ID'}); + my $rxInterfaceID = getPoolRxInterface($pool->{'ID'}); # Grab the traffic class from the pool my $txPoolTcClass = getPoolAttribute($pool->{'ID'},'tc.txclass'); my $rxPoolTcClass = getPoolAttribute($pool->{'ID'},'tc.rxclass'); @@ -439,15 +501,15 @@ sub _session_pool_change my $trafficPriority = getTrafficClassPriority($trafficClassID); # Grab our minor classes - my $txTrafficClassTcClass = _getTcClassFromTrafficClassID($txInterface,$trafficClassID); - my $rxTrafficClassTcClass = _getTcClassFromTrafficClassID($rxInterface,$trafficClassID); + my $txTrafficClassTcClass = _getTcClassFromTrafficClassID($txInterfaceID,$trafficClassID); + my $rxTrafficClassTcClass = _getTcClassFromTrafficClassID($rxInterfaceID,$trafficClassID); # Generate changeset my $changeSet = TC::ChangeSet->new(); - _tc_class_change($changeSet,$txInterface,TC_ROOT_CLASS,$txTrafficClassTcClass,$txPoolTcClass,$txCIR, + _tc_class_change($changeSet,$txInterfaceID,TC_ROOT_CLASS,$txTrafficClassTcClass,$txPoolTcClass,$txCIR, $txLimit,$trafficPriority); - _tc_class_change($changeSet,$rxInterface,TC_ROOT_CLASS,$rxTrafficClassTcClass,$rxPoolTcClass,$rxCIR, + _tc_class_change($changeSet,$rxInterfaceID,TC_ROOT_CLASS,$rxTrafficClassTcClass,$rxPoolTcClass,$rxCIR, $rxLimit,$trafficPriority); # Post changeset @@ -501,47 +563,47 @@ sub _session_poolmember_add } # Grab some variables we going to need below - my $txInterface = getPoolTxInterface($pool->{'ID'}); - my $rxInterface = getPoolRxInterface($pool->{'ID'}); + my $txInterfaceID = getPoolTxInterface($pool->{'ID'}); + my $rxInterfaceID = getPoolRxInterface($pool->{'ID'}); my $trafficPriority = getTrafficClassPriority($pool->{'TrafficClassID'}); my $matchPriority = getPoolMemberMatchPriority($poolMember->{'ID'}); # Check if we have a entry for the /8, if not we must create our 2nd level hash table and link it - if (!defined($globals->{'TcFilterMappings'}->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1})) { + if (!defined($globals->{'TcFilterMappings'}->{$txInterfaceID}->{'dst'}->{$matchPriority}->{$ip1})) { # Grab filter ID's for 2nd level - my $filterID = _reserveTcFilter($txInterface,$matchPriority,$pool->{'ID'}); + my $filterID = _reserveTcFilter($txInterfaceID,$matchPriority,$pool->{'ID'}); # Track our mapping - $globals->{'TcFilterMappings'}->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{'id'} = $filterID; + $globals->{'TcFilterMappings'}->{$txInterfaceID}->{'dst'}->{$matchPriority}->{$ip1}->{'id'} = $filterID; $logger->log(LOG_DEBUG,"[TC] Linking 2nd level TX hash table to '%s' to '%s.0.0.0/8', priority '%s'", $filterID, $ip1, $matchPriority ); - _tc_filter_add_dstlink($changeSet,$txInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},800,"", + _tc_filter_add_dstlink($changeSet,$txInterfaceID,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},800,"", "$ip1.0.0.0/8","00ff0000"); } - if (!defined($globals->{'TcFilterMappings'}->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1})) { + if (!defined($globals->{'TcFilterMappings'}->{$rxInterfaceID}->{'src'}->{$matchPriority}->{$ip1})) { # Grab filter ID's for 2nd level - my $filterID = _reserveTcFilter($rxInterface,$matchPriority,$pool->{'ID'}); + my $filterID = _reserveTcFilter($rxInterfaceID,$matchPriority,$pool->{'ID'}); # Track our mapping - $globals->{'TcFilterMappings'}->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{'id'} = $filterID; + $globals->{'TcFilterMappings'}->{$rxInterfaceID}->{'src'}->{$matchPriority}->{$ip1}->{'id'} = $filterID; $logger->log(LOG_DEBUG,"[TC] Linking 2nd level RX hash table to '%s' to '%s.0.0.0/8', priority '%s'", $filterID, $ip1, $matchPriority ); - _tc_filter_add_srclink($changeSet,$rxInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},800,"", + _tc_filter_add_srclink($changeSet,$rxInterfaceID,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},800,"", "$ip1.0.0.0/8","00ff0000"); } # Check if we have our /16 hash entry, if not we must create the 3rd level hash table - if (!defined($globals->{'TcFilterMappings'}->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2})) { + if (!defined($globals->{'TcFilterMappings'}->{$txInterfaceID}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2})) { # Grab filter ID's for 3rd level - my $filterID = _reserveTcFilter($txInterface,$matchPriority,$pool->{'ID'}); + my $filterID = _reserveTcFilter($txInterfaceID,$matchPriority,$pool->{'ID'}); # Track our mapping - $globals->{'TcFilterMappings'}->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'} = $filterID; + $globals->{'TcFilterMappings'}->{$txInterfaceID}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'} = $filterID; # Grab some hash table ID's we need - my $ip1HtHex = $globals->{'TcFilterMappings'}->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{'id'}; + my $ip1HtHex = $globals->{'TcFilterMappings'}->{$txInterfaceID}->{'dst'}->{$matchPriority}->{$ip1}->{'id'}; # And hex our IP component my $ip2Hex = toHex($ip2); $logger->log(LOG_DEBUG,"[TC] Linking 3rd level TX hash table to '%s' to '%s.%s.0.0/16', priority '%s'", @@ -550,16 +612,16 @@ sub _session_poolmember_add $ip2, $matchPriority ); - _tc_filter_add_dstlink($changeSet,$txInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip1HtHex, + _tc_filter_add_dstlink($changeSet,$txInterfaceID,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip1HtHex, $ip2Hex,"$ip1.$ip2.0.0/16","0000ff00"); } - if (!defined($globals->{'TcFilterMappings'}->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2})) { + if (!defined($globals->{'TcFilterMappings'}->{$rxInterfaceID}->{'src'}->{$matchPriority}->{$ip1}->{$ip2})) { # Grab filter ID's for 3rd level - my $filterID = _reserveTcFilter($rxInterface,$matchPriority,$pool->{'ID'}); + my $filterID = _reserveTcFilter($rxInterfaceID,$matchPriority,$pool->{'ID'}); # Track our mapping - $globals->{'TcFilterMappings'}->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'} = $filterID; + $globals->{'TcFilterMappings'}->{$rxInterfaceID}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'} = $filterID; # Grab some hash table ID's we need - my $ip1HtHex = $globals->{'TcFilterMappings'}->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{'id'}; + my $ip1HtHex = $globals->{'TcFilterMappings'}->{$rxInterfaceID}->{'src'}->{$matchPriority}->{$ip1}->{'id'}; # And hex our IP component my $ip2Hex = toHex($ip2); $logger->log(LOG_DEBUG,"[TC] Linking 3rd level RX hash table to '%s' to '%s.%s.0.0/16', priority '%s'", @@ -568,18 +630,18 @@ sub _session_poolmember_add $ip2, $matchPriority ); - _tc_filter_add_srclink($changeSet,$rxInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip1HtHex, + _tc_filter_add_srclink($changeSet,$rxInterfaceID,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip1HtHex, $ip2Hex,"$ip1.$ip2.0.0/16","0000ff00"); } # Check if we have our /24 hash entry, if not we must create the 4th level hash table - if (!defined($globals->{'TcFilterMappings'}->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3})) { + if (!defined($globals->{'TcFilterMappings'}->{$txInterfaceID}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3})) { # Grab filter ID's for 4th level - my $filterID = _reserveTcFilter($txInterface,$matchPriority,$pool->{'ID'}); + my $filterID = _reserveTcFilter($txInterfaceID,$matchPriority,$pool->{'ID'}); # Track our mapping - $globals->{'TcFilterMappings'}->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'} = $filterID; + $globals->{'TcFilterMappings'}->{$txInterfaceID}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'} = $filterID; # Grab some hash table ID's we need - my $ip2HtHex = $globals->{'TcFilterMappings'}->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'}; + my $ip2HtHex = $globals->{'TcFilterMappings'}->{$txInterfaceID}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'}; # And hex our IP component my $ip3Hex = toHex($ip3); $logger->log(LOG_DEBUG,"[TC] Linking 4th level TX hash table to '%s' to '%s.%s.%s.0/24', priority '%s'", @@ -589,16 +651,16 @@ sub _session_poolmember_add $ip3, $matchPriority ); - _tc_filter_add_dstlink($changeSet,$txInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip2HtHex, + _tc_filter_add_dstlink($changeSet,$txInterfaceID,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip2HtHex, $ip3Hex,"$ip1.$ip2.$ip3.0/24","000000ff"); } - if (!defined($globals->{'TcFilterMappings'}->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3})) { + if (!defined($globals->{'TcFilterMappings'}->{$rxInterfaceID}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3})) { # Grab filter ID's for 4th level - my $filterID = _reserveTcFilter($rxInterface,$matchPriority,$pool->{'ID'}); + my $filterID = _reserveTcFilter($rxInterfaceID,$matchPriority,$pool->{'ID'}); # Track our mapping - $globals->{'TcFilterMappings'}->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'} = $filterID; + $globals->{'TcFilterMappings'}->{$rxInterfaceID}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'} = $filterID; # Grab some hash table ID's we need - my $ip2HtHex = $globals->{'TcFilterMappings'}->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'}; + my $ip2HtHex = $globals->{'TcFilterMappings'}->{$rxInterfaceID}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'}; # And hex our IP component my $ip3Hex = toHex($ip3); $logger->log(LOG_DEBUG,"[TC] Linking 4th level RX hash table to '%s' to '%s.%s.%s.0/24', priority '%s'", @@ -608,7 +670,7 @@ sub _session_poolmember_add $ip3, $matchPriority ); - _tc_filter_add_srclink($changeSet,$rxInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip2HtHex, + _tc_filter_add_srclink($changeSet,$rxInterfaceID,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip2HtHex, $ip3Hex,"$ip1.$ip2.$ip3.0/24","000000ff"); } @@ -621,7 +683,7 @@ sub _session_poolmember_add # Get the TX class my $tcClass_trafficClass = getPoolAttribute($pool->{'ID'},'tc.txclass'); # Grab some hash table ID's we need - my $ip3HtHex = $globals->{'TcFilterMappings'}->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'}; + my $ip3HtHex = $globals->{'TcFilterMappings'}->{$txInterfaceID}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'}; # And hex our IP component my $ip4Hex = toHex($ip4); $logger->log(LOG_DEBUG,"[TC] Linking pool member IP '%s' to class '%s' at hash endpoint '%s:%s'", @@ -632,8 +694,8 @@ sub _session_poolmember_add ); # Link filter to traffic flow (class) - _tc_filter_add_flowlink($changeSet,$txInterface,TC_ROOT_CLASS,$trafficPriority,$config->{'ip_protocol'},$ip3HtHex,$ip4Hex, - "dst",16,$poolMember->{'IPAddress'},$tcClass_trafficClass); + _tc_filter_add_flowlink($changeSet,$txInterfaceID,TC_ROOT_CLASS,$trafficPriority,$config->{'ip_protocol'},$ip3HtHex, + $ip4Hex,"dst",16,$poolMember->{'IPAddress'},$tcClass_trafficClass); # Save pool member filter ID setPoolMemberAttribute($poolMember->{'ID'},'tc.txfilter',"${ip3HtHex}:${ip4Hex}:1"); @@ -643,7 +705,7 @@ sub _session_poolmember_add # Generate our limit TC class my $tcClass_trafficClass = getPoolAttribute($pool->{'ID'},'tc.rxclass'); # Grab some hash table ID's we need - my $ip3HtHex = $globals->{'TcFilterMappings'}->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'}; + my $ip3HtHex = $globals->{'TcFilterMappings'}->{$rxInterfaceID}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'}; # And hex our IP component my $ip4Hex = toHex($ip4); $logger->log(LOG_DEBUG,"[TC] Linking RX IP '%s' to class '%s' at hash endpoint '%s:%s'", @@ -654,8 +716,8 @@ sub _session_poolmember_add ); # Link filter to traffic flow (class) - _tc_filter_add_flowlink($changeSet,$rxInterface,TC_ROOT_CLASS,$trafficPriority,$config->{'ip_protocol'},$ip3HtHex,$ip4Hex, - "src",12,$poolMember->{'IPAddress'},$tcClass_trafficClass); + _tc_filter_add_flowlink($changeSet,$rxInterfaceID,TC_ROOT_CLASS,$trafficPriority,$config->{'ip_protocol'},$ip3HtHex, + $ip4Hex,"src",12,$poolMember->{'IPAddress'},$tcClass_trafficClass); # Save pool member filter ID setPoolMemberAttribute($poolMember->{'ID'},'tc.rxfilter',"${ip3HtHex}:${ip4Hex}:1"); @@ -706,8 +768,8 @@ sub _session_poolmember_remove ); # Grab our interfaces - my $txInterface = getPoolTxInterface($pool->{'ID'}); - my $rxInterface = getPoolRxInterface($pool->{'ID'}); + my $txInterfaceID = getPoolTxInterface($pool->{'ID'}); + my $rxInterfaceID = getPoolRxInterface($pool->{'ID'}); # Grab the filter ID's from the pool member which is linked to the traffic class my $txFilter = getPoolMemberAttribute($poolMember->{'ID'},'tc.txfilter'); my $rxFilter = getPoolMemberAttribute($poolMember->{'ID'},'tc.rxfilter'); @@ -716,13 +778,15 @@ sub _session_poolmember_remove my $trafficClassID = getPoolAttribute($pool->{'ID'},'shaper.live.ClassID'); my $trafficPriority = getTrafficClassPriority($trafficClassID); + my $txInterface = getInterface($txInterfaceID); + my $rxInterface = getInterface($rxInterfaceID); my $changeSet = TC::ChangeSet->new(); # Clear up the filter $changeSet->add([ '/sbin/tc','filter','del', - 'dev',$txInterface, + 'dev',$txInterface->{'Device'}, 'parent','1:', 'prio',$trafficPriority, 'handle',$txFilter, @@ -731,7 +795,7 @@ sub _session_poolmember_remove ]); $changeSet->add([ '/sbin/tc','filter','del', - 'dev',$rxInterface, + 'dev',$rxInterface->{'Device'}, 'parent','1:', 'prio',$trafficPriority, 'handle',$rxFilter, @@ -819,6 +883,7 @@ sub _tc_iface_init # Grab our interface rate my $interface = getInterface($interfaceID); +### --- Interface Setup # Clear the qdisc from the interface $changeSet->add([ @@ -828,13 +893,20 @@ sub _tc_iface_init ]); # Initialize the major TC class - my $interfaceRootTcClas = _reserveMajorTcClass($interfaceID,"root"); + my $interfaceTcClass = _reserveMajorTcClass($interfaceID,"root"); + + # Set interface RootClass + $globals->{'Interfaces'}->{$interfaceID} = { + 'TcClass' => $interfaceTcClass + }; + +### --- Interface Traffic Class Setup # Reserve our parent TC classes my @trafficClasses = getAllTrafficClasses(); foreach my $trafficClassID (sort {$a <=> $b} @trafficClasses) { - # We don't really need the result, we just need the class created - _reserveTcClassByTrafficClassID($interfaceID,$trafficClassID); + # Record the class we get for this interface traffic class ID + my $interfaceTrafficClassTcClass = _reserveMinorTcClassByTrafficClassID($interfaceID,$trafficClassID); } # Do we have a default pool? if so we must direct traffic there @@ -847,6 +919,9 @@ sub _tc_iface_init push(@qdiscOpts,'default',$defaultPoolTcClass); } + +### --- Interface Setup Part 2 + # Add root qdisc $changeSet->add([ '/sbin/tc','qdisc','add', @@ -868,10 +943,20 @@ sub _tc_iface_init 'burst',"$interface->{'Limit'}kb", ]); + # Class 0 is our interface, it points to 1 (the major TcClass)) : 1 (class below) + $globals->{'Interfaces'}->{$interfaceID}->{'TrafficClasses'}->{'0'} = { + 'TcClass' => '1', + 'CIR' => $interface->{'Limit'}, + 'Limit' => $interface->{'Limit'} + }; + + +### --- Setup each class # Setup the classes foreach my $trafficClassID (@trafficClasses) { - my $interfaceTrafficClass = getEffectiveInterfaceTrafficClass($interfaceID,$trafficClassID); + my $interfaceTrafficClassID = isInterfaceTrafficClassValid($interfaceID,$trafficClassID); + my $interfaceTrafficClass = getEffectiveInterfaceTrafficClass2($interfaceTrafficClassID); my $tcClass = _getTcClassFromTrafficClassID($interfaceID,$trafficClassID); my $trafficPriority = getTrafficClassPriority($trafficClassID); @@ -887,11 +972,19 @@ sub _tc_iface_init 'prio',$trafficPriority, 'burst', "$interfaceTrafficClass->{'Limit'}kb", ]); + + # Setup interface traffic class details + $globals->{'Interfaces'}->{$interfaceID}->{'TrafficClasses'}->{$trafficClassID} = { + 'TcClass' => $tcClass, + 'CIR' => $interfaceTrafficClass->{'CIR'}, + 'Limit' => $interfaceTrafficClass->{'Limit'} + }; } # Process our default pool traffic optimizations if (defined($defaultPool)) { - my $interfaceTrafficClass = getEffectiveInterfaceTrafficClass($interfaceID,$defaultPool); + my $interfaceTrafficClassID = isInterfaceTrafficClassValid($interfaceID,$defaultPool); + my $interfaceTrafficClass = getEffectiveInterfaceTrafficClass2($interfaceTrafficClassID); # If we have a rate for this iface, then use it @@ -1473,8 +1566,21 @@ sub _tc_class_change my $interface = getInterface($interfaceID); - # Set burst to a sane value - my $burst = int($ceil / 8 / 5); + my @args = (); + + # Based on if ceil is avaiable, set burst + my $burst; + if (defined($ceil)) { + $burst = int($ceil / 8 / 5); + } else { + # If ceil is not available, set burst and ceil + $burst = $ceil = $rate; + } + + # Check if we have a priority + if (defined($trafficPriority)) { + push(@args,'prio',$trafficPriority); + } # Create main rate limiting classes $changeSet->add([ @@ -1485,15 +1591,15 @@ sub _tc_class_change 'htb', 'rate', "${rate}kbit", 'ceil', "${ceil}kbit", - 'prio', $trafficPriority, 'burst', "${burst}kb", + @args ]); } # Get a pool TC class from pool ID -sub _reserveTcClassByPoolID +sub _reserveMinorTcClassByPoolID { my ($interfaceID,$pid) = @_; @@ -1503,7 +1609,7 @@ sub _reserveTcClassByPoolID # Get a traffic class TC class -sub _reserveTcClassByTrafficClassID +sub _reserveMinorTcClassByTrafficClassID { my ($interfaceID,$trafficClassID) = @_; diff --git a/opentrafficshaper/plugins/webserver/pages/configmanager.pm b/opentrafficshaper/plugins/webserver/pages/configmanager.pm index 138ad11072737e4aee9cf19083744018eaa61acc..6b2ef0bc12397582f160cb3734a114894f5c5672 100644 --- a/opentrafficshaper/plugins/webserver/pages/configmanager.pm +++ b/opentrafficshaper/plugins/webserver/pages/configmanager.pm @@ -49,15 +49,16 @@ use opentrafficshaper::logger; use opentrafficshaper::plugins; use opentrafficshaper::plugins::configmanager qw( getInterfaces - getInterfaceTrafficClass getInterface getTrafficClass getAllTrafficClasses + getInterfaceTrafficClass + changeInterfaceTrafficClass + isInterfaceIDValid - changeTrafficClass isTrafficClassIDValid ); @@ -91,45 +92,27 @@ sub default } + # Admin configuration sub admin_config { my ($kernel,$globals,$client_session_id,$request) = @_; - # Items for our form... - my @formElements = qw( - FriendlyName - Identifier - ClassID - TrafficLimitTx TrafficLimitTxBurst - TrafficLimitRx TrafficLimitRxBurst - Notes - ); - # Grab stuff we need - my $interfaces = getInterfaces(); + my @interfaces = getInterfaces(); + + # Errors to display above the form + my @errors; # Build content my $content = ""; - # Header + # Form header $content .=<<EOF; - <!-- Config Tabs --> - <ul class="nav nav-tabs" id="configTabs"> - <li class="active"><a href="#interfaces" data-toggle="tab">Interfaces</a></li> - <li><a href="#system" data-toggle="tab">System (TODO)</a></li> - </ul> - <!-- Tab panes --> - <div class="tab-content"> - <div class="tab-pane active" id="interfaces"> + <legend>Interface Rate Setup</legend> EOF - - - # Title of the form, by default its an add form - my $formType = "Add"; - my $formNoEdit = ""; # Form data my $formData; @@ -138,11 +121,87 @@ EOF if ($request->method eq "POST") { # Parse form data my $form = parseFormContent($request->content); - use Data::Dumper; warn "CONTENTDATA: ".Dumper($request->content); - use Data::Dumper; warn "FORMDATA: ".Dumper($form); + + # Loop with rate changes + my $rateChanges = { }; + foreach my $elementName (keys %{$form}) { + my $rateChange = $form->{$elementName}; + + # Skip over blanks + if ($rateChange->{'value'} =~ /^\s*$/) { + next; + } + # Split off the various components of the element name + my ($item,$interfaceID,$trafficClassID) = ($elementName =~ /^((?:CIR|Limit))\[([a-z0-9:.]+)\]\[([0-9]+)\]$/); + # Make sure everything is defined + if (!defined($item) || !defined($interfaceID) || !defined($trafficClassID)) { + push(@errors,"Invalid data received"); + last; + } + + # Check interface exists + if (!defined($interfaceID = isInterfaceIDValid($interfaceID))) { + push(@errors,"Invalid data received, interface ID is invalid"); + last; + } + # Check class is valid + if ( + !defined($trafficClassID = isNumber($trafficClassID,ISNUMBER_ALLOW_ZERO)) || + ($trafficClassID && !isTrafficClassIDValid($trafficClassID)) + ) { + push(@errors,"Invalid class ID received for interface [$interfaceID]"); + last; + } + # Check value is valid + if (!defined($rateChange->{'value'} = isNumber($rateChange->{'value'}))) { + push(@errors,"Invalid value received for interface [$interfaceID], class [$trafficClassID]"); + last; + } + + $rateChanges->{$interfaceID}->{$trafficClassID}->{$item} = $rateChange->{'value'}; + } +# FIXME - check speed does not exceed inteface speed + # Check if there are no errors + if (!@errors) { + # Loop with interfaces + foreach my $interfaceID (keys %{$rateChanges}) { + my $trafficClasses = $rateChanges->{$interfaceID}; + + # Loop with traffic classes + foreach my $trafficClassID (keys %{$trafficClasses}) { + my $trafficClass = $trafficClasses->{$trafficClassID}; + + # Set some additional items we need + $trafficClass->{'InterfaceID'} = $interfaceID; + $trafficClass->{'TrafficClassID'} = $trafficClassID; + # Push changes + changeInterfaceTrafficClass($trafficClass); + } + } + + return (HTTP_TEMPORARY_REDIRECT,"/configmanager"); + } } + # Header + $content .=<<EOF; + <!-- Config Tabs --> + <ul class="nav nav-tabs" id="configTabs"> + <li class="active"><a href="#interfaces" data-toggle="tab">Interfaces</a></li> + </ul> + <!-- Tab panes --> + <div class="tab-content"> + <div class="tab-pane active" id="interfaces"> +EOF + + # Spit out errors if we have any + if (@errors > 0) { + foreach my $error (@errors) { + $content .= '<div class="alert alert-danger">'.encode_entities($error).'</div>'; + } + } + # Interfaces tab setup $content .=<<EOF; <br /> @@ -150,14 +209,21 @@ EOF <ul class="nav nav-tabs" id="configInterfaceTabs"> EOF my $firstPaneActive = " active"; - foreach my $interface (@{$interfaces}) { + foreach my $interfaceID (@interfaces) { + my $interface = getInterface($interfaceID); + my $encodedInterfaceID = encode_entities($interfaceID); + my $encodedInterfaceName = encode_entities($interface->{'Name'}); + + $content .=<<EOF; - <li class="$firstPaneActive"><a href="#interface$interface" data-toggle="tab">Interface: $interface</a></li> + <li class="$firstPaneActive"> + <a href="#interface$encodedInterfaceID" data-toggle="tab"> + Interface: $encodedInterfaceName + </a> + </li> EOF # No longer the first pane $firstPaneActive = ""; - - $formData->{"MainTrafficLimitTx[$interface]"} = getInterfaceRate($interface); } $content .=<<EOF; </ul> @@ -167,17 +233,26 @@ EOF # Suck in list of interfaces $firstPaneActive = " active"; - foreach my $interface (@{$interfaces}) { + foreach my $interfaceID (@interfaces) { + my $interface = getInterface($interfaceID); + my $encodedInterfaceID = encode_entities($interfaceID); + my $encodedInterfaceName = encode_entities($interface->{'Name'}); + my $encodedInterfaceLimit = encode_entities($interface->{'Limit'}); + + # Interface tab $content .=<<EOF; - <div class="tab-pane$firstPaneActive" id="interface$interface"> + <div class="tab-pane$firstPaneActive" id="interface$encodedInterfaceID"> EOF # No longer the first pane $firstPaneActive = ""; # Sanitize params if we need to - foreach my $item (@formElements) { - $formData->{$item} = defined($formData->{$item}) ? encode_entities($formData->{$item}) : ""; + if (defined($formData->{"Limit[$encodedInterfaceID][0]"})) { + $formData->{"Limit[$encodedInterfaceID][0]"} = + encode_entities($formData->{"Limit[$encodedInterfaceID][0]"}); + } else { + $formData->{"Limit[$encodedInterfaceID][0]"} = ""; } @@ -192,52 +267,70 @@ EOF # $content .=<<EOF; <br /> - <legend>Main: $interface</legend> + <legend>Main: $encodedInterfaceName</legend> <div class="form-group"> - <label for="TrafficLimitTx" class="col-md-1 control-label">CIR</label> + <label for="Limit" class="col-md-1 control-label">Speed</label> <div class="row"> <div class="col-md-3"> <div class="input-group"> - <input name="MainTrafficLimitTx[$interface]" type="text" placeholder="CIR" class="form-control" value="$formData->{"MainTrafficLimitTx[$interface]"}" /> + <input name="Limit[$encodedInterfaceID][0]" type="text" + placeholder="$encodedInterfaceLimit" class="form-control" + value="$formData->{"Limit[$encodedInterfaceID][0]"}" /> <span class="input-group-addon">Kbps *<span> </div> </div> - - <label for="TrafficLimitTxBurst" class="col-md-1 control-label">Limit</label> - <div class="col-md-3"> - <div class="input-group"> - <input name="MainTrafficLimitTxBurst[$interface]" type="text" placeholder="Limit" class="form-control" value="$formData->{'TrafficLimitTxBurst'}" /> - <span class="input-group-addon">Kbps<span> - </div> - </div> </div> </div> EOF - my $classes = getInterfaceTrafficClasses($interface); - foreach my $class (sort { $a <=> $b } keys %{$classes}) { - my $className = getTrafficClassName($class); - my $classNameStr = encode_entities($className); + # Grab classes and loop + my @trafficClasses = getAllTrafficClasses(); + foreach my $trafficClassID (sort { $a <=> $b } @trafficClasses) { + my $trafficClass = getTrafficClass($trafficClassID); + my $encodedTrafficClassID = encode_entities($trafficClassID); + my $encodedTrafficClassName = encode_entities($trafficClass->{'Name'}); + my $interfaceTrafficClass = getInterfaceTrafficClass($interfaceID,$trafficClassID); + my $encodedInterfaceTrafficClassCIR = encode_entities($interfaceTrafficClass->{'CIR'}); + my $encodedInterfaceTrafficClassLimit = encode_entities($interfaceTrafficClass->{'Limit'}); + + + # Sanitize params if we need to + if (defined($formData->{"CIR[$encodedInterfaceID][$encodedTrafficClassID]"})) { + $formData->{"CIR[$encodedInterfaceID][$encodedTrafficClassID]"} = + encode_entities($formData->{"CIR[$encodedInterfaceID][$encodedTrafficClassID]"}); + } else { + $formData->{"CIR[$encodedInterfaceID][$encodedTrafficClassID]"} = ""; + } + if (defined($formData->{"Limit[$encodedInterfaceID][$encodedTrafficClassID]"})) { + $formData->{"Limit[$encodedInterfaceID][$encodedTrafficClassID]"} = + encode_entities($formData->{"Limit[$encodedInterfaceID][$encodedTrafficClassID]"}); + } else { + $formData->{"Limit[$encodedInterfaceID][$encodedTrafficClassID]"} = ""; + } # # Page content # $content .=<<EOF; - <legend>Class: $interface - $classNameStr</legend> + <legend>Class: $encodedInterfaceName - $encodedTrafficClassName</legend> <div class="form-group"> - <label for="TrafficLimitTx" class="col-md-1 control-label">CIR</label> + <label for="TxCIR" class="col-md-1 control-label">CIR</label> <div class="row"> <div class="col-md-3"> <div class="input-group"> - <input name="ClassTrafficLimitTx[$interface] x y [$class]" type="text" placeholder="CIR" class="form-control" value="$formData->{'TrafficLimitTx'}" /> + <input name="CIR[$encodedInterfaceID][$encodedTrafficClassID]" type="text" + placeholder="$encodedInterfaceTrafficClassCIR" class="form-control" + value="$formData->{"CIR[$encodedInterfaceID][$encodedTrafficClassID]"}" /> <span class="input-group-addon">Kbps *<span> </div> </div> - <label for="TrafficLimitTxBurst" class="col-md-1 control-label">Limit</label> + <label for="TxLimit" class="col-md-1 control-label">Limit</label> <div class="col-md-3"> <div class="input-group"> - <input name="ClassTrafficLimitTxBurst[$interface][$class]" type="text" placeholder="Limit" class="form-control" value="$formData->{'TrafficLimitTxBurst'}" /> + <input name="Limit[$encodedInterfaceID][$encodedTrafficClassID]" type="text" + placeholder="$encodedInterfaceTrafficClassLimit" class="form-control" + value="$formData->{"Limit[$encodedInterfaceID][$encodedTrafficClassID]"}" /> <span class="input-group-addon">Kbps<span> </div> </div>