Ejecución remota de comandos contra máquinas Unix desde .Net
Estoy involucrado en un interesante proyecto que tiene por objetivo demostrar que VSTS for Tester es una plataforma interesante a la hora de testear arquitecturas no centradas en plataforma Microsoft, arquitecturas muy heterogéneas. Un primer post relacionado este proyecto fue: Pruebas web de Team System usando Firefox. Y seguro que habrá más.
Uno de los primeros pasos que tengo que dar es obtener de manera remota información del comportamiento, desde el punto de vista del rendimiento, de una máquina Unix/Linux. La familia de los Unix cuenta con una serie de comandos que nos permiten saber como se está comportando la máquina, el más relevante de ellos quizás sea vmstat que da información sobre varios aspectos relacionados con el rendimiento. El afán final es poder exponer información de máquinas Unix como contadores de rendimiento de Windows para poderlos recolectar desde pruebas de carga de VSTS, ya que esta es la única fuente de información sobre el rendimiento que Team System integra en las pruebas (aunque es probable que esto cambie con Rosario)... pero no llegaré tan lejos hoy, hoy solo voy a comentar como ejecutar comandos remotamente desde .Net (en concreto con C#) contra máquinas Unix.
La manera habitual de ejecutar comandos Unix de manera remota es utilizar ssh (Secure Shell). En la siguiente imagen podéis ver el resultado de ejecutar el comando vmstat utilizando PuTTY como cliente ssh contra una máquina Linux(corriendo Fedora) desde una máquina Windows:
Ahora la pregunta es obvia, ¿existe alguna manera de ejecutar un comando usando ssh desde código .net y recoger la salida del mismo? La respuesta es sí gracias a SharpSSH una librería de código abierto creada para este cometido. No cuenta con mucha documentación pero su uso es simple, a menos para lo que yo necesito. Basta poner una referencia a la libreria Tamir.SharpSSH.dll y utilizar la clase SshExec para ejecutar el comando. Os dejo el código comentado a continuación:
//Establecemos la conexión SSH
const string host = "192.168.1.65";
SshExec sshExec = new SshExec(host, "root", "pass+w0rd");
sshExec.Connect();
//Usamos el comando vmstat para obtener la información de rendimiento
//Usamos el comando sed para quedarnos solo con la línea que nos interesa
//de la salida de vmstat
string result = sshExec.RunCommand(@"vmstat 1 2 | sed '4!d'");
//vmstat da una salida en columnas separadas por espacios
//obtenemos el valor de cada una de esas columnas
Regex re = new Regex(@"(\d*?)\s+");
MatchCollection matches = re.Matches(result);
//Mostramos por consola los resultado junto con una descripción
Console.WriteLine("-------Host: {0}----------", host);
Console.WriteLine("-----------Procs----------");
Console.WriteLine("Number of processes waiting for run time: {0}", matches[1]);
Console.WriteLine("Number of processes in uninterruptible sleep: {0}", matches[2]);
Console.WriteLine("-----------Memory----------");
Console.WriteLine("Amount of virtual memory used: {0}", matches[3]);
Console.WriteLine("Amount of free memory: {0}", matches[4]);
Console.WriteLine("Amount of memory used as buffers: {0}", matches[5]);
Console.WriteLine("Amount of memory used as cache: {0}", matches[ 6]);
Console.WriteLine("-----------Swap----------");
Console.WriteLine("Amount of memory swapped in from disk (/s): {0}", matches[7]);
Console.WriteLine("Amount of memory swapped to disk (/s): {0}", matches[ 8]);
Console.WriteLine("-----------IO----------");
Console.WriteLine("Blocks received from a block device (blocks/s): {0}", matches[9]);
Console.WriteLine("Blocks sent to a block device (blocks/s): {0}", matches[10]);
Console.WriteLine("-----------System----------");
Console.WriteLine("Number of interrupts per second, including the clock: {0}", matches[11]);
Console.WriteLine("Number of context switches per second: {0}", matches[12]);
Console.WriteLine("-----------CPU----------");
Console.WriteLine("Time spent running non-kernel code. (user time, including nice time): {0}", matches[13]);
Console.WriteLine("Time spent running kernel code. (system time): {0}", matches[14]);
Console.WriteLine("Time spent idle: {0}", matches[15]);
Console.WriteLine("Time spent waiting for IO: {0}", matches[16]);
Console.WriteLine("Time stolen from a virtual machine: {0}", matches[17]);
La salida de la ejecución del este programa es la siguiente:
Con lo que doy por cumplido mi objetivo, al menos de momento... os seguiré contando mis avances.