Ene 192013
 

In this article we will see how to limit the bandwidth of DomUs in XEN 4.1 installed on a debian squeeze.
Clarification should be configured so that the DomU vif, vifname and mac must have set, the name parameter of the VPS and vifname are equal, we should also add a line with variable configuration bandwith_limit, XEN does not recognize and will ignore this parameter but will be used by our script. We must also ensure that the packages are installed mawk, ethtool and iproute.

We can see a sample configuration file.

root@x49:/# cat /etc/xen/x50.cfg
#
# Configuration file for the Xen instance x50, created
# by xen-tools 4.3.1 on Thu Jan 17 01:33:06 2013.
#
#
#  Kernel + memory size
#
kernel      = '/boot/vmlinuz-3.2.0-0.bpo.4-amd64'
ramdisk     = '/boot/initrd.img-3.2.0-0.bpo.4-amd64'

vcpus       = '1'
memory      = '1024'

#
#  Disk device(s).
#
root        = '/dev/xvda2 ro'
disk        = [
                  'file:/var/lib/xen/DomUs/domains/x50/root.img,xvda2,w',
                  'file:/var/lib/xen/DomUs/domains/x50/swap.img,xvda1,w',
              ]

#
#  Physical volumes
#

#
#  Hostname
#
name        = 'x50'

#
#  Networking
#
vif         = [ 'ip=192.168.252.50 ,mac=00:16:C0:A8:FC:32,vifname=x50' ]

#
#  Behaviour
#
on_poweroff = 'destroy'
on_reboot   = 'restart'
on_crash    = 'restart'

extra = 'elevator=deadline console=hvc0 noht loglevel=0'

bandwith_limit = '4MBit'
root@x49:/#

We can see where the lines are highlighted variable name, vifname, mac and bandwith_limit

Then create the file /usr/local/xen/bandwith-limit.sh with the following content

root@x49:/# cat /usr/local/xen/bandwith-limit.sh
#!/bin/bash

TC=/sbin/tc
IPTABLES=/sbin/iptables
ECHO=/bin/echo
CAT=/bin/cat
CUT=/usr/bin/cut
SED=/bin/sed
TR=/usr/bin/tr
WC=/usr/bin/wc
GREP=/bin/grep
IFCONFIG=/sbin/ifconfig
MAWK=/usr/bin/mawk
SORT=/usr/bin/sort
XEN=/usr/sbin/xen
ETHTOOL=/sbin/ethtool
XENSTORE_READ=/usr/sbin/xenstore-read

#############################################################################################################################################################################################################
function set_bridge_limit_up
{
    local ETHS=`${IFCONFIG} | ${GREP} encap:Ethernet | ${MAWK} '{print $1;}' | ${GREP} eth`

    for eth_data in ${ETHS}
    do
        if [ ${eth_data} = ${ETH_BR} ]
        then
            local NUM_TC_RULES=`${TC} class show dev ${eth_data} | ${WC} -l`
            if [ "${NUM_TC_RULES}" = "0" ]
            then
                limit_device ${eth_data}
            fi
        else
            local NUM_TC_RULES=`${TC} qdisc show dev ${eth_data} | ${GREP} rate | ${GREP} burst | ${GREP} peakrate | ${WC} -l`
            if [ "${NUM_TC_RULES}" = "0" ]
            then
                limit_device ${eth_data}
            fi
        fi
    done
}

function limit_device()
{
    local DEV_IN=$1

    local ETH_SUB3=`${ECHO} ${DEV_IN} | ${CUT} -b 1-3`

    if [ "${ETH_SUB3}" = "eth" ]
    then
        local CEIL_LIMIT_VALUE=`${ETHTOOL} ${DEV_IN} | ${GREP} -i speed: | ${MAWK} -F ":" '{print $2;}' | ${MAWK} -F "/" '{print $1;}' | ${TR} -d " " | ${TR} -d "a-zA-Z" `
        local RATE_LIMIT_VALUE=$[ ${CEIL_LIMIT_VALUE} * 95 / 100 ]
        local RATE_LIMIT_UNIT=`${ETHTOOL} ${DEV_IN} | ${GREP} -i speed: | ${MAWK} -F ":" '{print $2;}' | ${MAWK} -F "/" '{print $1;}' | ${TR} -d " " | ${TR} -d "0-9" | ${TR} '[A-Z]' '[a-z]' `

        if [ ${RATE_LIMIT_UNIT} = "mb" ]
        then
            RATE_LIMIT_UNIT=mbit
        elif [ ${RATE_LIMIT_UNIT} = "kb" ]
        then
            RATE_LIMIT_UNIT=kbit
        fi

        local RATE_IN=${RATE_LIMIT_VALUE}${RATE_LIMIT_UNIT}
        local CEIL_IN=${CEIL_LIMIT_VALUE}${RATE_LIMIT_UNIT}

        if [ -z "${RATE_IN}" ]
        then
            RATE_IN=95mbit
            CEIL_IN=100mbit
        fi

        if [ "${DEV_IN}" = "${ETH_BR}" ]
        then
            ${TC} qdisc add dev ${DEV_IN} root handle 1: htb 1>/dev/null 2>>${FILE_LOG_ERROR}
            ${TC} class add dev ${DEV_IN} parent 1: classid 1:1 htb rate ${RATE_IN} ceil ${CEIL_IN} 1>/dev/null 2>>${FILE_LOG_ERROR}
        else
            ${TC} qdisc add dev ${DEV_IN} root tbf rate ${RATE_IN} burst 64kb latency 25ms mpu 64 peakrate ${CEIL_IN} mtu 64kb 1>/dev/null 2>>${FILE_LOG_ERROR}
        fi
    else
        local DOMID=`${XEN} domid ${DEV_IN}`
        local MAC_IN=`${XENSTORE_READ} /local/domain/${DOMID}/device/vif/0/mac`
        local CLASS_TR=$[ (${DOMID} + 1007) % 10000 ]
        local RATE_LIMIT_VALUE=`${CAT} ${DIR_CONFIG}/${DEV_IN}.cfg | ${GREP} bandwith_limit | ${MAWK} -F "=" '{print $2;}' | ${TR} -d " '\"" | ${TR} -d "a-zA-Z" `
        local RATE_LIMIT_UNIT=`${CAT} ${DIR_CONFIG}/${DEV_IN}.cfg | ${GREP} bandwith_limit | ${MAWK} -F "=" '{print $2;}' | ${TR} -d " '\"" | ${TR} -d "0-9" `
        if [ ${RATE_LIMIT_UNIT} = "mb" ]
        then
            RATE_LIMIT_UNIT=mbit
        elif [ ${RATE_LIMIT_UNIT} = "kb" ]
        then
            RATE_LIMIT_UNIT=kbit
        fi

        local CEIL_LIMIT_VALUE=$[ ${RATE_LIMIT_VALUE} * 110 / 100 ]
        if [ "${CEIL_LIMIT_VALUE} = ${RATE_LIMIT_VALUE}" ]
        then
            CEIL_LIMIT_VALUE=$[ ${CEIL_LIMIT_VALUE} + 1 ]
        fi

        local RATE_IN=${RATE_LIMIT_VALUE}${RATE_LIMIT_UNIT}
        local CEIL_IN=${CEIL_LIMIT_VALUE}${RATE_LIMIT_UNIT}

        if [ -z "${RATE_IN}" ]
        then
            RATE_IN=10mbit
            CEIL_IN=12mbit
        fi

        set_bridge_limit_up

        ${TC} qdisc add dev ${DEV_IN} root tbf rate ${RATE_IN} burst 64kb latency 25ms mpu 64 peakrate ${CEIL_IN} mtu 64kb 1>/dev/null 2>>${FILE_LOG_ERROR}
        ${TC} class add dev ${ETH_BR} parent 1:1 classid 1:${CLASS_TR} htb rate ${RATE_IN} ceil ${CEIL_IN} prio 4 1>/dev/null 2>>${FILE_LOG_ERROR}
        ${TC} qdisc add dev ${ETH_BR} parent 1:${CLASS_TR} handle ${CLASS_TR}: sfq perturb 10 1>/dev/null 2>>${FILE_LOG_ERROR}
        ${IPTABLES} -t mangle -A FORWARD -m mac --mac-source ${MAC_IN} -j CLASSIFY --set-class 1:${CLASS_TR} 1>/dev/null 2>>${FILE_LOG_ERROR}
    fi
}

function unlimit_device()
{
    local DEV_IN=$1

    local ETH_SUB3=`${ECHO} ${DEV_IN} | ${CUT} -b 1-3`

    if [ "${ETH_SUB3}" = "eth" ]
    then
        if [ "${DEV_IN}" = "${ETH_BR}" ]
        then
            ${TC} qdisc del dev ${DEV_IN} root handle 1: htb 1>/dev/null 2>>${FILE_LOG_ERROR}
        else
            ${TC} qdisc del dev ${DEV_IN} root 1>/dev/null 2>>${FILE_LOG_ERROR}
        fi
    else
        local DOMID=`${ECHO} ${XENBUS_PATH} | ${MAWK} -F "/" '{print $3;}' 2>>${FILE_LOG_ERROR}`
        local CLASS_TR=$[ (${DOMID} + 1007) % 10000 ]

        local RATE_LIMIT_VALUE=`${CAT} ${DIR_CONFIG}/${DEV}.cfg | ${GREP} bandwith_limit | ${MAWK} -F "=" '{print $2;}' | ${TR} -d " '\"" | ${TR} -d "a-zA-Z" `
        local RATE_LIMIT_UNIT=`${CAT} ${DIR_CONFIG}/${DEV}.cfg | ${GREP} bandwith_limit | ${MAWK} -F "=" '{print $2;}' | ${TR} -d " '\"" | ${TR} -d "0-9" `
        local CEIL_LIMIT_VALUE=$[ ${RATE_LIMIT_VALUE} * 110 / 100 ]
        if [ ${RATE_LIMIT_UNIT} = "mb" ]
        then
            RATE_LIMIT_UNIT=mbit
        elif [ ${RATE_LIMIT_UNIT} = "kb" ]
        then
            RATE_LIMIT_UNIT=kbit
        fi

        local RATE_IN=${RATE_LIMIT_VALUE}${RATE_LIMIT_UNIT}
        local CEIL_IN=${CEIL_LIMIT_VALUE}${RATE_LIMIT_UNIT}
        if [ -z "${RATE_IN}" ]
        then
            RATE_IN=10mbit
            CEIL_IN=12mbit
        fi

        ${TC} qdisc del dev ${ETH_BR} parent 1:${CLASS_TR} handle ${CLASS_TR}: sfq perturb 10 1>/dev/null 2>>${FILE_LOG_ERROR}
        ${TC} class del dev ${ETH_BR} parent 1:1 classid 1:${CLASS_TR} htb rate ${RATE_IN} ceil ${CEIL_IN} prio 4 1>/dev/null 2>>${FILE_LOG_ERROR}
        #${TC} qdisc del dev ${DEV_IN} root 1>/dev/null 2>>${FILE_LOG_ERROR}
        local IPCHAIN=`${IPTABLES} -n -t mangle -L FORWARD --line-numbers | ${GREP} 1:${CLASS_TR} | ${MAWK} '{print $1;}'`

        if [ -n "${IPCHAIN}" ]
        then
            ${IPTABLES} -t mangle -D FORWARD ${IPCHAIN} 1>/dev/null 2>>${FILE_LOG_ERROR}
        fi

    fi
}

#############################################################################################################################################################################################################
DIR_CONFIG=/etc/xen
ETH_BR=eth1
THIS_FILE=`basename $0`
FILE_LOG_ERROR=/var/log/${THIS_FILE}.log
#############################################################################################################################################################################################################

COMMAND=$1
DEV=$2
DIR_CONFIG=/etc/xen
ETH_BR=eth1

if [ "${COMMAND}" = "start" ]
then
    limit_device ${DEV}
fi

if [ "${COMMAND}" = "stop" ]
then
    unlimit_device ${DEV}
fi

root@x49:/#

As you can see in the highlighted lines read our script bandwith_limit parameter configuration file, the line is also highlighted «ETH_BR = eth1» should be replaced eth1 by the physical interface in the bridge

Then we edit the file /usr/local/xen/vif-bridge-local.sh look so well

NOTE: We assume that we are using a configuration similar to that of our previous articles 1 and 2

Note 2: The unit parameter must be a unit bandwith_limit accepted by tc command, see section units in tc manpage accepted values ​​for rate (kbps, mbps, Kbit, Mbit, bps)

root@x49:/# cat /usr/local/xen/vif-bridge-local.sh
#!/bin/bash

XENSTORE_READ=/usr/sbin/xenstore-read
VNSTAT=/usr/bin/vnstat
LIMIT_BANDWITH=/usr/local/xen/bandwith-limit.sh

THIS_FILE=`basename $0`

FILE_LOG_ERRORS=/var/log/${THIS_FILE}.log

. /etc/xen/scripts/xen-script-common.sh

ACTION_TYPE=$1
TYPEIF=$2

evalVariables "$@"

dev=vif

case "$type_if" in
    vif)
        dev=${vif}
        ;;
    tap)
        dev=$INTERFACE
        ;;
    *)
        exit 1
        ;;
esac

if [ "${ACTION_TYPE}" = "online" ]
then
    if [ "${type_if}" = "vif" ]
    then
        /etc/xen/scripts/vif-bridge $@
        KEY_VIFNAME=/local/domain/0/${XENBUS_PATH}/vifname
        vifname=`${XENSTORE_READ} ${KEY_VIFNAME} 2>/dev/null`
        ${VNSTAT} -u -i ${vifname} 1>/dev/null 2>>${FILE_LOG_ERRORS}
        ${VNSTAT} --enable -i ${vifname} 1>/dev/null 2>>${FILE_LOG_ERRORS}
        ${VNSTAT} --sync -i ${vifname} 1>/dev/null 2>>${FILE_LOG_ERRORS}
        ${VNSTAT} --reset -i ${vifname} 1>/dev/null 2>>${FILE_LOG_ERRORS}
        ${LIMIT_BANDWITH} start ${vifname}
    else
        /etc/xen/scripts/vif-bridge $@
    fi
fi

if [ "${ACTION_TYPE}" = "offline" ]
then
    if [ "${type_if}" = "vif" ]
    then
        KEY_VIFNAME=/local/domain/0/${XENBUS_PATH}/vifname
        vifname=`${XENSTORE_READ} ${KEY_VIFNAME} 2>/dev/null`
        ${LIMIT_BANDWITH} stop ${vifname}
        ${VNSTAT} -r --disable -i ${vifname} 1>/dev/null 2>>${FILE_LOG_ERRORS}
        /etc/xen/scripts/vif-bridge $@
    else
        /etc/xen/scripts/vif-bridge $@
    fi
fi

if [ "${ACTION_TYPE}" = "add" ]
then
    /etc/xen/scripts/vif-bridge $@
fi

root@x49:/#

Finally if you want us to install the VPS with xen-tools to DomU configuration file will automatically add the line bandwith_limit = ’10mbit’ must edit the /etc/xen-tools/xm.tmpl and add at the end the next line

bandwith_limit = '10mbit';

So it looks like this

root@x49:/# cat /etc/xen-tools/xm.tmpl
#
# Configuration file for the Xen instance {$hostname}, created
# by xen-tools {$xen_tools_version} on { scalar localtime }.
#

#
#  Kernel + memory size
#
{ if ( ( $kernel ) && ( !defined($pygrub)) )
  {
    $OUT.= "kernel      = '$kernel'";
  }
}
{ if ( ( $initrd ) && ( !defined($pygrub)) )
  {
    $OUT.= "ramdisk     = '$initrd'";
  }
}
{
  if ( $pygrub )
  {
    my $pygrub_bin = '';
    foreach my $pygrub_path (reverse glob('/usr/lib/xen-*/bin/pygrub
                                           /usr/lib/xen-default/bin/pygrub
                                           /usr/*bin/pygrub')) {
      if (-x $pygrub_path) {
        $pygrub_bin = $pygrub_path;
        last;
      }
    }

    die "pygrub not found" unless $pygrub_bin;

    $OUT .= "bootloader = '$pygrub_bin'\n";
  }
}
vcpus       = '{$vcpus}'
memory      = '{$memory}'

#
#  Disk device(s).
#
{
  if ( !defined($image_vbd ) )
  {
    for ( my $i = $#PARTITIONS; $i >= 0 ; $i-- )
    {
      if ( $PARTITIONS[$i]{'mountpoint'} eq '/' )
      {
          $OUT .= "root        = '/dev/$device" . ($i + 1) . " ro'\n";
      }
    }
    $OUT .= "disk        = [\n";
    for ( my $i = $#PARTITIONS; $i >= 0 ; $i-- )
    {
       if ( $PARTITIONS[$i]{'mountpoint'} eq '/' )
       {
           $OUT .= "                  '$PARTITIONS[$i]{'imagetype'}$PARTITIONS[$i]{'image'},$device" . ( $i + 1 ) .",w',\n";
       }
    }
    for ( my $i = $#PARTITIONS; $i >= 0 ; $i-- )
    {
       if ( $PARTITIONS[$i]{'mountpoint'} ne '/' )
       {
           $OUT .= "                  '$PARTITIONS[$i]{'imagetype'}$PARTITIONS[$i]{'image'},$device" . ( $i + 1 ) .",w',\n";
       }
    }
    $OUT .= "              ]\n";
  }
}

#
#  Physical volumes
#
{
    if ( $image_vbd )
    {
        $OUT .= "root        = '/dev/$device" . "2 ro'\n";
        $OUT .= "disk        = [\n";
        $OUT .= "                  '$image_vbd," . $device . "2,w',\n";

        if ( $swap_vbd )
        {
            $OUT .= "                  '$swap_vbd," . $device . "1,w',\n";
        }
        $OUT .= "              ]\n";
    }
}

#
#  Hostname
#
name        = '{$hostname}'

#
#  Networking
#
{ if ( $dhcp )
  {
    $OUT .= "dhcp        = 'dhcp'\n";

    # Setup the mac address, if present.
    my $m = '';
    if ( $mac )
    {
      $m = "mac=$mac"
    }
    my $br = '';
    if ( $bridge )
    {
      if ( $mac )
      {
        $br = ",bridge=$bridge"
      }
      else
      {
        $br = "bridge=$bridge"
      }
    }
    $OUT .= "vif         = [ '";
    $OUT .= "$m";
    $OUT .= "$br";
    $OUT .= "' ]";
  }
  else
  {
    #
    # Setup the mac address, if present.
    #
    my $m = '';
    if ( $mac )
    {
      $m = ",mac=$mac"
    }

    my $vn = '';
    if ( $vifname )
    {
      $vn = ",vifname=$vifname";
    }

    my $br = '';
    if ( $bridge )
    {
      $br = ",bridge=$bridge"
    }

    $OUT .= "vif         = [ 'ip=$ips";
    $OUT .= "$m";
    $OUT .= "$vn";
    $OUT .= "$br";
    $OUT .= "' ]";
  }
}

#
#  Behaviour
#
on_poweroff = 'destroy'
on_reboot   = 'restart'
on_crash    = 'restart'

{ if ( $admins )
  {
    $OUT .= "xen_shell = '$admins'\n";
  }
}

bandwith_limit = '10mbit'

root@x49:/#

Ready, then check that the configuration is working properly, restart the VPS and test the bandwith both input and output of VPSs.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.