Ene 192013
 

En este articulo veremos como limitar el ancho de banda de los DomUs en XEN 4.1 instalado en una debian squeeze.
Aclaración debe estar configurado el DomU de modo que vif debe tener seteados vifname y mac, el parámetro name del VPS y vifname sean iguales, también debemos agregar una linea de configuración con la variable bandwith_limit, XEN no reconoce este parámetro y lo ignorara pero sera usado por nuestro script. Además debemos asegurarnos que estén instalados los paquetes mawk, ethtool e iproute.

Podemos ver un archivo de configuración de ejemplo.

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:/#

Podemos ver resaltadas las lineas donde estan las variables name, vifname, mac y bandwith_limit

Luego creamos el archivo /usr/local/xen/bandwith-limit.sh con el siguiente contenido

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:/#

Como se puede ver en las lineas resaltadas nuestro script lee el parámetro bandwith_limit del archivo de configuración, también esta resaltada la linea «ETH_BR=eth1» deben reemplazar eth1 por la interfaz física que este en el bridge

Luego debemos editar el archivo /usr/local/xen/vif-bridge-local.sh de modo que luzca así

NOTA: Suponemos que estamos usando una configuración similar a la de nuestros anteriores artículos 1 y 2

Nota 2: La unidad del parámetro bandwith_limit debe ser una unidad aceptada por el comando tc, ver man tc la sección units los valores aceptados para 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:/#

Por ultimo si queremos que a los VPSs que instalemos con xen-tools al archivo de configuración del DomU se le agregue automáticamente la linea bandwith_limit = ’10mbit’ debemos editar el archivo /etc/xen-tools/xm.tmpl y agregarle al final la siguiente linea

bandwith_limit = '10mbit';

De modo que quede así

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:/#

Listo, luego chequear que la configuración funcione correctamente, reiniciar los VPSs y testear el bandwith tanto de entrada como de salida de los VPSs.

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