lunes, 12 de diciembre de 2011

Chequear si se ha iniciado una sesión en PHP

Para iniciar una sesión en PHP, utilizamos la función session_start(), sin embargo, una práctica común (no necesariamente buena siempre) es forzar a que las sesiones se inicien automáticamente, modificando el archivo de configuración php.ini, especificamente haciendo session.auto_start = 1.

Bueno, el problema es que si ejecutamos session_start() y ya se encuentra una sesión activa (porque hayamos invocado anteriormente session_start() o porque se inicien automáticamente por nuestro php.ini), a partir de la versión 4.3.3 de PHP, se imprimirá un horrible mensaje tipo Notice:


Notice: A session had already been started - ignoring session_start()


Lo correcto es que invoquemos session_start() sólo si es necesario. Para ello, utilizaremos la función session_id(), la cual devuelve una cadena correspondiente al id de la sesión actual o una cadena vacía si no hay una sesión iniciado. Un posible uso sería:

<?php
if (strlen(session_id()) < 1)

  session_start();
?>



lunes, 5 de diciembre de 2011

Gestión de Máquinas Virtuales por Cónsola

En varias entradas anteriores he escrito un poco sobre máquinas virtuales en Linux, utilizando KVM/Qemu y Virt-Manager. En los últimos tiempos las he estado utilizando para simular una pequeña red de 4 computadores, en la que 3 de ellos son virtuales y se utilzan como un clúster de balanceo de carga (1 nodo director y 2 servidores reales).

El manejo de GUIs con Virt-Manager es realmente sencillo, más sin embargo, se desperdician muchos recursos si queremos tener las interfaces de 3 máquinas virtuales, para simplemente editar uno que otro archivo. En esta entrada, voy a mostrar algunos de los comandos virsh más comunes para la gestión de MVs mediante la consola.

Arrancar una Máquina Virtual es tan sencillo como:
virsh -c qemu:///system start nombre_maquina

Para arrancar automáticamente una MV al bootear:
virsh -c qemu:///system autostart nombre_maquina

Apagar una MV:
virsh -c qemu:///system shutdown nombre_maquina

Para listar las MVs que están corriendo en el sistema:
virsh -c qemu:///system list

Para reiniciar una MV:
virsh -c qemu:///system reboot nombre_maquina

Guardar el estado de una maquina virtual (y detenerla):
virsh -c qemu:///system save nombre_maquina nombre-20111205.state

Restaurar una MV a partir de un estado guardado:
virsh -c qemu:///sysem restore nombre-20111205.state


Con estos comandos es más que suficiente para realizar las tareas básicas de gestión. Y por supuesto, si lo que necesitamos es conectarnos a una cónsola para realizar alguna tarea sobre una MV, pues nada, ahí está el comando SSH ;)




miércoles, 16 de noviembre de 2011

jQuery: Cómo saber si un objeto pertenece a una clase

Supongamos que estamos iterando sobre una serie de elementos del DOM con jQuery y que realizaremos algunas operaciones dependiendo de la clase a la que pertenezca cada elemento. ¿Cómo saber si un elemento pertenece a una determinada clase?

Sencillo, utilizando la función is() de jQuery. Por ejemplo:

if ($(#elementoID).is('.nombreclase')) {
  alert('pertenece a la clase nombreclase');
} else {
  alert('No pertenece a la clase nombreclase');
}

Además, el argumento que recibe is(), puede ser básicamente cualquier expresión, por lo que podemos preguntar cosas como: is(":first-child"), is(":contains('Peter')"), etc.

Reseteando botones JQuery UI

Tenemos un pequeño formulario en el que incluímos algunos radios hechos con jQueryUI Button. Por alguna razón, en algún momento queremos resetear dichos radios (de manera que ninguno esté seleccionado o que esté seleccionado alguno en específico). Hacemos lo obvio: recorremos los radios y ponemos checked = false a todos (y opcionalmente checked = true al que queremos quede seleccionado). Acto seguido, nos damos cuenta que no sirve.

Más concretamente, el campo checked si se actualizará, pero no así la parte gráfica. Cada vez que cambiemos los Buttons de jQueryUI, necesitamos refrescarlos para que también se actualice la apariencia.

El método clave para esto es "refresh", que según la documentación oficial "Refreshes the visual state of the button. Useful for updating button state after the native element's checked or disabled state is changed programatically."

Invocarlo sobre cualquier botón es muy sencillo, por el ID, sería algo como:

  $('#botonId').button("refresh");


Por poner un segundo ejemplo, yo tengo una función en javascript para limpiar formularios que invoco después de hacer envío de formularios via Ajax. Para resetear todos los radios, sólo tendría que incluir en esta función las siguientes líneas:

$('input:radio').each(function() {
  $(this).button("refresh");
});


Vale, eso es todo. Sencillo

viernes, 7 de octubre de 2011

Jugando con KumbiaPHP


Aunque ya he trabajado con KumbiaPHP en este blog, me tomaré unas 2 o 3 entradas, a manera de tutorial para explicar algunos aspectos del framework.

KumbiaPHP es un framework sumamente sencillo de usar y sobre todo práctico, implementa el patrón MVC y es además muy liviano, comparado con otros frameworks populares.

Otras razones para utilizar KumbiaPHP: está escrito en español, por hispanohablantes y se caracteriza por ser muy muy rápido. Aquí un benchmarking para comparar rendimiento con algunos frameworks.

Lo primero que debemos hacer es descargar la librería. Al momento de escribir este tutorial, la última versión oficial es la v1.0 Spirit, que se puede descargar desde la web. Sin embargo, mi recomendación es uitlizar la versión de desarrollo, que se encuentra en una fase estable y contiene muchas mejoras con respecto a la v1.0. Esta última, podemos descargarla del repositorio en launchpad. Es con esta versión que trabajaré en lo sucesivo.

Bien, luego de descargar, descomprimimos el archivo en nuestro directorio web y obtendremos la siguiente estructura:

kumbiaphp/
|--core
|--default
|--.htaccess

Para verificar que todo vaya bien, en un navegador vamos a la url de nuestro site, y debería mostrarse la página de bienvenida de kumbia.

Ahora detallemos un poco los directorios que vienen por omisión.

En core encontraremos todos los archivos del framework. En core/vendors están todas las bibliotecas externas que utiliza kumbia (es aquí donde debemos colocar cualquier biblioteca que necesitemos), y en core/libs están las bibliotecas desarrolladas/mantenidas por kumbia.

Con la descarga, viene un directorio default, el cual contiene una aplicación que sería la aplicación por omisión. Al mismo nivel de default podemos crear todas las aplicaciones que querramos.

En lo sucesivo trabajaré con default, pero repito, podemos crear otros directorios si queremos. Dentro de default, encontramos 2 subdirectorios: default/app y default/public. En el primero irá el core de nuestra aplicación, mientras que en el segundo almacenaremos todos los recursos (imágenes, scripts, hojas de estilo, archivos flash, etc) que se utilizarán en las páginas webs de nuestra aplicación.

En default/app/config están los archivos de configuración de nuestra aplicación. En default/app/models, default/app/views y default/app/controllers estarán los modelos, vistas y controladores.

Dentro de views/_shared/templates, está la plantilla por defecto: default.phtml (se usará en todas las vistas en las cuales no especifiquemos otra plantilla). En view/_shared/templates podemos guardar todas las plantillas que queramos y utilizarlas cuando sea necesario.


Un poco sobre MVC

No se trata de un tutorial del patrón MVC, así que daré sólo los detalles necesarios. Cada controlador está implementado como un archivo con nombre nombre_controller.php en el subdirectorio app/controllers. Por ejemplo, si queremos un cotrolador "principal", crearemos un archivo principal_controller.php. Este archivo es básicamente una clase, de nombre nombreController, heredada de AppController, con una serie de métodos públicos, que serían las acciones de nuestro controlador.

Por cada controlador, debe existir un subdirectorio en app/views con el mismo nombre de nuestro controlador. Por cada acción debe existir un archivo con extensión .phtml en este subdirectorio. Por ejemplo, si nuestro controlador principal tiene las acciones bienvenida y despedida, en app/views crearemos un directorio principal, que debe contener los archivos bienvenida.phtml y despedida.phtml.

La siguiente imagen, tomada del wiki de kumbia muestra cual es la relación entre controladores/acciones y URLs:



Esto si core y default están en el directorio raiz del directorio web del servidor. En nuestro caso, no es así, sino que están dentro de otro directorio que creamos dentro del directorio web del servidor y que hemos llamado kumbiaphp, por lo que nuestras rutas serían del tipo http://dominio/kumbiaphp/controller/action.

Sin mayor preámbulo, creamos nuestro primer controlador (principal_controller.php):

<?php 
class PrincipalController extends AppController {
    public function bienvenida() 
    {
        $this->bienvenida = "Bienvenido a KumbiaPHP";
    }

    public function despedida() 
    {
        $this->despedida = "Vuelve pronto";
    }
}
?>

Nuestra primera vista (bienvenida.phtml):

<h1>KumbiaPHP</h1> 
<p><?php echo $bienvenida ?></p>

Y nuestra segunda vista (despedida.phtml):

<h1>KumbiaPHP</h1> 
<p><?php echo $despedida ?></p>

Ahora si en el navegador vamos a http://localhost/kumbiaphp/principal/bienvenidahttp://localhost/kumbiaphp/principal/despedida, veremos los mensajes correspondientes a cada vista.

Debemos notar que aunque en las vistas sólo hay un par de líneas, en el navegador se ve nuestro contenido, enmarcado en la plantilla por omisión (default.phtml). Podemos modificar o sustituir esta plantilla por una nuestra. Sólo debemos tener presente 2 cosas:
  • No debemos eliminar la línea <?php View::content(); ?>, pues esa es la instrucción que le dice al framework que muestre el contenido propio de la vista (y en qué lugar dentro de la plantilla hacerlo).
  • La sintaxis para la inserción de contenido, que explicaré a continuación.


Incluyendo contenido en una plantilla

Bien sea que sustituyamos default.phtml, o que creemos una nueva plantilla, tenemos que saber como se incluyen ciertos elementos en nuestras vistas, a través de los numerosos helpers que provee el framework.

Para incluir correctamente archivos css:

<?php Tag::css('wide-blue/style') ?>
<?php echo Html::includeCss() ?>

Esto supone que tenemos un archivo style.css, y está ubicado en el subdirectorio app/public/css/wide-blue.

Para incluir imágenes:

<?php echo Html::img("lvbp.jpg"); ?>

Para incluir enlaces:

<?php echo Html::linkAction("enlace", 'Titulo') ?>

Para que estos helpers funcionen, debemos recordar que debemos guardar las imágenes, css y javascripts en los directorios correspondientes dentro de app/public.

En la próxima entrega...

De momento, con esto es suficiente para que elaboremos una o más plantillas y dejemos el entorno a punto para trabajar. Ya sabemos que para enviar información del controlador a la vista, basta con definir en el último, una variables $this->nombre_variable y luego en la vista podremos accederla como $nombre_variable. No hemos hablado de modelos porque no ha sido necesario, lo haremos en la siguiente entrega, al igual que el pase de datos desde las vistas hacia los controladores.




miércoles, 5 de octubre de 2011

martes, 4 de octubre de 2011

Bloquear interfaz con jQuery

Sigo en la onda de jQuery, esta vez con un plugin muy útil cuando trabajamos con ajax.

Es una práctica muy común hoy en día, realizar solicitudes POST o GET mediante ajax, de esta forma no tenemos que recargar la página completa cada vez, sino una pequeña parte de la misma.


Seguramente hemos visto en algun sitio que cuando subimos una foto a un servidor o realizamos alguna consulta, toda la interfaz se bloquea y aparece en medio un mensaje que nos informa que la página está realizando alguna tarea, que esperemos.

Esto se hace por 2 motivos. El primero es informarle al usuario que ya se está procesando su solicitud; el segundo, que viene a ser consecuencia del primero, es evitar el doble envío de data. Si por ejemplo lo que el usuario está realizando es una búsqueda compleja y no se le informa que ya se está procesando, al pasar unos segundos, el usuario puede pensar que el navegador no "agarró" la orden y volver a presionar el botón de envío, e inclusive hacerlo varias veces al no ver respuesta. Peor aún, si se trata de una inserción en base de datos, la data podría estar insertándose varias veces, lo cual sería fatal.

Bloqueando la interfaz, resolvemos los 2 problemas: le informamos al usuario que su solicitud se está procesando y a la vez protejemos nuestra aplicación de múltiples envíos de data. Esto le da además un comportamiento síncrono a nuestras aplicaciones asíncronas.

¿Cómo lo hacemos? Bien, con un poco de manejo del DOM y CSS podemos lograr el efecto, sin embargo, para jQuery ya existen varios plugins para hacerlo de forma realmente simple. Uno de ellos, se llama BlockUI, y su uso es tan sencillo como incluir el plugin, por supuesto, y luego lo usamos de la siguiente forma.

Para bloquear la pantalla:

$.blockUI({ message: '<h1><img src="busy.gif" /> Por favor espere...</h1>' });

Para desbloquear la pantalla:

$.unblockUI();

Si queremos que la interfaz se bloquee, cada vez que hacemos una solicitud ajax y se desbloquee al terminarla:

$(document).ajaxStart($.blockUI).ajaxStop($.unblockUI);

Supongamos que tenemos un formulario para buscar datos de clientes y mostrarlos en una tabla. Con jQuery ponemos un manejador al submit del formulario, para que realice la búsqueda por POST via ajax e incruste el código HTML de la tabla en un DIV que tengamos destinado para tal fin, podemos bloquear/desbloquear la interfaz de la siguiente manera:

<script type="text/javascript">
$(function() {
	$('#formulario').submit(function() {
		var url = $(location).attr('href');
		var key = $('#keyword').val();

		$.blockUI({ message: '<h1><img src="busy.gif" /> Por favor espere...</h1>' });

		$.post( url,
		{ search_key: key },
			function(data){
			var datos = $('#divdatos');
			datos.html(data);
			$.unblockUI();
		});
		return false;
	});
});
^lt;/script>

Si queremos que la interfaz se desbloquee después de cierto tiempo, podemos hacerlo con la función setTimeout:

setTimeout($.unblockUI, 2000);


Como siempre, recomiendo ir directamente a la fuente y revisar todos los demos, que se pueden hacer muchas más cosas.

Formato de números en PHP

Trabajar con números y fechas es una de las cosas más fastidiosas que se le presentan a todo programador. El formato de los datos, la compatibilidad de formatos con manejadores de base de datos, etc. Sin embargo, casi en toda aplicación, toca.

Dependiendo del ambiente/plataforma en que trabajemos se pueden tener soluciones más o menos eficientes. Sin embargo, muchas veces se da el caso en que no podemos hacer mucho a nivel de archivos de configuración porque no tenemos acceso a ellos, o que simplemente la aplicación no requiere mayor trabajo.

En PHP, si lo que queremos es presentar algunos números que sacamos de base de datos o producto de ciertas operaciones matemáticas, por ejemplo, podemos utilizar la función number_format(), cuyo prototipo es el siguiente:

string number_format ( float $number [, int $decimals = 0 ] )
string number_format ( float $number , int $decimals = 0 , string $dec_point = '.' , string $thousands_sep = ',' )


Muy simple. Podemos simplemente decirle el número (o variable que contiene el número) y cuantos decimales queremos que muestre, y además podemos decirle qué separador decimal y de miles utilizar.

<?php

$number = 1234.56;

// notación inglesa (por defecto)
$english_format_number = number_format($number);
// 1,235

// notación inglesa con 3 decimales
$english_format_number2 = number_format($number, 3);
// 1,235.560

// notación francesa
$french_format_number = number_format($number, 2, ',', ' ');
// 1 234,56

// notación inglesa sin separador de miles
$english_format_number3 = number_format($number, 2, '.', '');
// 1234.57

?>

lunes, 3 de octubre de 2011

jQueryUI datepicker

En entradas anteriores he trabajado un poco con jQuery, una de las mejores bibliotecas JavaScript (y de las más fáciles de usar) que existen hoy en día. Todo el mundo debería utilizarla.

Todo el mundo debería utilizar también jQueryUI, una biblioteca de componentes gráficos/visuales para jQuery. Uno de los aspectos más destacables de jQuery/jQueryUI es el de la compatibilidad; cuantos dolores de cabeza te quita de entrada, al saber que cualquier componente que utilices, será compatible con los grandes navegadores.

En esta entrada, abordo uno de los widgets de jQueryUI: el datepicker. Muy elegante, funcional y sencillo de manipular. Incluir uno, es tan sencillo como colocar este código:

<script>
  $(function(){
    $("#datepicker").datepicker();
  });
</script>

<div class="demo">
<p>Date: </p>
</div>

Es sólo colocar un input de tipo texto, y con un sencillo script datepickficarlo. Lógicamente, nos gustaría especificar ciertas opciones, como poner los nombres en español, seleccionar alguna fecha por defecto, especificar el formato de la fecha, etc. Bien, todo esto (y más) es posible, gracias a la gran cantidad de opciones y métodos que provee el widget. Veamos un ejemplo más completo:

<script>
	$(function(){
		$("#datepicker").datepicker({
			autoSize: true,
			dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
			dayNamesMin: ['Dom', 'Lu', 'Ma', 'Mi', 'Je', 'Vi', 'Sa'],
			firstDay: 1,
			monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
			monthNamesShort: 'Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'],
			dateFormat: 'dd/mm/yy',
			changeMonth: true,
			changeYear: true});
 		                
	});
</script>

En este segundo ejemplo hemos agregado algunas opciones. Por ejemplo, hemos especificado los nombres de los días de la semana con dayNames y dayNamesShort, también hemos especificado los nombres de los meses con monthNames y monthNamesShort y hemos dicho que tanto los meses como los años se podrán cambiar a partir de listas desplegables, con changeMonth y changeYear.

Así podemos especificar muchas otras opciones. Es bueno ver la lista completa en la documentación oficial del widget aquí.

Cuando se despliega el calendario, vemos que por omisión aparece seleccionada la fecha actual (de la máquina en la que estamos). Esta es una opción que podemos cambiar, con la opción defaultDate. Por ejemplo, podríamos agregar algo como defaultDate: '03/10/2011' (debe ser consistente con el formato de fecha dateFormat) y siempre aparecerá seleccionada la fecha 3 de octubre de 2011. También, podemos especificar alguna otra fecha que obtengamos dinámicamente.

Sólo hay que estar claro en que con defaultDate especificamos la fecha que aparece por omisión al levantar el calendario, de ninguna manera esto significa que si no se selecciona alguna, esta opción nos dará la fecha por defecto.

Si lo que queremos es modificar la fecha actual, esto debemos hacerlo a través del método setDate, que podemos utilizar en cualquier momento, como cualquier otro elemento de jQuery. Agreguemos un evento setDate al script anterior:

<script>
	$(function(){
		$("#datepicker").datepicker({
			autoSize: true,
			dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
			dayNamesMin: ['Dom', 'Lu', 'Ma', 'Mi', 'Je', 'Vi', 'Sa'],
			firstDay: 1,
			monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
			monthNamesShort: 'Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'],
			dateFormat: 'dd/mm/yy',
			changeYear: true});

		var myDate = "'"+ <?php echo $_SESSION['fecha_hoy'] ?>+"'";
		$("#datepicker").datepicker('setDate', myDate);
 		                
	});
</script>

En este ejemplo, asumimos que estamos trabajando en PHP y que tenemos en sesión una variable 'fecha_hoy' y esa es la fecha que le asignamos al datepicker, justo después de su creación. Podemos utilizar el mismo método en cualquier otra parte del documento, por supuesto.

Otra opción interesante es disabled, la cual dice si el widget estará deshabilitado (true) o habilitado (false).

Si queremos que nuestro datepicker tenga una fecha por defecto y además esté bloqueado, debemos deshabilitar el widget posterior a que hemos asignado la fecha. Cierro la entrada, precisamente con este ejemplo:

<script>
	$(function(){
		$("#datepicker").datepicker({
			autoSize: true,
			dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
			dayNamesMin: ['Dom', 'Lu', 'Ma', 'Mi', 'Je', 'Vi', 'Sa'],
			firstDay: 1,
			monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
			monthNamesShort: 'Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'],
			dateFormat: 'dd/mm/yy',
			changeYear: true});

		var myDate = "'"+ <?php echo $_SESSION['fecha_hoy'] ?>+"'";
		$(\"#" . $field . "\").datepicker('setDate', myDate);
		$(\"#" . $field . "\").datepicker( 'option', 'disabled', true);
 		                
	});
</script>

Sólo agregamos una línea de código al final, que dice que la opción disabled a partir de ese momento, comienza a tener el valor true, por lo que no se podrá seleccionar ninguna otra fecha. Posteriormente, por supuesto, podemos habilitar el widget, volver a deshabilitarlo, volver a habilitarlo, etc. Eso sí, si esto es parte de un formulario y al momento de enviarlo, el widget está deshabilitado, también lo estará el input, y por tanto NO se enviará el campo, así que pendiente.

Espero la entrada haya sido de utilidad.

viernes, 30 de septiembre de 2011

Peticiones Ajax síncronas con jQuery

En la entrada pasada publiqué un código para asignar variables de sesión PHP desde jQuery con un POST enviado via Ajax. El código del script es el siguiente:

<script type="text/javascript">
  $(function() {
    $('.print_link').click(function() {
		var fixedurl = 'http://localhost/miapp/main/set_session_var';

		$.post( fixedurl,
		{ object_name: 'tmprow',
          id: $(this).attr('id'),
          monto: $(this).parent().parent().find('.monto').html()
		 },
			function(data){
			//donothing
		});

      return true;
    });
  });
</script>

Luego, en set_session_var (ya en PHP) se hacía el trabajo ordinario de asignar la variable de sesión. Hice la prueba con un ejemplo sencillo en que se tenían 2 vistas, test1 y test2, y cuando se hacía click a un enlace en test1 que llevaba a test2, se ejecutaba la función, justo antes de abandonar la página, de manera que ya en test2, "con suerte" se pudieran usar las variables recién asignadas.

He dicho "con suerte", porque, tratándose de Ajax y su asincronía, no tenemos la certeza de que cuando se cargue test2, ya las variables estén asignadas. El ejemplo mostrado era realmente sencillo, por lo que esto practicamente siempre se cumplía, pero luego comencé a hacer otras cosas en la función set_session_var, que hicieron que eventualmente, cuando cargara test2, aún no estaban mis variables de sesión.

¿La solución? Hacer el envío de forma síncrona, de manera que en el "return true;" garantice que ya se haya ejecutado el POST. Esto con $.post() de jQuery no se puede hacer, hay que utilizar $.ajax() directamente. Recordemos que $.post() es un atajo/simplificador de $.ajax para envío de POST asíncronos, el cual no permite especificar todas las opciones de $.ajax().

Bien, a $.ajax() podemos especificar la opción 'async' (que por omisión está en true, es decir, todo es enviado de forma asíncrona). Lo que tenemos que hacer es ponerla en false y ya. Es muy simple, en realidad. Dejo el código del script, sustituyendo el $.post() por $.ajax():

<script type="text/javascript">
  $(function() {
    $('.print_link').click(function() {
		var fixedurl = 'http://localhost/miapp/main/set_session_var';

		$.ajax({url: fixedurl,
		  type: 'POST',
		  async: false,
		  data: { object_name: 'tmprow',
           id: $(this).attr('id'),
           monto: $(this).parent().parent().find('.monto').html() }
		});
      return true;
    });
  });
</script>

Asignando variables de sesión PHP desde jQuery (con KumbiaPHP)

En esta entrada muestro un pequeño ejemplo en el que asignamos una variable de sesión PHP "dentro" de una función Javascript. ¿Para qué queremos esto? Pues para lo mismo que normalmente queremos asignar una variable de sesión, sólo que si estamos, por ejemplo, manejando un evento con jQuery, no estamos en PHP, estamos en Javascript, así que no vale un $_SESSION['clave'] = valor.

La solución que planteo aquí es la de enviar un POST via Ajax que se encargue de hacer la asignación. Simple. Voy a hacer uso de KumbiaPHP, con el cual ya he mostrado códigos anteriormente, pero en definitiva, el procedimiento es realmente simple e independiente del framework que utilicemos (si utilizamos alguno).

En este ejemplo, tengo una tabla con algunos datos sobre unas hipotéticas transacciones que presento al usuario, incluyendo un enlace a otra página. Será una tabla con 3 columnas: 1) tipo de transacción 2) monto de transacción y 3) enlace. Muestro el código HTML de la vista, llamada test1.phtml:

<table border="0" width="100%" class="grid-table" id="transacciones-table">
<thead>
  <tr>
    <th class="table-header-repeat line-left minwidth-1">Transacción</th>
    <th class="table-header-repeat line-left minwidth-1">Monto</th>
    <th class="table-header-repeat line-left minwidth-1">Detalle</th>
  </tr>
</thead>
<tbody>
  <tr class="row-1">
    <td class="descripcion">Apertura</td>
    <td class="monto">850.00</td>
    <td><a href="test2" class="print_link" id="671">imprimir</a></td>
  </tr>
  <tr class="row-2">
    <td class="descripcion">Intereses</td>
    <td class="monto">150.00</td>
    <td><a href="test2" class="print_link" id="686">imprimir</a></td>
  </tr>
  <tr class="row-1">
    <td class="descripcion">Intereses</td>
    <td class="monto">150.00</td>
    <td><a href="test2" class="print_link" id="778">imprimir</a></td>
  </tr>
  <tr class="row-2">
    <td class="descripcion">Cancelación</td>
    <td class="monto">850.00</td>
    <td><a href="test2" class="print_link" id="990">imprimir</a></td>
  </tr>
</tbody>
</table>

Como vemos una simple tabla con datos. Obsérvese que el anchor tiene un id y que el resto de las columnas tienen una clase representativa del dato que contienen. Queremos que cuando el usuario presione el enlace, el navegador se vaya a la vista test2.phtml para, por ejemplo, imprimir una planilla en pdf. En este caso, podríamos enviarle el valor del id de la fila que queremos imprimir y hacer una nueva consulta a la base de datos para pedir el resto de los datos, o podemos enviar un GET a test2 con los datos, para no tener que consultar, o alguna otra cosa. Pero como queremos es utilizar variables de sesión, pues eso haremos: con jQuery, crearemos una función para manejar el evento click de los enlaces, de manera tal de que justo antes de ir a test2, asignemos las variables de sesión y podamos luego utilizarlas sin problema. Es sólo cuestión de agregar la función a la vista test1.phtml, que quedaría de la siguiente forma:

<script type="text/javascript">
  $(function() {
    $('.print_link').click(function() {
		var fixedurl = 'http://localhost/miapp/main/set_session_var';
		$.post( fixedurl,
		{ object_name: 'tmprow',
          id: $(this).attr('id'),
          monto: $(this).parent().parent().find('.monto').html()
		 },
			function(data){
			//donothing
		});

      return true;
    });
  });
</script>

<table border="0" width="100%" class="grid-table" id="transacciones-table">
<thead>
  <tr>
    <th class="table-header-repeat line-left minwidth-1">Transacción</th>
    <th class="table-header-repeat line-left minwidth-1">Monto</th>
    <th class="table-header-repeat line-left minwidth-1">Detalle</th>
  </tr>
</thead>
<tbody>
  <tr class="row-1">
    <td class="descripcion">Apertura</td>
    <td class="monto">850.00</td>
    <td><a href="test2" class="print_link" id="671">imprimir</a></td>
  </tr>
  <tr class="row-2">
    <td class="descripcion">Intereses</td>
    <td class="monto">150.00</td>
    <td><a href="test2" class="print_link" id="686">imprimir</a></td>
  </tr>
  <tr class="row-1">
    <td class="descripcion">Intereses</td>
    <td class="monto">150.00</td>
    <td><a href="test2" class="print_link" id="778">imprimir</a></td>
  </tr>
  <tr class="row-2">
    <td class="descripcion">Cancelación</td>
    <td class="monto">850.00</td>
    <td><a href="test2" class="print_link" id="990">imprimir</a></td>
  </tr>
</tbody>
</table>

Muy simple y autoexplicativo (sobre todo para el que sabe jQuery). Simplemente declaramos un handler para el evento click de los objetos de clase "print_link", que envíe un POST via Ajax a la url especificada, con 3 datos: id (el id de la columna), monto (el monto de la transacción que sería el dato de la segunda columna) y object_name (con el nombre que le queremos asignar a la variable de sesión, 'tmprow' en este caso). Lo que queda es ver el controlador:

<?php

class MainController extends AppController
{

public function index()
{
	View::template('admin');
}

public function set_session_var()
	{
		View::template(null);
		$tmpname = '_object';
		foreach($_POST as $clave => $valor)
		{
			if($clave == 'object_name')
			{
				$tmpname = $valor;
				unset($_POST[$clave]);
			}
		}
		$_SESSION[$tmpname] = $_POST;
	}

	public function test1()
	{
		View::template('admin');
	}
	
	public function test2()
	{
		View::template('admin');
	}
}
?>

Y eso es todo, lo que acabamos de hacer es PHP básico, que no hace falta comentar. Ya podemos utilizar en test2 las variables de sesión que asignamos. Por ejemplo:

<p>En esta vista se puede hacer cualquier cosa con la información
que se guardó en sesión en test1.</p>

<p>El id de la transacción es: <?php echo $_SESSION['tmprow']['id'] ?></p>
<p>El monto de la transacción es: <?php echo $_SESSION['tmprow']['monto'] ?></p>

<p>Recuerda que siempre debes limpiar las variables de sesión cuando
ya no las utilices.</p>
<?php unset($_SESSION['tmprow']); ?>

Nótese que la función set_session_var() es reutilizada. Es decir, desde cualquier controlador, podemos enviarle un POST Ajax, con algunos datos y ella asignará nuestras variables de sesión. Se trata de un código sencillo pero que puede resultar muy útil.

Actualización
En esta entrada he mejorado el código, cambiando el $.post() por un $.ajax() para que la enviada sea síncrona, y así garantizar que en test2, ya tengamos nuestras variables con seguridad.

jueves, 8 de septiembre de 2011

Resaltado de código en blogger

Los que escribimos sobre programación, necesitamos una manera elegante y visualmente agradable de presentar nuestro código a los lectores. Yo, por ejemplo, alguna vez usé un pequeño script PHP que le pasaba mi código, y él me devolvía el HTML que resaltaba con colores palabras claves y otros elementos de PHP, C++, etc.

Últimamente he andado muy contento con JavaScript (con JQuery en realidad), y por casualidad llegó a mí SyntaxHighlighter, un resaltador de código fuente que utiliza exclusivamente javascript y css, y que se podría describir en una sola palabra: maravilloso. De verdad es una maravilla.

Es además muy muy sencillo de utilizar. Es cuestión de incluir algunos archivos javascript y css a nuestras páginas y luego encerrar nuestro código entre etiquetas PRE.

En este blog, dan indicaciones específicas para blogger. Básicamente lo que hay es que pegar justo antes de cerrar el header de nuestra plantilla, el siguiente código:

<link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/> 
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushRuby.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushVb.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'></script> 
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPerl.js' type='text/javascript'></script> 
<script language='javascript'> 
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf';
SyntaxHighlighter.all();
</script>
Sí, el código está resaltado, utilizando el resaltador. Luego, en nuestros posts, sólo tendremos que encerrar las porciones de código entre etiquetas PRE, especificando como clase "brush: alias", en donde alias es el nombre del lenguaje que se usará para resaltar. Por ejemplo, si queremos resaltar una función escrita en PHP, podemos utilizar:
<pre class="brush: php">
<?php 
public void Method($saludo) {
	$saludo .= " :)";
}
?>
</pre>
Sí, este código también fue resaltado utilizando el resaltador.Sencillamente maravilloso.
El único detalle que hay que tener en cuenta, es que debemos escapar todos los < (sustituirlos por el equivalente <). Yo hice un script que lo haga por mí, pero vamos que con un "replace all" en cualquier editor de texto, basta.

Sobre el reenvío de formularios via POST


A todo el mundo le ha pasado que en una aplicación web hay un formulario, la data se envía por POST, se realiza alguna acción con esa data, se muestra algo al usuario, el usuario pincha un link para ir a otra página del sitio, luego le da al botón Atrás y... ¡PUM! El navegador muestra al usuario ese horrible mensaje de que debe reenviar la información del formulario. Pasa en eztv.it, por ejemplo.

No es un bug de la aplicación, ni del navegador, pero es simplemente es desastroso para la imagen de nuestra aplicación frente al cliente.

Solución 1

Existen diversas formas de tratar el problema. La solución obvia, es no usar POST, sino GET. Particularmente no me gusta el método GET, pero si enviamos la data del formulario por esta via, no tendremos el problema, pues va en la propia URL.

Solución 2

Usar POST, pero con Ajax. Simple y elegante. Enviamos via AJAX nuestra mensaje POST a un método que nos devuelva el código HTML de la data que queremos y lo incrustamos en el DIV que tengamos destinados para mostrar la data. Con JQuery, por ejemplo hacer esto es realmente sencillo.

Ejemplo

Voy a mostrar un pequeño ejemplo, utilizando la librería KumbiaPHP con JQuery. Queremos una página para buscar (y listar) clientes de una tabla de clientes en BD. En la vista tenemos un campo input para que el usuario introduzca la palabra clave y un botón de submit

El controlador cliente_controller.php:

<?php
Load::models('cliente');

class ClienteController extends AppController
{
	public function index()
	{
		$cliente = new cliente();

		if(Input::hasPost('search_key'))
		{
			$search_key = Input::post('search_key');
			$this->clientes = $cliente->selectClienteByCedulaLike($search_key);
			View::select('p_clientes');
			View::template(null);
		}
		else
		{
			$this->clientes = null;
		}
	}
}
?>

La vista index.phtml:

<script type="text/javascript">
$(function() {
	$('#validateform').submit(function() {
		var url = $(location).attr('href');
		var key = $('#cedula').val();
		$.post( url,
		{ search_key: key },
			function(data){
			var capa = $('#tablecontainer');
			capa.html(data);
		});
		return false;
	});
});
</script>

<div id="content">

	<div id="page-heading"><h1>Gestión de Clientes</h1></div>

	<!--  start searchForm  -->
	<?php echo Form::open('','post','id="validateform"'); ?>
	<table>
	<tr>
	<td><?php echo Form::text('cedula','class="inp-form"') ?></td>
	<td><?php echo Form::submit('Buscar', 'class="form-search"') ?></td>
	</tr>
	</table>
	<?php echo Form::close() ?>
	<!--  end searchForm -->

	<div> </div>
	<div class="clear"></div>

	<!--  start client-table -->
	<div id="tablecontainer">
	<!--  Aqui es donde ira el contenido que luego incluiremos via Ajax -->
	</div>
	<!--  end client-table -->
</div>

La vista en la que crearemos la tabla p_clientes.phtml:

<table border="0" width="100%" cellpadding="0" cellspacing="0" id="client-table">
<tr>
	<th class="table-header-repeat line-left minwidth-1"><a href="">Cedula</a></th>
	<th class="table-header-repeat line-left minwidth-1"><a href="">Nombre</a></th>
	<th class="table-header-repeat line-left"><a href="">Telefono</a></th>
</tr>


<?php if($clientes != null) foreach ($clientes as $cliente) : ?>
<tr>
	<?php echo "<tr>"; ?>
	<td><?php echo $cliente->Cedula ?></td>
	<td><?php echo $cliente->nombre ?></td>
	<td><?php echo $cliente->telefono ?></td>
</tr>
<?php endforeach; ?>
</table>

Y eso es todo. Cada vez que el usuario de click al botón buscar, se ejecutará una petición POST con la palabra clave que introdujo el usuario, con el resultado de la búsqueda se construirá una tabla en la vista p_clientes.phtml, que será incrustada dentro del div "tablecontainer" de nuestra vista index.phtml.

No incluí el modelo, porque no lo considero relevante para lo que se quiere mostrar aquí.

Préstese especial atención al script que se encarga de sustituir la data. Es una simple función que se ejecuta cada vez que el usuario presiona el boton de enviar. Obsérvese además que esa función retorna false, pues no queremos que el post sea enviado tradicionalmente. Por supuesto, para mayor detalle, acudir a la documentación oficial de jQuery.


miércoles, 17 de agosto de 2011

GLUT en Visual Studio


Estoy trabajando en un proyecto en C++ con OpenGL bajo Linux. Por razones ajenas a mi voluntad me he visto obligado a trabajar últimamente en Windows, en el cual uso Visual Studio para codificar/compilar mis programas en C, C++, C#, etc.

Poner a correr un proyecto con GLUT en Windows, no es muy complicado, sin embargo, quita tiempo, así que dejaré aquí la receta para no perder tiempo la próxima vez. La mayor parte de la información la saqué de esta web. Personalmente los pasos que describo a continuación los he probado tanto en Visual Studio 2008, como en Visual C++ 2008 Express y Visual C++ 2010 Express.

GLUT como tal, es un proyecto viejo y abandonado. freeglut es un proyecto basado en GLUT, con un desarrollo activo, por lo que generalmente se instalan ambas librerías.

Básicamente, lo que tenemos que hacer es copiar los archivos de cabecera (.h), las librerías (.lib) y las dlls (.dll) correspondientes (los dejo todos al final para descargar). Vamos paso a paso.


1) Copiar los archivos de cabecera

freeglut.h
freeglut_ext.h
freeglut_std.h
glut.h

Se deben copiar en el siguiente directorio:

Para MS Visual C++ 2008:
C:\Archivos de programa\Microsoft SDKs\Windows\v6.0A\include\gl\

Para MS Visual C++ 2010:
C:\Archivos de programa\Microsoft SDKs\Windows\v7.0A\include\gl\



2) Copiar las librerías

freeglut.lib
glut32.lib

Se deben copiar en el siguiente directorio:

Para MS Visual C++ 2008:
C:\Archivos de programa\Microsoft SDKs\Windows\v6.0A\Lib\

Para MS Visual C++ 2010:
C:\Archivos de programa\Microsoft SDKs\Windows\v7.0A\Lib\


3) Copiar las dll

freeglut.dll
glut32.dll

Se deben copiar en el siguiente directorio

Para Sistema Operativo de 32bits:
C:\Windows\system32\

Para Sistema Operativo de 64bits:
C:\Windows\SysWow64\


4) Usar GLUT

Crear un nuevo proyecto de tipo Win32 Console Application (y en opciones, seleccionar "Empty Project").

Abrir Project > Properties

Ir a Configuration Properties > Linker > Input

En Additional Dependencies, colocar:
freeglut.lib
glut32.lib


Fin

Y eso es todo, ya podemos compilar nuestros programas con glut sin problemas.

Paquete con todos los archivos necesarios: descargar

lunes, 8 de agosto de 2011

Configurar Apache detrás de un router TP-LINK TL-WR642G


Estoy desarrollando una pequeña aplicación web, sobre un servidor Apache, en una máquina con Windows XP, que se encuentra dentro de la red local de mi casa, la cual funciona con un AP TP-LINK WR642G.

Y bueno, para mostrarle la aplicación al cliente, lo que voy a hacer es configurar el router para que reenvie todas las peticiones públicas, a la dirección IP local de la máquina que tiene la aplicación. Simple.

Antes de comenzar hay que tener en cuenta que muchos ISPs bloquean los puertos conocidos (específicamente el 80, utilizado tradicionalmente para HTTP), tal es el caso de ABA en Venezuela. En este caso (es el mío), hay que configurar nuestro Apache para que permita conexiones a través de otros puertos (básicamente cualquiera por encima del 1024 está bien).

En el archivo httpd.conf, si no le hemos metido mucha mano, debemos tener un "Listen 80". Sólo basta agregar otra línea (antes o después de ésta, da igual) "Listen 1067" por ejemplo, si queremos que apache también escuche en el puerto 1067. Si tenemos una configuración más compleja, con Virtual Hosts por ejemplo, pues tendremos que hacer las modificaciones según sea nuestro caso. Para que los cambios surtan efecto, debemos reiniciar el servicio de Apache.

Luego sólo hacer falta ir a nuestro router y configurarlo para que reenvíe todas las peticiones a la máquina que tiene nuestro Apache. Esto se hace entrando a la configuración de nuestro router (http://192.168.1.1 es la dirección por omsión), y luego ir a Advanced Settings > Forwarding > Virtual Servers, y configurarlo de la siguiente manera:


No es necesario reiniciar el router. La configuración debería tomar efecto de inmediato.

Consideraciones adicionales

Debemos tener en cuenta muchas otras cosas. Menciono algunas.

La configuración del firewall de la máquina: lógicamente, si en el firewall tenemos bloqueado Apache o tenemos bloqueado el puerto, no podremos acceder.

La configuración del firewall del router: misma historia con el firewall del router.

La dirección IP de la máquina: si trabajamos bajo DHCP, eventualmente la máquina con el servidor Apache podría cambiar de dirección IP local y el router estará reenviando las peticiones web a la dirección IP antigua, que ya no se encontrará. Lo más lógico es que nuestra máquina servidor tenga dirección IP estática y/o tener el ARP binding configurado en el router, para que sólo asigne la dirección IP a la máquina servidor.


martes, 2 de agosto de 2011

Cambiar el nombre del pc en Ubuntu


Luego de algunas pruebas con máquinas virtuales, utilizando KVM, para ahorrar trabajo en la creación de los sistemas operativos invitados, comencé a utilizar la opción de clonar una VM, que trae el virt-manager (que por cierto, hace su trabajo impecablemente, valga la propaganda).

Un inconveniente al clonar las VMs, es que, lógicamente, la VM clonada queda con el mismo hostname y usuario(s) de la VM original. Cambiar el hostname es algo realmente sencillo, dejo acá los pasos, a manera de receta.

Lo primero es cambiar el hostname, con el comando hostname:

hostname nuevo_hostname

Luego, hay que editar el archivo /etc/hostname para colocar nuestro nuevo_hostname.

Finalmente editar /etc/hosts para que 127.0.0.1 apunte al nuevo_hostname.



miércoles, 29 de junio de 2011

Sockets en C (II)

En la entrada pasada describí (de forma relativamente informal) el modelo cliente-servidor y las funciones involucradas en la programación de Sockets bajo Linux, tanto en el caso de sockets orientados a conexión, como en el caso de sockets no orientados a conexión.

En esta entrada voy a hacer una presentación un poco más formal y voy a desarrollar un ejemplo de socket utilizando UDP. Lo aquí mostrado está basado en este tutorial y los apuntes de mis clases de Redes.

Comenzamos por describir los archivos de cabecera involucrados:

1) types.h
Contiene las definiciones de una gran cantidad de tipos de datos utilizados en llamadas al sistema, en particular, algunos utilizados en los siguientes archivos de cabecera.

2) socket.h
Incluye todas las estructuras de datos necesarias para el trabajo con sockets.

3) in.h
Contiene constantes y estructuras necesarias para trabajar con direcciones de Internet.

Entre las estructuras de datos utilizadas, es destacable sockaddr_in, cuya definición es la siguiente:

struct sockaddr_in
{
short sin_family; /* must be AF_INET */
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8]; /* Not used, must be zero */
};

Los objetos tipo sockaddr_in se utilizan par almacenar direcciones de Internet. Como veremos en el código, necesitaremos lógicamente 2 variables de este tipo: una para almacenar la dirección del servidor y una para la dirección del cliente.


El caso de estudio

Tenemos un sistema de balanceo de carga, en el que por X razón, nos interesa que el nodo director conozca en todo momento el estado de carga de los servidores reales. En una entrada pasada, estudiamos el método utilizado para el cálculo de la carga del sistema en Linux, cuyo valor podemos leer en el archivo /proc/loadavg.

Con esto en mente, lo que planeo hacer a continuación es:
1) Un proceso para los servidores reales que cada cierto tiempo envíe a través de un socket UDP su carga al nodo director.
2) Un proceso en el nodo director que esté siempre en espera de la data enviada por los servidores reales y la almacene en un log.


Procedimiento para los servidores reales

Este procedimiento es realmente sencillo, básicamente hace 3 cosas:
1) Abrir el socket UDP
2) En un proceso de n iteraciones lee el archivo /proc/loadavg y envía lo leído a través del socket
3) Envía una señal de parada al nodo remoto y cierra el socket

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#define tam_buffer 100
#define NAME_FILE "///proc//loadavg"

int main( int argc, char *argv[] )
{

FILE *file_load;
char *d, line_load[tam_buffer];
double OneMin, FiveMins, FifteenMins;
int i = 0, socket_udp, tam, num_bytes, time_sleep;
struct sockaddr_in addressip;

int n_packets = 0, curr_packets = 0;

/* validation input arguments */
if( argc != 5 )
{
printf(">:ERROR: Use mode: ./realsv IP PORT TIME N_PACKETS\n");
exit( -1 );
}
time_sleep = atoi(argv[3]);
n_packets = atoi(argv[4]);

/* init socket */
addressip.sin_family = AF_INET;
addressip.sin_port = htons( atoi( argv[2] ) );
inet_aton( argv[1], &(addressip.sin_addr) );
bzero( &(addressip.sin_zero), 8 );
socket_udp = socket(AF_INET, SOCK_DGRAM, 0);

tam = sizeof(struct sockaddr);

do
{
if((file_load = fopen(NAME_FILE,"r")) == NULL )
{
fprintf(stderr,"Can't open the file, %s \n",NAME_FILE);
exit(1);
}
else
{
if(feof(file_load))
{
fclose (file_load);
break;
}

d = fgets(line_load,100,file_load);
sscanf(line_load,"%lf %lf %lf", &OneMin, &FiveMins,
&FifteenMins);
// Send load to lvs
num_bytes = sendto(socket_udp, (double *)&OneMin,
sizeof( OneMin ), 0,
(struct sockaddr *)&addressip, tam );

i++;
printf("\n>: Send No: %d", i);
printf("\n>: bytes send: %d", num_bytes);
printf("\n>: load last minute: %f \n",OneMin);
// Close the file
fclose (file_load);
sleep(time_sleep);
}
curr_packets++;
}while(curr_packets < n_packets);

// Send STOP Signal:
OneMin = -1.0;
num_bytes = sendto(socket_udp, (double *)&OneMin,
sizeof( OneMin ), 0,
(struct sockaddr *)&addressip, tam );

// Close the connection:
printf("\n>: Close conection.");
close(socket_udp);

return 0;
}

El código se explica solo, sin embargo haré algunas notas.
  • El procedimiento toma como argumentos: la IP del nodo remoto, el puerto del nodo remoto, el tiempo (en segundos) que indica cada cuanto se debe enviar un paquete y el número de paquetes que se deben enviar.
  • Se utiliza la función sleep() como temporizador para el envío de los paquetes.
  • Se utiliza AF_INET para indicar que se trata de un socket que funciona en red.
  • Se utiliza la función htons() para evitar problemas "entendimiento" entre máquinas, pues todos los procesadores no representan los número de la misma forma (algunos lo hacen con el byte más significativo a la izquierda, otros a la derecha).
  • Es importante comprender los argumentos que se pasan a sendto().
  • Cuando ya se han enviado los n paquetes especificados por línea de comando, se rompe el bucle, se envía un paquete con -1 para que el nodo remoto (nodo director) sepa que ya hemos terminado, y finalmente se cierra el socket.

Procedimiento para el nodo director

Ahora continuaremos con el procedimiento que se debe ejecutar en el nodo director, el cual es tanto o más sencillo que el anterior. En el nodo director, simplemente estaremos a la espera de los datos enviados por el socket y mostraremos por pantalla cada vez que recibamos alguno.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define tam_load 8
#define tam_ip 20

int main(int argc,char *argv[])
{

struct sockaddr_in iprealsv;
char addressip[tam_ip];
double buffer;
int socket_udp, tam, numbytes = 1, n = 0, interval = 1;

/* validation input arguments */
if(argc != 3)
{
printf("\n>:ERROR: Use mode: ./lvsrcp PORT TIME \n");
exit( -1 );
}

/* init socket */
iprealsv.sin_family = AF_INET;
iprealsv.sin_port = htons( atoi(argv[1]) );
iprealsv.sin_addr.s_addr = INADDR_ANY;
bzero( &(iprealsv.sin_zero), 8 );

socket_udp = socket( AF_INET, SOCK_DGRAM, 0 );
tam = sizeof( struct sockaddr );

if ((bind( socket_udp, (struct sockaddr *)&iprealsv, tam )) == -1)
printf("\n>:ERROR: bind function \n");

while (1)
{
n++;
numbytes = recvfrom(socket_udp, (double *)&buffer,
sizeof(buffer), 0,
(struct sockaddr *)&iprealsv, (socklen_t *)&tam);
printf(">: Package received #: %d \n", n);
printf(">: bytes received: %d \n", numbytes);
printf(">: load real server: %f \n", buffer);
strcpy(addressip, inet_ntoa(iprealsv.sin_addr));
addressip[strlen(addressip)] = '\0';
printf(">: real_server: address IP :%s and load: %f \n", addressip, buffer);

if(buffer < 0)
break;
}

printf("\n>: Finished connection. \n");
/* close connection */
close( socket_udp );
return 0;
}

Muy sencillo el código. Simplemente abrimos el socket, tal como en el primer programa, y luego creamos un bucle infinito para esperar a que lleguen los datos. Cuando el programa recibe un paquete cuyo dato es -1, se rompe el bucle, se cierra el socket y se termina el programa.

Es destacable notar que ambas máquinas deben saber qué tipo de datos están enviando/recibiendo; en este caso el único dato que se está enviando es un valor real. Si el paquete está formado por varios datos, lógicamente tendremos que pasar por el proceso de armar el dato compuesto (seguramente en una cadena) del lado del cliente y luego desarmar el dato en el lado del servidor.


martes, 28 de junio de 2011

lunes, 27 de junio de 2011

/proc/loadavg

El archivo /proc/loadavg contiene información acerca de la carga del sistema. Específicamente, los siguientes 5 indicadores:
  1. Promedio de procesos activos en el último minuto
  2. Promedio de procesos activos en los últimos 5 minutos
  3. Promedio de procesos activos en los últimos 15 minutos
  4. Procesos que estan planificados para ejecutarse/total de procesos
  5. Process ID del proceso más recientemente ejecutado
Los 3 primeros indicadores, son en los que nos centraremos en esta entrada. Ellos representan los promedios aproximados exponenciales de la carga del sistema. Mientras mayores sean estos valores, más cargado se encuentra la máquina. Estos promedios son los mostrados por diversos comandos de Linux como uptime, w, top y procinfo.


Forma de cálculo

La fórmula para calcular el valor actual de la carga es la de medias móviles amortiguadas exponencialmente, utilizada ampliamente en Estadística y Economía:

load(t) = load(t-1) * EXP_M + (1 - EXP) * x

En donde:
  • x = número de procesos activos (procesos que están en cola para ser ejecutados)
  • EXP_M = 1/exp(5seg/(60seg*m)), siendo m = 1, 5 y 15 (minutos)
Para 1 minuto:
EXP_1 = 1 / exp(5 seg/1 min) = 1/exp(1/12) =~ 0.91

Para 5 minutos:
EXP_5 = 1 / exp(5 seg/5 min) = 1/exp(1/60) =~ 0.98

Para 15 minutos:
EXP_15 = 1 / exp(5 seg/15 min) = 1/exp(1/180) =~ 0.99

El valor de 5seg involucrado en el cálculo de EXP_M tiene que ver con la frecuencia de cálculo, que es de 5HZ, lo cual implica que la función de cálculo se ejecuta cada 5000ms o 5seg.

Cuanto más grande es EXP_M, la historia "permanece" por más tiempo. Se puede decir que la historia tiene un "valor de decaimiento" de 0.91 para el primer caso, 0.98 para el segundo y 0.99 para el tercero.

Una buena discusión del uso de medias móviles ponderadas exponencialmente y su comparación con otro tipo de medias, se puede leer aquí.


Implementación en Linux

En realidad, estando el kernel de Linux ampliamente optimizado, en vez de representar los valores de EXP_M en punto flotante, se representan en punto fijo, pues los cálculos se realizan en el espacio de kernel y no en espacio de usuario.

La función de cálculo de la carga se encuentra definida en <kernel/timer.c> y su prototipo es:
static inline void calc_load(unsigned long ticks)
Esta función, internamente hace uso de la macro CALC_LOAD(load, exp, n), definida en <linux/sched.h&gt.

La función que imprime los valores de carga en /proc/loadavg se encuentra definida en linux/fs/proc/proc_misc.c, y su prototipo es:
static int loadavg_read_proc(char *page, char **start, off_t off,
                 int count, int *eof, void *data) 
Esta función está constantemente ejecutándose en el sistema, y es así como cada vez que ejecutamos el comando uptime (por ejemplo) podemos acceder a estos valores.


Más información

No es mucha la información que se consigue sobre este tema en la red. Sin embargo dejo un par de enlaces útiles:

BULMA: ¿Cómo se calculas los promedios de loadavg en Linux?. link
TeamQuest: UNIX Load Average. link

jueves, 23 de junio de 2011

Creando un repositorio SVN (Linux)




En esta entrada voy a explicar paso a paso, como crear un repositorio subversion. En mi caso particular, instalo el repositorio en la máquina que hace de servidor en mi casa (en la cual muchas veces codifico), la cual tiene Ubuntu 11.04, y a la cual me voy a conectar para hacer commits/updates desde al menos otras 2 máquinas frecuentemente.

Requisitos

Asumo que se tiene un servidor Apache instalado, puesto que utilizaremos este servidor web para conectarnos via http a nuestros repositorios.

Paso a paso

1) Instalar subversion y el módulo svn para apache.

En mi caso, subversion ya venía instalado por defecto cuando instalé Ubuntu, sin embargo, de no estarlo, es lo primero que debemos hacer:

sudo aptitude install subversion

Luego, instalar el módulo svn para apache:

sudo aptitude install libapache2-svn

2) Grupos y permisología

Esto no es estrictamente necesario, pero 100% recomendable por cuestiones de seguridad. Crearemos un grupo, al cual agregaremos nuestro usuario y el usuario de Apache (www-data):

sudo addgroup svn
sudo usermod -a -G svn marco
sudo usermod -a -G svn www-data

Lo siguiente que debemos crear es el archivo de acceso al repositorio. Así crearemos todos los usuarios que accederán al repositorio:
sudo htpasswd -c /etc/apache2/dav_svn.passwd marco
sudo htpasswd /etc/apache2/dav_svn.passwd otro_usuario

Obsérvese que a partir del segundo usuario, no se pasa el parámetro -c, puesto que ya dav_svn.passwd se encuentra creado. Por cada usuario nos pedirá una contraseña, la cual no tiene por qué coincidir con la contraseña del correspondiente usuario en el sistema (en caso de existir).

3) Modificamos el archivo de configuración

Como root, debemos editar el archivo /etc/apache2/mods-enabled/dav_svn.conf para especificar nuestros datos de configuración. El archivo se explica solo, sin embargo dejo las líneas que me interesan:

<Location /svn>

DAV svn

SVNParentPath /var/lib/svn

AuthType Basic
AuthName "Subversion Repository"
AuthUserFile /etc/apache2/dav_svn.passwd

<LimitExcept GET PROPFIND OPTIONS REPORT>

Require valid-user
</LimitExcept>

</Location>


Ahí especifico la ruta por la cual quiero acceder a los repositorios (en este caso http://servidor/svn), la ruta física donde se almacenarán todos los repositorios (/var/lib/svn), digo que quiero utilizar autenticación básica y especifico la ruta de dav_svn.passwd.

4) Reiniciar el servidor Apache.

sudo /etc/init.d/apache2 stop
sudo /etc/init.d/apache2 start


5) Creamos el directorio padre para nuestros repositorios
sudo mkdir /var/lib/svn

6) Agregar proyectos

sudo svnadmin create /var/lib/svn/myproject
sudo svn mkdir -m "crear la estructura" file:///var/lib/svn/myproject/trunk file:///var/lib/svn/myproject/tags file:///var/lib/svn/myproject/branches

sudo chown -R www-data:svn /var/lib/svn/myproject/
sudo chmod -R g+rws /var/lib/svn/myproject/

7) Comenzar a trabajar

Entramos al directorio donde tengamos los archivos actuales de nuestro proyecto (puede ser en la misma máquina u otra) y hacemos un checkout:

svn co http://server.name/svn/myproject/trunk .

o en el caso de que estemos en la misma máquina, podemos utilizar:

svn co http://localhost/svn/myproject/trunk .

Agregamos lo que tengamos que agregar (por ejemplo con un svn add *) y hacemos el import inicial:

sudo svn import -m "iniciando" . file:///var/lib/svn/myproject/trunk


Para hacer el import inicial, utilicé "file:///", esto fue porque estaba trabajando en el servidor directamente. Si en cambio hubiese estado en otra de las máquinas de la red, debí utilizar http y la dirección IP del servidor.

Y eso es todo. Así como creamos el proyecto "myproject" podemos crear todos los que queramos. El resto es simplemente trabajar y utilizar los comandos svn que utilizamos habitualmente.


Más información

doc.ubuntu-es: Subversion