sábado, 1 de febrero de 2014

Relaciones entre modelos en Yii


Supongamos que tenemos 3 tablas: user, city y country, relacionadas de la siguiente forma: un usuario vive en una ciudad (user tiene un campo city_id que referencia a una entrada de la tabla city), y una ciudad pertenece a un país (city tiene un campo country_id que referencia a una entrada de la tabla country).

Si en alguna vista relacionada con los usuarios queremos mostrar información referente al país del usuario, la clave está en definir relaciones. Las relaciones entre modelos se establecen mediante la función relations(), y pueden ser de 4 tipos:
  • BELONGS_TO
  • HAS_MANY
  • HAS_ONE
  • MANY_MANY

En este caso, nos basta con relaciones BELONGS_TO, pero es bueno saber el significado de cada relación y cómo ha de usarse (alguien que sepa crear y administrar bases de datos, debería saber de entrada qué significa cada relación). Para profundizar en el tema, consultar la guía.

En el modelo User debemos definir una relación BELONGS_TO con relación a la ciudad:

        public function relations()
{
return array(
  'city' = array(self::BELONGS_TO, 'City', 'city_id');
);
}

En el modelo City debemos definir otra relación BELONGS_TO con relación al país:

        public function relations()
{
return array(
  'country' => array(self::BELONGS_TO, 'Country', 'country_id'),
);
}

Con esto es suficiente, no es necesario modificar la(s) función(es) de búsqueda del modelo User para incluir joins o cosas parecidas, el Active Record de Yii se encargará de todo.

Si por ejemplo vamos a mostrar una lista de usuarios en un CGridView, en el cual accedemos a los atributos de User como $data->username o $data->email, podemos acceder a los campos correspondientes al país, mediante $data->city['country']['name'] o $data->city['country']['population'].

Personalmente, a esto último llegué por ensayo y error, pero una vez asimilado creo que no se olvida. Si por ejemplo hubiese una hipotética tercera relación, digamos que un país pertenezca a un planeta, accederíamos a la data del planeta mediante $data->city['country']['planet']['name'] (siempre que hayamos definido una relación planet en el modelo country, lógicamente).

martes, 13 de agosto de 2013

Android PNG


Dimensiones: 1920x1080
Formato: PNG
Tamaño: 5.94MB

Para utilizar como Wallpaper, ícono, etc.

(Clic en la imagen para descargar en tamaño completo)


jueves, 1 de agosto de 2013

Menú desplegable simple (HTML/CSS)


Esta mañana, un cliente me pedía un menú desplegable (dropdown menu) para su sitio web (hecho en HTML puro). Cosa muy sencilla, que va de ocultar/mostrar elementos con la propiedad display de CSS.

En code-tricks tienen un excelente ejemplo de menú desplegable, hecho con listas. El menú de mi cliente está hecho con tablas, así que para modificar su código lo menos posible, adapté el ejemplo de code-tricks para tablas. Por supuesto, siendo obsoleto el uso de tablas para maquetación, recomiendo utilizar listas, pero por si acaso se presenta el caso, les muestro el código.

El HTML:

<table cellpadding="0" cellspacing="0" style="text-align:center">
  <tr>
    <td class="menu" width="120">
   <a href="#">Item 1</a>
   <ul class="sub-menu">
        <li><a href="sub1-1.htm">Sub Menu 1-1</a></li>
  <li><a href="sub1-2.htm">Sub Menu 1-2</a></li>
  <li><a href="sub1-3.htm">Sub Menu 1-3</a></li>
  <li><a href="sub1-4.htm">Sub Menu 1-4</a></li>
   </ul>
 </td>
    <td class="menu" width="120">
   <a href="#">Item 2</a>
   <ul class="sub-menu">
        <li><a href="sub2-1.htm">Sub Menu 2-1</a></li>
  <li><a href="sub2-2.htm">Sub Menu 2-2</a></li>
  <li><a href="sub2-3.htm">Sub Menu 2-3</a></li>
   </ul>
 </td>
    <td class="menu" width="120">
   <a href="#">Item 3</a>
   <ul class="sub-menu">
        <li><a href="sub3-1.htm">Sub Menu 3-1</a></li>
  <li><a href="sub3-2.htm">Sub Menu 3-2</a></li>
  <li><a href="sub3-3.htm">Sub Menu 3-3</a></li>
   </ul>
 </td>
    </tr>
</table>

El CSS:

.menu, .menu ul.sub-menu {
    padding:0;
    margin: 0;
}
.menu ul.sub-menu li {
    list-style-type: none;
    display: inline-block;
    background: url(../images/nav-bar.png) center left repeat-x;
}
.menu a, .menu ul.sub-menu li a {
    text-decoration: none;
    padding: 5px;
    display:inline-block;
}
.menu {
    position: relative;
}
/*sub menu*/
.menu ul.sub-menu {
    display:none;
    position: absolute;
    top: 40px;
    width: 100px;
}
.menu:hover ul.sub-menu {
    display:block;
}

Y el resultado es este:



Por supuesto necesitaremos darle formato para adaptarlo a las necesidades del momento. Este ejemplo es sólo una base para ello.

jueves, 25 de julio de 2013

ndiswrapper y Ubuntu 13.04

En un artículo anterior describí como logré hacer funcionar mi tarjeta Encore ENLWI-NX2 en Ubuntu 10.10, utilizando ndiswrapper para utilizar el driver de Windows, ya que no existía un driver nativo para Linux. Posteriormente conté qué tuve que hacer cuando mi tarjeta dejó de funcionar tras un upgrade a Ubuntu 12.04.

Bien, hoy realicé una instalación limpia de Ubuntu 13.04, y esto fue lo que tuve que hacer para que mi ENLWI-NX2 funcionara.


1) Descargar e instalar dkms

Asumiendo que no tenemos otra forma de conexión a Internet, toca descargar los paquetes desde otra máquina y llevarlos en una memoria flash, o algo así.

El paquete se puede descargar desde aquí: dkms y luego lo instalamos con:
sudo dpkg -i dkms.deb //usar el nombre específico del .deb descargado


2) Descargar, compilar e instalar ndiswrapper

En algún foro recomendaban instalar directamente el paquete ndiswrapper-dkms (junto con ndiswrapper-common). A mí no me funcionó (Linux no reconocía el módulo, no investigué mucho por qué), por lo cual opté por bajar el código fuente desde aquí, y luego compilarlo e instalarlo con:
make
sudo make install


3) Descargar e instalar ndiswrapper-dkms

Bajar desde aquí e instalar con:
sudo dpkg -i ndiswrapper-dkms.deb //usar el nombre específico del .deb descargado


4) Instalar el controlador de la tarjeta

sudo ndiswrapper -i DRIVERNAME.inf //logicamente debemos tener el .inf


5) Cargar el módulo ndiswrapper
depmode -a
modprobe ndiswrapper


6) Agregar ndiswrapper a /etc/modules
Para que ndiswrapper se cargue cada vez que arranque el sistema, agregar ndiswrapper a
/etc/modules.


Eso es todo, ya podremos conectarnos a nuestra red Wi-Fi favorita :)






lunes, 24 de junio de 2013

Creación de Imágenes ISO en Ubuntu con Brasero

Brasero es probablemente la aplicación para creación de discos más usada en Linux, y es además realmente sencilla. A continuación, muestro dos ejemplos de uso para la creación de imágenes (ISO en este caso).


Caso 1: ISO a partir de CD/DVD

Insertamos el disco del que queremos extraer la imagen, abrimos Brasero y seleccionamos Disc Copy. A continuación seleccionamos la unidad donde insertamos el disco y escogemos Image File.

Por omisión, Brasero graba las imágenes en formato .toc, por lo que debemos hacer click en Properties y seleccionar .iso (también acá podemos seleccionar la ruta donde se guardará el archivo).


Finalmente hacemos Create Image y esperamos que se genere nuestro archivo.





Caso 2: ISO a partir de archivos/directorios

Si lo que tenemos es un conjunto de directorios y archivos en nuestro ordenador, a partir del cual queremos hacer un archivo ISO, debemos seleccionar Data Project, y añadir todos los archivos/directores que deben ir a la imagen, tal como si fueramos a crear una copia física.

Seleccionamos Image File y luego hacemos clic en Burn (Si brasero no detecta un disco virgen en la unidad, mostrará un breve mensaje y seleccionará Image File por omisión).


Al hacer clic en Burn, Brasero nos pedirá el nombre y ubicación del archivo.


Para terminar, hacemos clic en Create Image y comenzará el proceso de creación de la imagen.



jueves, 7 de marzo de 2013

Marca de agua con GIMP

Conozco 2 opciones a la hora de crear una marca de agua para nuestras fotografías: guardarla como PNG (o nuestro formato de preferencia) y luego pegarla sobre cada fotografía, o guardarla como un pincel/brush. Guardar nuestra marca como imagen, permite que la podamos usar en prácticamente cualquier editor de imágenes, mientras que los brushes son relativos a la aplicación que usemos (generalmente). Sin embargo, si utilizamos siempre una misma herramienta de edición, es más fácil crear un brush una vez y usarlo luego utilizarlo, además de que seguramente así tendremos muchas más opciones.

Bien, la herramienta que yo uso es GIMP, y a continuación describo cómo guardar (y cómo usar luego) nuestras marcas de agua como archivos GBR (Gimp Brush).

Comenzamos creando un nuevo archivo fondo transparente, como se ve en la siguiente imagen.



Las dimensiones no tienen que ser exactamente esas, pueden ser mucho más pequeñas si va a ser una marca simple. Todo depende de la calidad de imágenes que vayamos a firmar, si son muy grandes, nuestra firma también debería serlo.

El siguiente paso es crear la firma... Allí es donde entran los gustos y la creatividad de cada quien. Bien, yo hice una simple para este ejemplo que es sólo el texto '@marcocamejo'.

Si hemos usado varias capas, antes de guardar, debemos mezclarlas (Image > Merge visible layers). También es muy recomendable eliminar el espacio que sobre (Image > Autocrop image).

Bien, una vez terminada la firma, guardamos con extensión .gbr. GIMP mostrará el siguiente cuadro.



Luego, para que GIMP reconozca el brush, debemos copiar el archivo gbr al directorio de brushes de GIMP. En esta máquina (con Ubuntu 12.10 y GIMP 2.6), ese directorio es $HOME/gimp-2.6/brushes.

Y eso es todo. Cada vez que queramos firmar una fotografía, sólo debemos usar la herramienta paintbrush y escoger nuestro pincel.



Como ven, y como comentaba al comienzo, de esta forma tenemos algunas opciones para configurar nuestra firma (escala, opacidad, etc).






martes, 12 de febrero de 2013

Creando parches para el kernel de Linux


Como parte del producto de mi trabajo de maestría en Computación, he requerido generar un parche para el kernel de Linux. El proceso es muy sencillo, y no es muy diferente al crear un parche de cualquier archivo o directorio común y corriente, utilizando el comando diff. Resumo el proceso en 3 pasos.

1. Preparar los directorios

Debes tener un directorio con el kernel vanilla y otro directorio con el kernel modificado. Por supuesto, deben corresponder a la misma versión. En mi caso, estoy trabajando con la versión 3.2.1, y he nombrado linux-3.2.1 a mi kernel modificado y linux-vanilla-3.2.1 al kernel vanilla.

Ambos kernels los tengo en el directorio /usr/src, pero esto no es necesario, pueden estar en un directorio cualquiera.

2. Limpiar ambos directorios

Asumiendo que los kernels se encuentran en /usr/src, basta con ejecutar:
cd /usr/src/linux-3.2.1
make mrproper
make clean
cd ../linux-vanilla-3.2.1
make mrproper
make clean

3. Generar el parche

Finalmente, usamos el comando diff para generar el parche:
cd /usr/src/
diff -Nur linux-vanilla-3.2.1 linux-3.2.1 > patch-3.2.1

¡Y eso es todo! El archivo patch-3-2.1 es nuestro parche.

NOTA: al usar el comando diff, el orden es importante, primero va el kernel vanilla y luego el modificado, no al revés.

lunes, 17 de septiembre de 2012

Impriendo data selectivamente con Gnuplot

Gnuplot es una muy buena herramienta (de línea de comandos) de graficado 2D/3D que funciona en los Sistemas Operativos más comunes. Me es particularmente útil para hacer gráficas de datos tabulados.

Eventualmente recibimos alguna data tabulada para graficar, pero de la que no nos interesa más que una parte. Por ejemplo, nos dan un archivo en el que cada fila representa ciertos indicadores económicos desde el año 1960 hasta 2012, y sólo queremos graficar los indicadores desde 1980 hasta 2000. Otro ejemplo podría ser que tengamos que graficar muchos archivos que tienen una o más líneas de cabecera y lógicamente debemos omitirlas. Otro caso, podría ser que, volviendo al primer ejemplo, quisieramos graficar los datos de cada 5 años y no todos (1965, 1970, 1975, etc).

Para estos casos y muchos más, tenemos la opción every, cuya sintaxis es la siguiente:

every I:J:K:L:M:N

En donde:

  • I: incremento de línea
  • J: incremento de bloque de datos
  • K: primera línea (empezando en 0)
  • L: primer bloque de datos (empezando en 1). Se consideran bloques de datos, los separados por líneas en blanco en el archivo
  • M: última línea
  • N: último bloque de datos

Por ejemplo, si tenemos un archivo con la siguiente estructura:

#year #var1 #var2 #var3
1960 120 80 90
1961 125 85 91
1962 124 79 91
1963 121 81 88
...

y queremos graficar year Vs. var3,  omitiendo la primera línea (cabecera) y sólo tomando en cuenta los primero 100 valores:

plot "archivo.in" every ::1::100 using 1:4 with lp


Observe que sólo se especifica hasta el índice que estemos utilizando (no se colocan ":"s de relleno al final). Por ejemplo, si quisieramos imprimir todos los datos, sólo omitiendo los primeros 3, sería:

plot "archivo.in" every ::3 using 1:4 with lp

Combinando los índices, tenemos muchas posibilidades.

miércoles, 12 de septiembre de 2012

ndiswrapper y Ubuntu 12.04

Hace un rato hice el upgrade a Ubuntu 12.04 (sí, ya sé que está casi listo el 12.10), desde Ubuntu 11.10, en una PC de escritorio que se conecta a Internet a través de una tarjeta wifi encore ENLWI-NX2, que como conté hace algún tiempo, no es soportada por Ubuntu, así que uso ndiswrapper para cargar el driver de Windows.

El punto es que luego de terminado el upgrade no tenía acceso a Internet. En el menú de redes de Ubuntu no me aparecía ninguna red inalámbrica. Para diagnosticar el problema, lo primero que hago es revisar si el driver estaba cargado:
sudo lshw -C network

Y me aparece como UNCLAIMED la interfaz de la tarjeta inalámbrica, es decir, como si no hubiese ningun driver cargado. Sin embargo, luego pruebo:
sudo ndiswrapper -l

y me doy cuenta que el driver SÍ estaba instalado. Aquí fue donde comencé a googlear, y me econtré con esta pregunta. Allí la primera sugerencia era instalar ndiswrapper-dkms, y con ello resolví el problema.

Si la única forma de acceso a Internet de esa máquina es mediante la red inalámbrica, tocará llevarse el paquete en un disco o memoria usb. Se puede descargar de acá:

Además, este paquete depende de dkms, que probablemente no esté instalado, así que hay que llevarlo también:

Luego instalamos los paquetes y cargamos el módulo:
sudo modprobe ndiswrapper

Y recordar que si queremos que el módulo se cargue cada vez que se arranca el sistema, debemos agregarlo a /etc/modules.

viernes, 13 de julio de 2012

7 ejemplos prácticos del comando Locate de Linux



Cuando se necesita buscar algún archivo, comunmente se utiliza el comando find. find es una buena utilidad de búsqueda, pero es lenta. locate puede buscar archivos de forma muy rápida. A continuación presento una traducción -libre- de este artículo, donde se detalla el funcionamiento de locate y se incluyen algunos prácticos ejemplos de uso.



Cómo funciona locate? - updatedb yh updatedb.conf


Cuando decimos que locate busca muy rápido, entonces la primera pregunta que viene a la mente es ¿qué lo hace tan rápido?

Bien, locate no busca los archivos en disco, sino que busca las rutas a los archivos en una base de datos.

La base de datos es un archivo que contiene información sobre los archivos y sus rutas en el sistema. La base de datos de locate se encuentra en /var/lib/mlocate/mlocate.db

La siguiente pregunta lógica es ¿qué mantiene a esta base de datos mlocate actualizada?

Bien, existe otra utilidad conocida como updatedb. Cuando se ejecuta updatedb, se escanea el sistema completo y se actualiza mlocate.db.

De tal manera que una de las limitaciones de locate es que depende de la base de datos, la cual es actualizada por updatedb. Por lo tanto, para obtener resultados actualizados y fiables, de locate, la base de datos debe ser actualizada frecuentemente.

También se puede configurar updatedb de acuerdo a nuestras necesidades. Esto se puede lograr actualizando el archivo updatedb.conf. Este es un archivo de configuración que lee updatedb antes de actualizar la base de datos. updatedb.con se encuentra dentro de /etc/:

# cat /etc/updatedb.conf
PRUNE_BIND_MOUNTS="yes"
PRUNENAMES=".git .bzr .hg .svn"
PRUNEPATHS="/tmp /var/spool /media"
PRUNEFS="NFS nfs nfs4 rpc_pipefs afs binfmt_misc proc smbfs autofs iso9660 ncpfs coda devpts ftpfs devfs mfs shfs sysfs cifs lustre_lite tmpfs usbfs udf fuse.glusterfs fuse.sshfs ecryptfs fusesmb devtmpfs"

updatedb.conf contiene data en la forma VARIABLE=VALOR. Estas variables pueden ser clasificadas en las siguientes categorías:

  • PRUNEFS: Una lista (separada por espacios en blanco) de tipos de sistemas de archivos (como se usan en /etc/mtab) que no deben ser escaneados por updatedb. La comparación de los tipos de archivos es insensible a mayúsculas y minúsculas. Por defecto, ningún sistema de archivo es omitido. Cuando se escanea y un sistema de archivo es omitido, todos los sistemas de archivos montados en su sub-árbol son también omitidos, inclusive si su tipo no coincide con alguna entrada en PRUNEFS.
  • PRUNENAMES: Una lista (separada por espacios en blanco) de nombres de directorio (sin rutas) que no deben ser escaneados por updatedb. Por defecto, ningún nombre de directorio es omitido. Note que sólo se pueden especificar directorios y no se pueden utilizar mecanismos de patrones (como globbing, por ejemplo).
  • PRUNEPATHS: Una lista (separa por espacios en blanco) de nombres de rutas de directorio que no deben ser escaneadas por updatedb. Cada nombre de ruta debe estar en la forma exacta en la cual el directorio sería reportado por locate. Por defecto, ninguna ruta es omitida.
  • PRUNE_BIND_MOUNTS: puede valer "0", "no", "1" o "yes". Si PRUNE_BIND_MOUNTS vale "1" o "yes", los bind mounts que tengamos en el sistema no serán escaneados por updatedb. Todos los sistemas de archivo montados en el sub-árbol de un bind mount también serán omitidos, inclusive si no son bind mounts. Por defecto, los bind mounts no son omitidos.

Tenga en cuenta que toda esta configuración se puede modificar a tavés de las opciones del comando updatedb.


Ejemplos prácticos del uso de locate

1. Buscar un archivo usando locate


Para buscar un archivo con locate, ejecutamos:

locate nombre_de_archivo

Si se especifica la opción -0 se muestra la salida en una sola línea.

locate -0 nombre_archivo


2. Mostrar sólo la cantidad de archivos encontrados

locate -c nombre_de_archivo


3. Usar updatedb para actualizar la base de datos mlocate

sudo updatedb #debe ser ejecutado como root


4. Cambiar la base de datos mlocate

La base de datos que locate utiliza por omisión es /var/lib/mlocate/mlocate.db, pero se puede especificar otra, se utiliza la opción -d:

locate -d ruta_otra_base_datos nombre_de_archivo

5. Verificar si un archivo existe

Si eliminamos un archivo del sistema, hasta que no se actualice la base de datos, seguirá apareciendo en los resultados de búsqueda de locate. Si especificamos la opción -e, locate verificará la existencia física de los archivos y no mostrará aquellos archivos que no se encuentren.

locate -e nombre_archivo

6. No tomar en cuenta mayúsculas y minúsculas

Siendo locate sensible a las mayúsculas y minúsculas, "new.txt" es distinto a "NEW.txt". Si queremos que esto no sea tomado en cuenta, utilizamos la opción -i:

locate -i nombre_De_ArchiVo


7. Filtrar las salidas

Si queremos ver sólo los primeros N resultados, utilizamos la opción -l:

locate -l N nombre_de_archivo

Fuente: TheGeekStuff

jueves, 12 de julio de 2012

Programación de Módulos para el Kernel de Linux (II)

En una entrada anterior realicé una introducción a la programación de módulos para el kernel del Linux: los comandos básicos para gestionarlos (lsmod, insmod, rmmod, etc), su estructura básica, compilación, alguno que otro detalle y por supuesto, el típico hola mundo.

Particularmente para la compilación, utilicé un Makefile sencillo para utilizarlo en cualquier directorio y contra la versión actual del kernel.

Ahora queremos compilar un kernel vanilla, con nuestros propios módulos. Esto es bastante parecido a lo que hicimos en la entrada anterior con algunas consideraciones adicionales.

Partimos del hecho de que tenemos el kernel vanilla sobre el que trabajaremos, y más importante aún, que sabemos compilarlo, así que no tocaremos detalles de la compilación per se.

Agregaremos el hola mundo que ya hemos hecho, al directorio del kernel en el que se almacena el Linux Virtual Server, que en la versión sobre la cual estoy trabajando (la 3.0.1), sería net/netfilter/ipvs/.

Bien, vamos al directorio net/netfilter/ipvs y agregamos nuestro módulo que llamaremos ip_vs_hola.c. Dentro de él, colocaremos el código tal cual como el ejemplo final de la entrada anterior:

#include <linux/module.h>  /* utilizada por todos los modulos */
#include <linux/init.h>    /* utilizada para poder usar macros */
#include <linux/kernel.h>  /* utilizada por printk */

int init_func()
{
    printk(KERN_INFO "holamundo: cargando...\n");
    return 0;
}

void exit_func()
{
    printk(KERN_INFO "holamundo: descargando...\n");
}

module_init(init_func);
modue_exit(exit_func);

Bien, si queremos que nuestro módulo aparezca entre las opciones cuando hacemos make menuconfig, por ejemplo, debemos agregarlo al archivo Kconfig del directorio en el cual se encuentra el módulo (net/netfilter/ipvs/Kconfig en este caso).

config CONFIG_IP_VS_HOLA
    tristate "Hola Mundo"
    help
      Modulo hola mundo para el ip_vs.

Acá le hemos puesto un nombre para ser usado en el ámbito de la configuración, hemos dicho que puede ser seleccionado como módulo, built-in o ninguno, y hemos especificado una ayuda corta. Para más detalles, revisar la documentación oficial de kconfig aquí.

El siguiente paso es modificar el Makefile (también del directorio donde se encuentra el módulo) con las reglas necesarias para su compilación. En este caso, dada la sencillez del problema, basta con agregar obj-$(CONFIG_IP_VS_HOLA) += ip_vs_hola.o en alguna parte del Makefile. Así estaríamos usando la variable con la cual identificamos a nuestro módulo, que al momento de compilar se transformará en "y" o "m" si configuramos para ser compilada buit-in o como módulo, respectivamente.

Finalmente nos queda completar los pasos normales para una instalación de un kernel a partir de sus fuentes (compilar el kernel, compilar los módulos, instalar los módulos e instalar el kernel).

Podemos hacer el ejercicio de seleccionar nuestra opción como built-in, en vez de módulo, y veremos como luego de instalado el kernel, cada vez que lo iniciemos, tendremos en el log el mensaje que imprimimos en la función de inicialización. Si por el contrario, seleccionamos como módulo, tendremos que hacer un modprobe para que se ejecute dicha función.


EXPORT_SYMBOL

Una de las múltiples razones por las cuales quisieramos agregar nuestros módulos a un kernel, es agregar funcionalidades de algún tipo. Eventualmente podríamos requerir utilizar en un módulo, una función que hemos definido en otro módulo.

Ahora vamos a agregar una función saludar() a nuestro módulo ip_vs_hola, que también imprima algo con printk(). Si quisieramos utilizar esta función dentro de otro módulo ip_vs_hola_2, utilizamos EXPORT_SYMBOL de la siguiente manera en ip_vs_hola:

#include <linux/module.h>  /* utilizada por todos los modulos */
#include <linux/init.h>    /* utilizada para poder usar macros */
#include <linux/kernel.h>  /* utilizada por printk */

int saludar()
{
 printk(KERN_INFO "la funcion saludar ha sido invocada \n");
 return 1;
}
EXPORT_SYMBOL(saludar);

int init_func()
{
    printk(KERN_INFO "holamundo: cargando...\n");
    return 0;
}

void exit_func()
{
    printk(KERN_INFO "holamundo: descargando...\n");
}

module_init(init_func);
modue_exit(exit_func);

Y luego, en ip_vs_hola_2 bastaría con una referencia extern a dicha función.
#include <linux/module.h>  /* utilizada por todos los modulos */
#include <linux/init.h>    /* utilizada para poder usar macros */
#include <linux/kernel.h>  /* utilizada por printk */

extern int saludar(void);

int init_func()
{
    saludar();
    return 0;
}

void exit_func()
{
    printk(KERN_INFO "holamundo: descargando...\n");
}

module_init(init_func);
modue_exit(exit_func);

Y eso es todo. No es nada del otro mundo, pero hay que tomar un par de cosillas adicionales en cuenta. 

lunes, 2 de julio de 2012

Descargar videos de youtube desde Ubuntu con youtube-dl

Existen infinidad de opciones para descargar videos de youtube. Una de las que más me gusta, es la que ofrece el site keepvid.com (valga la publicidad): simplemente introducimos la URL del video y obtenemos los enlaces de descarga en distintas calidades. Como keepvid existen muchos sitios, la mayoría de ellos plagados de publicidad, cosa molesta, de la que keepvid no tiene mucha.

Sin embargo, hoy me he encontrado con una aplicación para Linux que funciona por línea de comandos: youtube-dl. Es una maravilla, viene con muchas opciones y, repito, funciona en línea de comandos :)

En Ubuntu, la instalación es muy simple, ya que el paquete se encuentra en los repositorios oficiales, así que es cuestión de una línea:
sudo aptitude install youtube-dl

Luego de instalado, tecleamos:
youtube-dl http://www.youtube.com/watch?v=BUhZF9KghyI
y nos estaremos bajando, al directorio actual, un video con los 73 goles de Messi en la temporada 2011-2012 :)

Como dije, youtube-dl viene con una buena cantidad de opciones. Entre las más interesantes está el de la calidad. La especificamos con la opción -f. La lista de calidades la podemos ver aquí. Por ejemplo, para bajar el video anterior en format FLV a 480p, sería:
youtube-dl -f 35 http://www.youtube.com/watch?v=BUhZF9KghyI

Si vieron la lista, entenderán que 35 corresponde al itag de la calidad que dijimos. Hay que aclarar que en Youtube, no todas las calidades están disponibles para todos los videos. En este caso, youtube-dl nos avisará que no puede realizar la descarga.

Si queremos además ponerle un nombre, utilizamos la opción -o:
youtube-dl -f 35 -o golesDeMessi.flv http://www.youtube.com/watch?v=BUhZF9KghyI

También tenemos la posibilidad de descargar una playlist completa o parte de ella, para lo cual usamos opcionalmente --playlist-start=M para especificar el número de video desde el cual comenzar a descargar, y --playlist-end=N para especificar el último. Si no especificamos playlist-start se comienza con el video número 1 y sin playlist-end se termina con el último.

Si sólo queremos el audio del vídeo, podemos utilizar --extract-audio y --audio-format=FORMAT (Siendo FORMAT: "best", "aac" o "mp3").

Existen unas cuantas opcions más, cuestión de hacer youtube-dl --help para revisarlas todas. Es importante destacar que además de youtube, youtube-dl también permite descargar videos de Dailymotion, Vimeo y algunos otros sitios.

Migrar Máquina Virtual KVM

Normalmente uso una PC de escritorio y una Laptop. En ambas necesito tener un cluster de máquinas virtuales con Ubuntu corriendo, algunas de ellas con kernels personalizados. He dedicado bastante tiempo para lograr cierta configuración, por lo que perderlas por alguna falla de disco, por ejemplo, sería catastrófico para mí.

Por ello, dediqué un tiempo a averiguar qué tan factible sería poder hacer un backup de mis VMs para luego poder llevarlas a otra máquina y no perder horas de trabajo para lograr una configuración como la que tenía. Afortunadamente, llegué a la conclusión de que sí es factible (y además fácil) :D

Para comenzar la migración, lo primero que debemos hacer es copiar la imagen de la Máquina Virtual que queremos migrar hacia el nuevo host. Si utilizamos libvirt, normalmente las VMs se almacenan en el directorio /var/lib/libvirt/images.

Luego, desde el nuevo host, abrir Virtual Machine Manager, y arrancar el asistente para crear una nueva Máquina Virtual y seleccionar la opción Import existing disk image.


En el siguiente paso del asistente nos pedirá especificar la ruta a nuestra imagen. No creo que sea estrictamente necesario, pero creo que es bueno, que la imagen se encuentre en /var/lib/libvirt/images y pertenezca al usuario root.



A partir de aquí el proceso es igual al que normalmente realizamos para crear VMs, es decir, seleccionamos la cantidad de CPUs, cantidad de memoria RAM, etc. Obviamente, el asistente no nos permitirá seleccionar el tamaño de disco, porque ya tenemos la imagen.

Al finalizar el asistente, la Máquina Virtual estará lista para usarse y arrancará inmediatamente. En mi caso, hice la migración de una imagen de 8GB y otra de 5.5GB, ambas con Ubuntu 11.10 y funcionó perfectamente en ambos casos. Sólo hay que tener en cuenta que la dirección de red local cambia (en el caso de que eso sea relevante).

sábado, 30 de junio de 2012

Recuperar GRUB después de instalar Windows

Es sumamente común: tenemos una PC con varios sistemas operativos, utilizamos GRUB como gestor de arranque; todo bien hasta que (re)instalamos algún Windows y GRUB desaparece.

Existen diversas soluciones al problema, unas más complicadas que otras, algunas inviables por algun motivo particular. En mi caso, para que Ubuntu pueda utilizar la tarjeta Wi-Fi, debo utilizar ndiswrapper con un  controlador para Windows, por lo que utilizando un Live-CD de entrada no tengo Internet. Además estoy lejos del AP, como para conectarme por cable. En fin, opciones que requieran el uso de Internet no me sirven.

A continuación pongo los pasos que me sirvieron en este caso, repito, en el que sólo dispongo de un Live-CD de Ubuntu, sin conexión a Internet.

0. Arrancar el Live-CD

1. Determinar la partición que tiene Ubuntu.
sudo fdisk -l

2. Montar la partición.
sudo mount /dev/sdXY /mnt

En donde X corresponde a la letra del disco duro y Y a la partición, que determinamos en el paso anterior. En mi caso X=a, Y=5, es decir, que mi comando fue sudo mount /dev/sda5 /mnt

3. Instalar GRUB
sudo grub-install --root-directory=/mnt /dev/sdX

En donde X corresponde a la letra del disco duro (a en mi caso)

4. Reiniciar la pc. Si todo salió bien, no debería aparecer ningún error y arrancar GRUB.

5. Actualizar GRUB.
sudo update-grub2

Y eso es todo. Espero que haya sido de utilidad

miércoles, 16 de mayo de 2012

Stress para sobrecargar un sistema Linux

Stress (de Amos Waterland) es una sencilla herramienta que nos permite estresar o sobrecargar sistemas Unix/Linux en 4 aspectos: CPU, Memoria, uso de disco y Entrada/Salida.


Instalación

La instalación es muy sencilla. Es cuestión de descargar y descomprimir el paquete, luego navegar hasta el directorio y ejecutar:
./configure
make
sudo make install


Si se cuenta con Debian/Ubuntu, es inclusive más sencillo, ya que se encuentra en los repositorios, así que en este caso sólo sería cuestión de hacer un
sudo aptitude install stress



Uso

Su uso también es muy simple. Basta con indicarle algunos de los siguientes parámetros:
  • -c, --cpu N: genera N workers realizando operaciones de tipo sqrt()
  • -i, --io N: genera N workers realizando operaciones de tipo sync()
  • -m, --vm N: genera N workers realizando operaciones malloc()/free()
  • -d, --hdd N: genera N workers realizando operaciones write()/unlink()
Adicionalmente también podemos especificar si queremos que opere en modo verbose (-v, --verbose) o en modo quiet (-q, --quiet) y especificar algunas otras opciones que por supuesto podemos revisar en la documentación o en la ayuda.

Un ejemplo de uso puede ser el siguiente:
stress --cpu 2 --io 1 --vm 1 --timeout 10s --verbose


Aquí estaríamos especificando que cargaremos al sistema con: dos procesos relacionados con el procesador, un proceso relacionado con operaciones de entrada/salida y un proceso de asignación de memoria, que todo esto durará 10 segundos y que muestre salidas por cónsola.

viernes, 16 de marzo de 2012

Programación de Módulos para el Kernel de Linux



La programación de módulos para el Kernel de Linux es bastante parecida a la programación tradicional de aplicaciones en lenguaje C. Sin embargo, hay una gran cantidad de elementos a tener en cuenta, como por ejemplo que sólo se pueden utilizar un conjunto reducido de funciones públicas definidas en el kernel (no podemos utilizar la STL, por ejemplo), o que no se pueden realizar operaciones en punto flotante. Recomiendo leer "Understanding the Linux Kernel" de Daniel Bovet y Marco Cesati para profundizar en este tema.

Un módulo debe tener como mínimo dos funciones:
  • int init_module(): es la función que se ejecuta cuando se carga el módulo
  • void cleanup_module(): es la función que se ejecuta cuando se carga el módulo

El más básico de los módulos sería el siguiente:

#include <linux/module.h>  /* utilizada por todos los modulos */

int init_module()
{
    return 0;
}

void cleanup_module()
{

}

Este es un módulo perfectamente válido, pero que no haría absolutamente nada.

El valor de retorno de la función init_module() será cuando la carga del módulo sea exitosa. En caso de que la carga falle, el valor de retorno será -1 con errno igual a alguno de los siguientes valores:
  • EPERM: El usuario no es root
  • ENOENT: No existe ningún módulo con ese nombre
  • EINVAL: Argumentos no válidos
  • EBUSY: La rutina de inicialización del módulo falló
  • EFAULT: Tname o imagen fuera del espacio de direcciones accesible por el programa


Para personalizar un poco el módulo, podemos dar nombres a las funciones de carga/descarga, utilizando las macros module_init() y module_exit():

#include <linux/module.h>  /* utilizada por todos los modulos */
#include <linux/init.h>    /* utilizada para poder usar macros */

int init_func()
{
    return 0;
}

void exit_func()
{
}

module_init(init_func);
modue_exit(exit_func);

Para compilar nuestro módulo, podemos utilizar un Makefile como el siguiente (suponiendo que hallamos nombrado al archivo holamundo.c:

obj-m += holamundo.o
all:
 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

Luego, sólo ejecutamos
make
Y se generará un archivo holamundo.ko, correspondiente al código objeto del módulo, listo para ser cargado en el kernel. Nótese que se utiliza la opción -C para especificar que se genere código objeto.

Para cargar el módulo utilizamos el comando insmod (como root):
insmod ./holamundo.ko

Podemos ver la lista de módulos cargados en el kernel en /proc/modules o mediante el comando lsmod.

Para descargar el módulo, se utiliza el comando rmmod:
rmmod holamundo


La función printk()

Recordemos que un módulo del kernel no está hecho para interactuar con el usuario. Si queremos comunicar o dejar registro de lo que se hace dentro del módulo, podemos utilizar la función printk(), cuyo funcionamiento es similar al de printf(). printk() además permite especificar el tipo de mensaje que vamos a imprimir, el cual será uno de los siguientes:
  • KERN_EMERG
  • KERN_ALERT
  • KERN_CRIT
  • KERN_ERR
  • KERN_WARNING
  • KERN_NOTICE
  • KERN_INFO
  • KERN_DEBUG

Con printk() podemos mejorar nuestro primer módulo, para que deje un registro (ya veremos donde) al cargarse y descargarse el módulo:

#include <linux/module.h>  /* utilizada por todos los modulos */
#include <linux/init.h>    /* utilizada para poder usar macros */
#include <linux/kernel.h>  /* utilizada por printk */

int init_func()
{
    printk(KERN_INFO "holamundo: cargando...\n");
    return 0;
}

void exit_func()
{
    printk(KERN_INFO "holamundo: descargando...\n");
}

module_init(init_func);
modue_exit(exit_func);

Los logs generados por printk() se almacenan en /var/log/messages y en /var/log/syslog. En Ubuntu a partir de la versión 11.04 /var/log/messages se eliminó, por lo que en estos sistemas podemos ver los logs en syslog o modificando /etc/rsyslog.d/50-default.conf para activar /var/log/messages.



jueves, 15 de marzo de 2012

Administración de un LVS con ipvsadm


ipvsadm es una herramienta de línea de comando utilizada para la instalación, administración del LVS. La versión más reciente de ipvsadm fue liberada en febrero de 2011.

El comando tiene dos formas básicas de ejecución:

ipvsadm COMMAND [protocol] service-address
        [scheduling-method] [persistence options]

ipvsadm command [protocol] service-address
        server-address [packet-forwarding-method]
        [weight options]

La primera forma es para ser ejecutada en el nodo director y la segunda en los servidores reales. Una descripción detallada de los comandos y parámetros que acepta ipvsadm puede ser consultada aquí.


Comandos y parámetros

Entre las comandos más importantes, tenemos:
  • -A: permite agregar un servicio virtual
  • -C: limpia la tabla del lvs. Es importante un ipvsadm -C antes de iniciar una nueva configuración.
  • -a: permite agregar un servidor real a un servicio virtual.
  • -d: permite eliminar un servidor real de un servicio virtual.
En cuanto a los parámetros aceptados, están:
  • -t service-address: se utiliza para indicar que se agregará un servicio TCP. service-address debe ser especificada de la forma host[:port], siendo host una dirección IP o un hostname y port el número de puerto o el nombre del servicio (por ejemplo http).
  • -u service-address: igual que el anterior, pero indica que el servicio será UDP.
  • -s scheduling-method: se utiliza para especificar el algoritmo de planificación a utilizar. Los valores válidos para scheduling-method son:
    • rr (Round-Robin)
    • wrr (Weighted Round-Robin)
    • lc (Least-Connection)
    • wlc (Weighted Least-Connection)
    • lblc (Locality-Based Least-Connection)
    • lblcr (Locality-Based Least-Connection with Replication)
    • dh (Destination Hashing)
    • sh (Source Hashing)
    • sed (Shortest Expected Delay)
    • nq (Never Queue)
  • -r server-address: se utiliza para especificar la dirección ip de un servidor real, y opcionalmente puede agregarse un número de puerto.
  • -w weight: Se utiliza para especificar el peso de un servidor real. El peso es un valor numérico que indica la capacidad de procesamiento del servidor, relativa al conjunto de servidores. El rango de valores válidos de weight es [0, 65536]. El valor por omisión es 1. Un servidor con peso 5 indica que tiene mayor capacidad de procesamiento que un servidor con peso 2, y por tanto recibirá mayor número de peticiones.

Ejemplo práctico

Supongamos una red formada por un nodo director y 2 servidores reales con las siguientes direcciones IP:

La dirección IP que utilizaremos para el servicio virtual será: 192.168.122.110. Podemos configurar el LVS en el nodo director de la siguiente manera:

#clear ipvsadm table
ipvsadm -C

#especify the virtual service with Round-Robin algorithm
ipvsadm -A -t 192.168.122.110:http -s rr

#forward telnet to realserver 1 with weight 1
ipvsadm -a -t 192.168.122.110:http -r 192.168.122.225 -w 1

#forward telnet to realserver 2 with weight 3
ipvsadm -a -t 192.168.122.110:http -r 192.168.122.38 -w 3

martes, 31 de enero de 2012

Estructura de un Módulo de balanceo para LVS

De momento, en el LVS existen 10 algoritmos de balanceo de carga, de los cuales ya comenté brevemente. Voy a volver a listarlos, agregando el archivo en el cual se encuentran definidos:
  • Planificación por Round-Robin (Round-Robin Scheduling). Definido en: ip_vs_rr.c
  • Planificación por Round-Robin ponderado (Weighted Round-Robin  Scheduling). Definido en: ip_vs_wrr.c
  • Planificación por menor número de conexiones (Least-Connection Scheduling). Definido en: ip_vs_lc.c
  • Planificación por menor número de conexiones ponderado (Weighted Least-Connection Scheduling). Definido en: ip_vs_wlc.c
  • Planificación por menor número de conexiones local (Locality-Based Least-Connection Scheduling). Definido en: ip_vs_lblc.c
  • Planificación por menor número de conexiones local con réplicas (Locality-Based Least-Connection with Replication Scheduling). Definido en: ip_vs_lblcr.c
  • Planificación por hashing de destino (Destination Hashing Scheduling). Definido en: ip_vs_dh.c
  • Planificación por hashing de origen (Source Hashing Scheduling). Definido en: ip_vs_sh.c
  • Planificación por menor retardo esperado (Shortest Expected Delay Scheduling). Definido en: ip_vs_sed.c
  • Planificación por servidores sin peticiones en espera (Never Queue Scheduling). Definido en: ip_vs_nq.c


Cualquiera de estos algoritmos se puede escoger para realizar el balanceo en un sistema basado en LVS, a través de ipvsadm, la herramienta de administración para cónsola del LVS. Probablemente los más comunes sean el algoritmo de Round-Robin o el de Round-Robin ponderado (aunque no necesariamente sean los mejores para todas las situaciones).

Si queremos realizar nuestro propio algoritmo de balanceo, o queremos modificar alguno de los ya existentes, debemos empezar por estudiar la estructura que tienen o deben tener.

Recordemos que estos programas están diseñados para ser utilizados como módulos del kernel de Linux, por ello, todos deben tener al menos dos funciones:

static int __init_ip_vs_XXX_init(void);
Es la función que se ejecuta cuando se carga el módulo. Entre otras cosas, acá se registran los módulos.

static int __exit_ip_vs_XXX_cleanup(void);
Es la función que se ejecuta cuando se descarga el módulo.

Sobra decir que en las definiciones anteriores, XXX se sustituye por el nombre del módulo (rr, wrr, wlc, etc). Adicionalmente, en todo algoritmo de balanceo, se define un struct de tipo ip_vs_scheduler, en el cual se asignan las propiedades (atributos y funciones) que definen el comportamiento del módulo. La estructura de estos, es la siguiente:

struct ip_vs_scheduler {
 struct list_head n_list;  /* d-linked list head */
 char   *name;  /* scheduler name */
 atomic_t  refcnt;  /* reference counter */
 struct module  *module; /* THIS_MODULE/NULL */

 /* scheduler initializing service */
 int (*init_service)(struct ip_vs_service *svc);
 /* scheduling service finish */
 int (*done_service)(struct ip_vs_service *svc);
 /* scheduler updating service */
 int (*update_service)(struct ip_vs_service *svc);

 /* selecting a server from the given service */
 struct ip_vs_dest* (*schedule)(struct ip_vs_service *svc,
           const struct sk_buff *skb);
};

Las funciones que definen el comportamiento del algoritmo son:
  • init_service: se ejecuta en el momento en que se asocia un servicio con el algoritmo de planificación. Es opcional, no todos los algoritmos requieren realizar alguna tarea especial al iniciar el servicio. Ejemplo de uso: el algoritmo de round-robin, utiliza la función init_service para inicializar la data que utilizará para el balanceo (sched_data), con la lista de servidores reales actuales (destinations).
  • update_service: se ejecuta al actualizar o eliminar alguno de los destinos. Es opcional. Ejemplo de uso: el algoritmo de round-robin utiliza esta función para actualizar la data que utiliza para el balanceo (sched_data), con la lista de servidores reales actuales (destinations).
  • done_service: se ejecuta al finalizar el servicio. Es opcional. Ejemplo de uso: el algoritmo de round-robin ponderado utiliza la función done_service para liberar la memoria ocupada por la data utilizada para el balanceo (sched_data).
  • schedule: esta es la función en donde se define el algoritmo de balanceo per se. Es la parte más importante del módulo, y por supuesto no es opcional. Devuelve un apuntador a un objeto de tipo ip_vs_dest, que indica cuál es el servidor que el algoritmo escogió para responder la petición realizada por el cliente.
Por supuesto, las funciones que definamos para estas 4 opciones debemos especificarlas en el mismo archivo, o incluir el archivo de cabecera que las contenga. Lo mismo si estas funciones a su vez utilizan otras funciones.

Y bien, estos son todos los elementos que necesitamos conocer para escribir un algoritmo de balanceo o modificar alguno de los existentes. Recomiendo comenzar estudiando los algoritmos de balanceo por menor número de conexiones y por round-robin, pues son los más cortos (no pasa de 120 líneas ninguno de los dos) y sencillos de entender.


Linux Virtual Server


El Servidor Virtual de Linux (Linux Virtual Server o LVS) es una solución de balanceo de carga de alto rendimiento y alta disponibilidad, basada en software, que funciona sobre un cluster de servidores bajo el sistema operativo Linux. El funcionamiento del LVS es transparente al usuario, y además provee tolerancia a fallos, alta escalabilidad y confiabilidad.

La arquitectura del LVS está formada por un conjunto de servidores que se encargan de atender las peticiones de los visitantes, denominados servidores reales, y uno o más servidores, que se encargan de distribuir la carga entre los primeros, denominados directores. La conexión entre el(los) nodo(s) director(es) y los servidores reales puede ser a través de una red de área local (LAN) o una red de área amplia (WAN). En la siguiente figura se muestra esta arquitectura.


El LVS se puede implementar mediante dos mecanismos, ambos programados como un conjunto de módulos para el núcleo de Linux:

  • IP Virtual Server (IPVS): implementa el balanceo de carga a nivel de capa 4 del modelo OSI (capa de transporte). El IPVS se distribuye incorporado al Kernel de Linux, a partir de la versión 2.6.10, liberada el 24 de diciembre de 2004.
  • Kernel TCP Virtual Server (KTCPVS): implementa el balanceo de carga a nivel de la capa 7 del modelo OSI (capa de aplicación). La ventaja de realizar el balanceo en esta capa, es que se puede realizar la asignación de tareas a los servidores reales en base al contexto de las peticiones, sin embargo, por esta misma razón, resulta menos escalable que el IPVS.


Algoritmos nativos de planificación de tareas del LVS

Para distribuir la carga entre los servidores reales, el LVS implementa los siguientes algoritmos de planificación:

  • Planificación por Round-Robin (Round-Robin Scheduling).
  • Planificación por Round-Robin ponderado (Weighted Round-Robin  Scheduling).
  • Planificación por menor número de conexiones (Least-Connection Scheduling).
  • Planificación por menor número de conexiones ponderado (Weighted Least-Connection Scheduling).
  • Planificación por menor número de conexiones local (Locality-Based Least-Connection Scheduling).
  • Planificación por menor número de conexiones local con réplicas (Locality-Based Least-Connection with Replication Scheduling).
  • Planificación por hashing de destino (Destination Hashing Scheduling).
  • Planificación por hashing de origen (Source Hashing Scheduling).
  • Planificación por menor retardo esperado (Shortest Expected Delay Scheduling).
  • Planificación por servidores sin peticiones en espera (Never Queue Scheduling).


Los algoritmos de planificación por Round-Robin y Round-Robin ponderado son los más sencillos. El primero se basa en un esquema de Round-Robin tradicional: se mantiene una lista con los servidores reales, y las peticiones son asignadas, a medida que van llegando, de acuerdo al orden de la lista. En el algoritmo de Round-Robin ponderado, a cada servidor real se le asigna un peso (un valor entero que representa su capacidad de procesamiento), y las tareas son asignadas por el(los) nodo(s) director(es) de acuerdo a ese peso.

Los algoritmos de planificación Least-Connection y Weighted Least-Connection se basan en el número de conexiones activas. En el primero se entrega la petición al servidor con el menor número de conexiones activas establecidas. El segundo algoritmo se basa en el primero, pero pondera el número de conexiones activas de cada servidor real, por un peso asignado estáticamente, que busca representar la capacidad de procesamiento del servidor.

El algoritmo Locality-Based Least-Connection funciona de manera similar al algoritmo Least-Connection, pero mantiene una tabla caché por dirección IP destino; si el servidor solicitado se encuentra en la tabla caché y además no se encuentra sobrecargado (donde la sobrecarga significa que el servidor tenga más conexiones activas que el valor de su peso), le asignará la petición, en caso contrario, utilizará el algoritmo Weighted Least-Connection para seleccionar el servidor. Locality-Based Least-Connection with Replication, es muy parecido al anterior, pero en vez de tener un servidor por destino, mantiene un conjunto de servidores. Si ninguno de los servidores del conjunto está disponible, utiliza el algoritmo Weighted Least-Connection para seleccionar el servidor.

Los algoritmos por Hashing (destino y origen) designan el servidor que responderá la petición entrante, mediante una búsqueda en una tabla hash estática, por su IP destino o IP origen, según sea el caso. Si el servidor real se encuentra caído o sobrecargado (donde la sobrecarga significa que su número de conexiones activas es al menos el doble que el valor de su peso), entonces el director no asignará la tarea a ningún servidor y el usuario no obtendrá una respuesta satisfactoria.

El algoritmo por menor retardo esperado, asigna la petición al servidor que en ese momento tenga la menor demora esperada, definida esta para el i-ésimo servidor como $(Ci+1)/Ui$, donde Ci es el número de conexiones activas y Ui es el peso de ese servidor. El algoritmo por servidores sin peticiones en espera, si encuentra a un servidor inactivo (sin conexiones activas), le asignará la carga, en caso contrario, responderá como el algoritmo por menor retardo esperado.