Actualizar bash para evitar ataques Shellshock

Posted on 27 septiembre, 2014 Comments

Cuando vi el titular dije: “Debe tratarse de IE o algo con Windows…” pero cuando leí el contenido: WTF!! bash metido en esto? no puede ser!!

Aún no hay una solución precisa que resuelva el problema pero por lo pronto debemos actualizar bash “rait nau”. Para saber si tu sistema es vulnerable puedes ejecutar los siguientes comandos en consola:

Comando 1:

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

Si el comando muestra `vulnerable` entonces tu sistema lo es.

Comando 2:

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

Si el comando muestra una fecha, sigues siendo vulnerable.

Comando 3:

env -i X=' () { }; echo hello' bash -c 'date'

Si el comando anterior muestra `hello` sigues siendo vulnerable.

Como actualizar bash?

En Ubuntu/Debian: (solo para versiones LTS)

sudo apt-get update && sudo apt-get install --only-upgrade bash

En Centos/Redhat/Fedora:

sudo yum update bash

 

Esto hasta que publiquen una solución definitiva, sería buena idea realizar esas actualizaciones a diario hasta que la alerta roja desaparezca.

 

Tomado de: shellshocker

Consultas de referencias cruzadas en MySQL

Posted on 8 agosto, 2014 Comments

Hace algún tiempo que uso Redmine para gestionar proyectos, la herramienta en general es buena pero a veces necesitaba de reportes diarios de tiempo utilizado por los miembros del proyecto.

En este punto se me ocurrió si podría lograr una consulta de referencias cruzadas (al estilo MS Excel), MySQL no soporta consultas de ese tipo pero pueden simularse:

1. Escribir la consulta principal que contenga las filas más importantes: project identifier, task subject y total_hours, todo en un rango de fechas.

select p.identifier, 
	e.issue_id, i.subject, round(sum(e.hours),2) as total_hours
	from redmine_nsp.time_entries e
	inner join redmine_nsp.issues i on e.issue_id = i.id
	inner join redmine_nsp.projects p on e.project_id = p.id
	where e.spent_on between '2014-07-01' and '2014-07-07'
 group by p.identifier,e.issue_id;

Resultado:

+------------+----------+----------------------------+-------------+
| identifier | issue_id | subject                    | total_hours |
+------------+----------+----------------------------+-------------+
| bg02       |     3223 | Gestion de proyecto        |        0.25 |
| bg04       |     3256 | 1.1 Preparación del entor  |        0.63 |
| emision    |     3251 | Desarrollar la aplicación  |        3.97 |
| nsp00      |     3236 | Preparar propuesta para G  |        2.02 |
| nsp02      |     3234 | Subida al servidor de pro  |        0.52 |
| nsp02      |     3240 | Agregar funcionalidad de   |        0.55 |
| nsp02      |     3241 | Revertir el Documento      |        2.80 |
| nsp02      |     3242 | Agregar Filtros en las vi  |        0.72 |
| nsp02      |     3243 | Reportes de Cargo de entr  |        2.35 |
| nsp02      |     3254 | Control de sesiones        |        3.23 |
| nsp05      |     3252 | Mantenimiento del servido  |        0.18 |
| nsp05      |     3253 | Mantenimiento a redmine    |        0.53 |
| nsp06      |     3203 | Elaborar el visualzador d  |        0.23 |
| nsp06      |     3228 | Evitar que se tome mas de  |        0.25 |
| nsp06      |     3255 | Actualizar el porcentaje   |        1.50 |
| nsp08      |     3239 | Asistencia remota          |        1.38 |
+------------+----------+----------------------------+-------------+

2. Ahora queda averiguar quienes han estado activos en ese mismo rango de tiempo:

select e.user_id, u.login
from redmine_nsp.time_entries e
	inner join redmine_nsp.users u on e.user_id = u.id 
where e.spent_on between '2014-07-01' and '2014-07-07'
group by e.user_id;
+---------+----------+
| user_id | login    |
+---------+----------+
|       1 | user1    |
|       4 | user2    |
|       5 | user3    |
|       6 | user4    |
+---------+----------+

3. La tarea es insertar ese resultado como columnas del primer resultado, de tal modo que tengamos una consulta de referencias cruzadas. He creado un procedimiento (no explico mucho al respecto) que hace esa tarea recibiendo como parámetros las fechas:

DELIMITER $$

CREATE PROCEDURE `get_range_summary`(dDate1 date,dDate2 date)
BEGIN

DECLARE done INT DEFAULT 0;
declare p_user_id int;
declare p_sql text;
declare p_login varchar(255);
declare c_users cursor for
	select e.user_id, u.login
	from redmine_nsp.time_entries e
		inner join redmine_nsp.users u on e.user_id = u.id 
	where e.spent_on between  dDate1 and dDate2 
	group by e.user_id;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;

set p_sql = 'select s.codigo,s.issue_id,s.subject ';
open c_users;

read_loop: LOOP
	FETCH c_users INTO p_user_id, p_login;
    IF done THEN
      LEAVE read_loop;
    END IF;
	set p_sql = concat(p_sql,
      ', (select round(sum(t.hours),2) as total
		from redmine_nsp.time_entries t
		where t.spent_on between \'',dDate1,'\' and \'',dDate2,'\'  
        and t.issue_id = s.issue_id
		and t.user_id = ',p_user_id,') as `',p_login,'` ');

END LOOP;

set @sql = concat(p_sql,' ,s.total_hours from 
	(select p.identifier, e.issue_id, i.subject, 
         round(sum(e.hours),2) as total_hours
	from redmine_nsp.time_entries e
	inner join redmine_nsp.issues i on e.issue_id = i.id
	inner join redmine_nsp.projects p on e.project_id = p.id
	where e.spent_on between \'',dDate1,'\' and \'',dDate2,'\' group by p.identifier,e.issue_id) as s');


close c_users;

PREPARE stmt1 FROM @sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1; 

END

Al probar con la llamada: call get_range_summary(’2014-07-01′,’2014-07-07′):

+---------+----------+----------------------------+-------+--------+---------+----------+-------------+
| identif | issue_id | subject                    | user1 | user2  |  user3  |   user4  | total_hours |
+---------+----------+----------------------------+-------+--------+---------+----------+-------------+
| bg02    |       23 | Gestion de proyecto        |  NULL |   0.25 |    NULL |     NULL |        0.25 |
| bg04    |       56 | 1.1 Preparación del entor  |  NULL |   0.63 |    NULL |     NULL |        0.63 |
| emision |       51 | Desarrollar la aplicación  |  NULL |   NULL |    3.97 |     NULL |        3.97 |
| nsp00   |       36 | Preparar propuesta para G  |  NULL |   2.02 |    NULL |     NULL |        2.02 |
| nsp02   |       34 | Subida al servidor de pro  |  NULL |   0.52 |    NULL |     NULL |        0.52 |
| nsp02   |       40 | Agregar funcionalidad de   |  NULL |   NULL |    0.55 |     NULL |        0.55 |
| nsp02   |       41 | Revertir el Documento      |  1.40 |   NULL |    1.40 |     NULL |        2.80 |
| nsp02   |       42 | Agregar Filtros en las vi  |  NULL |   NULL |    0.72 |     NULL |        0.72 |
| nsp02   |       43 | Reportes de Cargo de entr  |  NULL |   NULL |    NULL |     2.35 |        2.35 |
| nsp02   |       54 | Control de sesiones        |  NULL |   NULL |    NULL |     3.23 |        3.23 |
| nsp05   |       52 | Mantenimiento del servidor |  NULL |   0.18 |    NULL |     NULL |        0.18 |
| nsp05   |       53 | Mantenimiento a redmine    |  NULL |   0.53 |    NULL |     NULL |        0.53 |
| nsp06   |        3 | Elaborar el visualzador de |  NULL |   0.23 |    NULL |     NULL |        0.23 |
| nsp06   |       28 | Evitar que se tome mas de  |  NULL |   0.25 |    NULL |     NULL |        0.25 |
| nsp06   |       55 | Actualizar el porcentaje d |  NULL |   1.50 |    NULL |     NULL |        1.50 |
| nsp08   |       39 | Asistencia remota          |  NULL |   1.38 |    NULL |     NULL |        1.38 |
+---------+----------+----------------------------+-------+--------+---------+----------+-------------+

Es todo! Luego con ese resultado es fácil crear un pequeño reporte diario/semanal sobre las horas utilizadas por cada usuario.

Acostumbrado?

Posted on 23 junio, 2014 Comments

Lo primero que me preguntan al saber vivo en Lima  un poco más de dos años es ¿Y ya te acostumbraste?

Para no alargar la conversación, les digo “Sí, ya me acostumbré” y me suelen decir después “Si pues, Lima es otra cosa”. Luego me digo a mi mismo “WTF!!!?”, lo que en realidad quiero responder es:

¿Acostumbrarme a caminar mirando en cada esquina si hay alguien sospechoso del cual deba cuidarme? ¿Acostumbrarme a no ver las estrellas ni las nubes blancas junto aun cielo con muchas tonalidades de azul? ¿A escuchar una historia peor que la anterior en los buses? ¿A sentir el hedor de todos los días? ¿A perder valiosas horas diarias para ir al trabajo? ¿A ver lo poco que vale la vida? ¿Acostumbrarme a no ver tantas cosas que no sabía que iba a extrañar? Cosas que que para mí eran “normales” y hasta vi con humor cuando alguien, recién llegado a mi pueblito, se quedó mirando el cielo diciendo “mira esas nubes ¿bien blancas no?”

Si pudiera mandar todo a la mierda y regresar… lo haría, pero todos tenemos una voz que nos dice “un momentito… !, basta de padecimientos infantiles, todos pasan por lo mismo y finalmente terminan acostumbrándose y no andan publicando cojudeces en sus blogs”.

El tiempo pasa y las cosas cambian.

Copia de seguridad de sqlite a la unidad sdcard en Android

Posted on 5 junio, 2013 Comments

Cuando ya tenemos una aplicación en producción y ocurre un problema causado por la base de datos suele ser complicado averiguar donde esta el problema, es buena idea tener una copia de la misma base de datos para ver exactamente que esta pasando.

Hace un tiempo que estaba buscando la manera de sacar un backup de la base de datos de una aplicación en Android, después de probar varias opciones encontré un segmento de código que sí ha funcionado:

public void backupdDatabase(){
    try {
	    File sd = Environment.getExternalStorageDirectory();
	    File data = Environment.getDataDirectory();
	    String packageName  = "com.yourapp.package";
	    String sourceDBName = "mydb.db";
	    String targetDBName = "mydb";
	    if (sd.canWrite()) {
	    	Date now = new Date();
		String currentDBPath = "data/" + packageName + "/databases/" + sourceDBName;
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm"); 
		String backupDBPath = targetDBName + dateFormat.format(now) + ".db";

		File currentDB = new File(data, currentDBPath);
		File backupDB = new File(sd, backupDBPath);

		Log.i("backup","backupDB=" + backupDB.getAbsolutePath());
		Log.i("backup","sourceDB=" + currentDB.getAbsolutePath());

		FileChannel src = new FileInputStream(currentDB).getChannel();
		FileChannel dst = new FileOutputStream(backupDB).getChannel();
		dst.transferFrom(src, 0, src.size());
		src.close();
		dst.close();
	    }
	} catch (Exception e) {
		Log.i("Backup", e.toString());
	}
}

Ustedes ya se encargan de cambiar los valores y mostrar los mensajes de error.

Me olvidaba que necesitarán del permiso para escribir en la memoria SD:

   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 

Obtenido de: Stackoverflow

 

Comer solo no es tan malo

Posted on 16 abril, 2013 Comments

Puede resultar muy extraño para la gran mayoría de ustedes, es complicado de explicar el por que no me agrada comer solo, no me refiero a comer unas papas fritas cerca a la computadora, me refiero a almorzar/cenar en un restaurante (incluso en casa).

Debo confesar que algunas veces usé la escusa de “no puedo comer solo” para ligar a alguien, pero en realidad es cierto… muy difícil para mi comer solo. Me asusta un poco la idea de estar sentado en la mesa de un restaurante comiendo solo, me cuesta mucho hacerlo, al punto que muchas veces he preferido no ir a comer –si no hay mucha hambre– o pedir para llevar y “tragar” en la privacidad de mi cuarto.

foreveralone

Siento el equivalente a ir a un bar, sentarse en la barra, pedir algo fuerte, tomar el trago sin conversar con nadie mas que tu conciencia y retirarte después de pagar la cuenta ¿Suena patético no?

Vivir solo, lejos de casa, de la familia y de los amigos mas entrañables hacen más difícil todo ya que no es tan fácil conseguir un acompañante, por mas delicioso que sea el menú no se come con gusto.

He tenido que aprender a convivir con esa fobia, manejar ese oscuro sentimiento, tragar las sensaciones que me aquejan para finalmente decir que comer solo no es tan malo como parece, muchos lo hacen y parece que no tienen problemas con ello.

No he superado el “problema” pero viviré. Tu que me estas leyendo podrás decir: “¿tengo muchos problemas y este tipo se hace un mundo para ir a comer?… JA!!!” pues sí… tal vez este post se vea patético, pero tenía que decirlo.

Buen provecho!

MySQL, el extraño caso de un campo timestamp

Posted on 9 enero, 2013 Comments

Hace un tiempo descubrí una característica (tal vez sea un bug) sobre los campos timestamp de MySQL. Es probable que este documentado en alguna parte que todavía no he leído:

Cuando se añade un campo timestamp a una tabla, MySQL agrega mágicamente algunas características al nuevo campo creado como un “trigger” y la fecha actual como valor por defecto.

Aquí esta el script donde se produce el caso:

-- CREANDO UNA TABLA CUALQUIERA E INSERTANDO DATOS 
mysql> create table t(
    -> id int not null primary key auto_increment,
    -> val varchar(50)
    -> );
Query OK, 0 rows affected (0.15 sec)

mysql> insert into t (val) values ("foo") ,("var");
Query OK, 2 rows affected (0.08 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from t;
+----+------+
| id | val  |
+----+------+
|  1 | foo  |
|  2 | var  |
+----+------+
2 rows in set (0.00 sec)


-- AGREGANDO UN CAMPO TIMESTAMP Y MAS DATOS
mysql> alter table t add ts_field timestamp;
Query OK, 2 rows affected (0.35 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into t (val) values ("foo 2") ,("var 2");
Query OK, 2 rows affected (0.06 sec)
Records: 2  Duplicates: 0  Warnings: 0

-- HE AQUI LA MAGIA:
mysql> select * from t;
+----+-------+---------------------+
| id | val   | ts_field            |
+----+-------+---------------------+
|  1 | foo   | 0000-00-00 00:00:00 |
|  2 | var   | 0000-00-00 00:00:00 |
|  3 | foo 2 | 2013-01-09 23:20:01 |    <---
|  4 | var 2 | 2013-01-09 23:20:01 |    <---
+----+-------+---------------------+
4 rows in set (0.00 sec)

¿¡Pero que acaba de pasar!?
no lo sé.

La nueva estructura de la tabla es:

CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `val` varchar(50) DEFAULT NULL,
  `ts_field` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1

Esto sólo pasa cuando en la tabla no existe otro campo timestamp aún.

Ahora, es útil? tal vez. Es un BUG? tal vez.

El "extraño caso" se repite en MySQL 5.1 y 5.5.

Update: Es una característica documentada en https://dev.mysql.com/doc/refman/5.5/en/timestamp-initialization.html , me disculpo por el post que finalmente resultó ser una chorrada, un lapsus, un horror.

MySQL Vista global de las bases de datos

Posted on 27 noviembre, 2012 Comments

Cansado de tener que averiguar manualmente  cuanto espacio ocupan mis bases de datos, acabo de crear un procedimiento almacenado para tener una vista global de las bases de datos que tenemos en nuestro servidor MySQL.

En MySQL no hay disponible  un comando que nos permita tener una resumen global de las bases de datos, algo que se parezca a SHOW TABLE STATUS pero para todas las bases de datos. El comando SHOW DATABASES sólo lista las bases actuales pero no nos dice cuanto espacio ocupan o cuantas tablas hay, etc.

El procedimiento que escribí esta basado en la base de datos INFORMATION_SCHEMA, que contiene mucha información de todas las bases de datos existentes.

Ya antes en un post anterior mencioné que podemos tener todas estas “herramientas” en una base de datos llamada tools, es por eso que recomiendo que éste procedimiento este en la misma base de datos tools (pero de todos modos puedes elegir otro lugar)

Al llamar al procedimiento si tenemos una vista global con información que nos será de utilidad para tas tareas de mantenimiento.

 

mysql> call tools.sp_overview();
+------------------------------+---------+--------+----------+---------+
| Database                     | Charset | Tables | Routines | Size Mb |
+------------------------------+---------+--------+----------+---------+
| database1                    | utf8    |     43 |       28 |     7.0 |
| database2                    | latin1  |     43 |       28 |   205.0 |
| database3                    | utf8    |    116 |        0 |   126.2 |
| database4                    | utf8    |     99 |        0 |     0.3 |
| database5                    | utf8    |    165 |        0 |    77.4 |
| database6                    | utf8    |    121 |        2 |   719.4 |
| database7                    | utf8    |    122 |        0 |    91.3 |
| database8                    | utf8    |    116 |        0 |    89.7 |
| database9                    | utf8    |    124 |        0 |     4.5 |
| database10                   | utf8    |    113 |        0 |   147.7 |
| database11                   | latin1  |    119 |        3 |   436.4 |
| database12                   | latin1  |    122 |        0 |   439.1 |
| database13                   | latin1  |    122 |        4 |   452.7 |
| database14                   | utf8    |    115 |        0 |   273.0 |
| database15                   | utf8    |    122 |        0 |   265.5 |
| database16                   | utf8    |      0 |        0 |    NULL |
| database17                   | latin1  |     26 |        0 |     0.9 |
| database18                   | latin1  |      9 |        0 |     0.0 |
| database19                   | latin1  |     22 |        0 |     0.3 |
+------------------------------+---------+--------+----------+---------+
19 rows in set (3.01 sec)

 

Advierto que la primera vez el procedimiento es muy lento, y puede demorar varios segundos.

Aquí tienen El código fuente del procedimiento:

 

DELIMITER $$

DROP PROCEDURE IF EXISTS tools.sp_overview$$
CREATE PROCEDURE tools.sp_overview()
BEGIN

    SELECT s.SCHEMA_NAME as `Database`, s.DEFAULT_CHARACTER_SET_NAME as `Charset`,
        count(t.TABLE_NAME) as `Tables`,

        (SELECT count(*) from information_schema.ROUTINES as r
            WHERE r.routine_schema = s.SCHEMA_NAME) as `Routines`,

         round(sum(t.DATA_LENGTH + t.INDEX_LENGTH) / 1048576 ,1) as `Size Mb`

        FROM information_schema.SCHEMATA AS s
            LEFT JOIN information_schema.TABLES t on s.schema_name = t.table_schema
        WHERE s.SCHEMA_NAME not in ('information_schema', 'performance_schema')

    GROUP BY s.SCHEMA_NAME;
END$$
DELIMITER ;

¿Estaremos condenados a tener hijas?

Posted on 18 agosto, 2012 Comments

Es la pregunta que me hice cuando caí en la cuenta de que casi todos mis “colegas” han tenido hijas, no es tan alarmante como parece, al parecer sólo 1 de cada 10 colegas han tenido hijos… un momento… O_o

…Antes que me lapiden, personalmente no creo que sea malo ser mujer o tener hijas, he dicho “condenados” en buena onda… así es que guárdense sus comentarios, ahora sigamos:

Mi muestra se limita a los pocos amigos que tengo, pero cuando me moví hacia otros círculos he notado una proporción similar y cuando lo comento con mis amigos también notan que algo raro está pasando ¿cuál será la causa?

No me atrevo a señalar una causa específica, pero podemos intuir sobre los presuntos culpables:

  1. El estrés, probablemente la principal causa, nosotros sabemos lo que significa tener encima la presión de terminar un proyecto en la fecha programada, de hecho el estrés afecta a todo nuestro organismo.
  2. Los equipos electrónicos con los que trabajamos día a día, sé que no hay un estudio definitivo sobre cuanto afecta a “nuestros muchachos” los celulares, laptops, puntos Wi-Fi, etc., pero estamos con ellos todo el tiempo e incluso muy cerca de la zona… el lugar donde… bueno el órgano este… los huevos pues no?
  3. Mala praxis, consultando con un ginecólogo, a éste le resulta muy fácil explicar la razón (combinado con los factores anteriores) y es el no conocer bien como funciona nuestro organismo.
  4. Tal ves sea que estemos “condenados” a ello, punto y se acabó.

Encontré unos manuales (ok no son manuales pero sirven como referencia) que resumen la larga conversación que tuve:

http://www.todopapas.com/fertilidad/salud-concebir/nino-o-nina-ahora-puedes-elegir-361

http://www.bbc.co.uk/mundo/noticias/2010/09/100929_fertilizacion_embarazo_men.shtml

http://es.answers.yahoo.com/question/index?qid=20070515130958AAUX60w

Lo sé hay, uno de Yahoo! Answers, que esperaban, me daba un poco de pereza escribir tanto, siempre consulten un especialista antes de hacer cualquier cosa con sus cuerpos.

Bueno, a practicar se ha dicho! Eso sí hay que tener mucha paciencia.

Un abrazo.

La App del Látigo

Posted on 2 agosto, 2012 Comments

Hace una semana que estoy fascinado con trollear  a mis amigos con la App del Látigo, lo vi por primera vez en un capítulo de The Big Bang Theory, es una App muy sencilla pero poderosa (si la sabes usar con sabiduría).

Es gratuita y esta disponible para Android (creo que hay versiones en la iStore):

 

The Whip en Google Play

 

Sólo tienen que mover el móvil como si fuera el látigo para que la App haga su trabajo: emitir el sonido del látigo junto a un mensaje motivador!

El autor de la App lo describe como:

The ultimate motivational tool.

The Whip: This app turns your Android-powered phone into the ultimate motivational tool. Useful whether asking your employees to work the weekend, or remarking on a friend’s relationship status…

 

Recomiendo que sólo lo usen en momentos que lo ameriten, por que pierde la gracia si lo usan constantemente.

Aquí un extracto del capítulo:

 

 

 

Migrar de MS Access a MySQL con relaciones

Posted on 16 marzo, 2012 Comments

He usado herramientas como MySQL Migration Toolkit (*) y Bullzip’s Access To MySQL, ambos hacen un excelente trabajo pero no exporta las relaciones entre tablas. Hacer esta tarea nos puede tomar varias horas (hasta ahora):

Escribí un código VBA para identificar las relaciones generando código MySQL con las sentencias de creación, esto puede ser muy útil después de la migración usando cualquier herramienta libre.

Option Explicit
'Copiar y pegar esta función en un módulo nuevo/existente de MS Access.

Public Sub printRelations()
    Dim sql, fk As String
    Dim i, j As Integer
    For i = 0 To CurrentDb.Relations.Count - 1
        sql = "ALTER TABLE `" & CurrentDb.Relations(i).ForeignTable & _
            "` ADD CONSTRAINT `" & CurrentDb.Relations(i).Name & "` FOREIGN KEY ("
        fk = "("
        For j = 0 To CurrentDb.Relations(i).Fields.Count - 1
            sql = sql & "`" & CurrentDb.Relations(i).Fields(j).ForeignName & "` ,"
            fk = fk & "`" & CurrentDb.Relations(i).Fields(j).Name & "` ,"
        Next j

        sql = Left(sql, Len(sql) - 1)
        fk = Left(fk, Len(fk) - 1)
        fk = fk & ")"
        sql = sql & ") REFERENCES `" & CurrentDb.Relations(i).Table & "`" & fk & ";"

        Debug.Print sql
    Next i
End Sub

Para ejecutar el código anterior, debemos invocarlo desde la ventana de Inmediato (Ctrl+G), luego solo tendremos que copiar el SQL generado para llevarlo a MySQL.

Enjoy!

(*)MySQL Migration Toolkit esta descontinuado pero aun sigue disponible desde los servidores Mirror de MysQL como:

http://mirrors.dotsrc.org/mysql/Downloads/MySQLGUITools/