miércoles, 11 de abril de 2012

Error ORA-09817 y sistemas de archivos llenos

Digamos que estás intentando ingresar a tu base de datos y de pronto obtienes este mensaje de error:
oracle@mydb$ sqlplus '/ as sysdba'

SQL*Plus: Release 11.1.0.7.0 - Production on Mon Feb 13 15:47:27 2012

Copyright (c) 1982, 2008, Oracle.  All rights reserved.

ERROR:
ORA-09817: Write to audit file failed.
SVR4 Error: 28: No space left on device
ORA-01075: you are currently logged on


Enter user-name: 

oracle@mydb$ 

Este es un problema fácil de adivinar y la clave está en el mensaje No space left on device. La instancia no puede escribir archivos de auditoría porque el sistema de archivos está lleno, por lo que tienes que averiguar por qué ese sistema de archivos está lleno:
oracle@mydb$ set | grep ORA
ORACLE_BASE=/oracle
ORACLE_HOME=/oracle/orahome
ORACLE_SID=mydb

oracle@mydb$ strings $ORACLE_HOME/dbs/*mydb.ora | grep audit_file_dest
*.audit_file_dest='/oracle/admin/mydb/adump'

oracle@mydb$ df -k | egrep '(Filesystem)|(/oracle)'
Filesystem                 kbytes    used   avail capacity  Mounted on
/dev/vx/dsk/orafs/oracle 16327680 16327680       0   100%    /oracle

En este caso el destino de los archivos de auditoría esta en el sistema de archivos base de Oracle, por lo tanto el problema podría estar en otro directorio:
oracle@mydb$ du -k /oracle | sort -nr | head 
16035094        /oracle
8412062 /oracle/diag
8297523 /oracle/diag/rdbms/mydb/mydb
8297523 /oracle/diag/rdbms/mydb
8297523 /oracle/diag/rdbms
8250027 /oracle/diag/rdbms/mydb/mydb/trace
6926099 /oracle/orahome
1631834 /oracle/orahome/mydirectory
938085  /oracle/orahome/mydirectory/somefiles
727017  /oracle/orahome/bin

oracle@mydb$ du -ka /oracle/diag/rdbms/mydb/mydb/trace | sort -nr | head
8250027 /oracle/diag/rdbms/mydb/mydb/trace
5737533 /oracle/diag/rdbms/mydb/mydb/trace/mydb_ora_5198.trc
2506955 /oracle/diag/rdbms/mydb/mydb/trace/mydb_ora_18305.trc
1946    /oracle/diag/rdbms/mydb/mydb/trace/mydb_ora_5198.trm
969     /oracle/diag/rdbms/mydb/mydb/trace/mydb_ora_18305.trm
457     /oracle/diag/rdbms/mydb/mydb/trace/mydb_ora_3695.trc
380     /oracle/diag/rdbms/mydb/mydb/trace/alert_mydb.log
314     /oracle/diag/rdbms/mydb/mydb/trace/mydb_j002_8800.trc
314     /oracle/diag/rdbms/mydb/mydb/trace/mydb_j002_13295.trc
314     /oracle/diag/rdbms/mydb/mydb/trace/mydb_j000_23311.trc

Aquí está el problema: hay dos archivos muy grandes de trazado que ocupan 8 gigabytes, la mitad del tamaño del sistema de archivos. Cuando averiguas por qué tu sistema de archivos está lleno tienes que decidir qué hacer, ya sea borrar, mover o comprimir archivos, sólo asegúrate de no desechar archivos que podrías necesitar.

lunes, 9 de abril de 2012

Creando tablas particionadas y error ORA-00922

Hay muchas versiones diferentes del software de Oracle Database y aún si la lógica y operaciones de la base de datos es más o menos la misma entre versiones, también es más o menos diferente entre versiones; por ejemplo, no hay sentencia drop database en Oracle 9i.

Entonces tu trabajas usualmente con bases de datos 11g y te encantan las tablas particionadas por intervalos, y creas una:
oracle@my11gdb~$ sqlplus '/ as sysdba'

SQL*Plus: Release 11.2.0.3.0 Production on Wed Feb 8 12:43:42 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> CREATE TABLE MY_TABLE
(   
SOMETEXT     VARCHAR2(4000),
MOREINFO     VARCHAR2(256),
ADATE        DATE
) PARTITION BY RANGE (ADATE)
INTERVAL (NUMTODSINTERVAL(1,'day'))
(partition p0 values less than (to_date('01-jan-2000','dd-mon-yyyy'))
);

Table created.

E intentas crear esa misma tabla en una base de datos 10g sólo para encontrar que no puedes:
oracle@my10gdb~$ sqlplus '/ as sysdba'

SQL*Plus: Release 10.2.0.4.0 - Production on Wed Feb 8 12:46:15 2012

Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, OLAP, Data Mining
and Real Application Testing options

SQL> CREATE TABLE MY_TABLE
(   
SOMETEXT     VARCHAR2(4000),
MOREINFO     VARCHAR2(256),
ADATE        DATE
) PARTITION BY RANGE (ADATE)
INTERVAL (NUMTODSINTERVAL(1,'day'))
(partition p0 values less than (to_date('01-jan-2000','dd-mon-yyyy'))
);
INTERVAL (NUMTODSINTERVAL(1,'day'))
*
ERROR at line 7:
ORA-00922: missing or invalid option

No busques más: el error ORA-00922 en una base de datos 10g significa que estás intentando usar una funcionalidad que no existe; en una base de datos 11g podría significar falta de paréntesis o errores de sintaxis.
oracle@my11gdb~$ sqlplus '/ as sysdba'

SQL*Plus: Release 11.2.0.3.0 Production on Wed Feb 8 12:50:54 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> CREATE TABLE MY_TABLE
(   
SOMETEXT     VARCHAR2(4000),
MOREINFO     VARCHAR2(256),
ADATE        DATE
) PARTITION BY RANGE (ADATE)
INTERVAL (NUMTODSINTERVAL(1,'day'))
partition p0 values less than (to_date('01-jan-2000','dd-mon-yyyy'));
partition p0 values less than (to_date('01-jan-2000','dd-mon-yyyy'))
*
ERROR at line 8:
ORA-00922: missing or invalid option

Más información:

CREATE TABLE (10g Release 2)
CREATE TABLE (11g Release 1)
Partition Administration

domingo, 8 de abril de 2012

Sumando numeros en un archivo

Si tienes un pequeño archivo con números y estás encargado de sumarlos, puedes hacerlo con bc y paste:
cronos@olimpo:~$ cat file.txt 
1|11
2|22
3|33
4|44
5|55
|
6|66
7|77
8|88
9|99

cronos@olimpo:~$ cat file.txt | cut -d '|' -f 1 | grep -v '^$' | paste -sd '+' - | bc
45

Con cut seleccionas el campo a sumar, con grep deseleccionas campos vacíos, con paste agregas al final de cada línea un signo +, y con bc haces la suma.

Pero si tienes un archivo muy grande esto no va a funcionar; puedes mejor usar awk:
cronos@olimpo:~$ cat file.txt | awk 'BEGIN {FS="|";OFMT="%.2f"} {a+=$1;b+=$2} END {print "First field:",a,"Second field:",b}'

First field: 45 Second field: 495

Con FS configuras el delimitador de campo y con OMFT configuras el formato del numero, en este caso dos decimales sin notación científica. Con awk un bonito extra es poder agregar dos o más columnas al mismo tiempo.

Más información:

Sum of numbers in file - UNIX alternatives

sábado, 7 de abril de 2012

Bloqueando estadísticas de tablas

Tener estadísticas exactas de objetos de bases de datos es obligatorio para obtener la mejor trayectoria de ejecución, y usualmente puedes recabar estadísticas usando el paquete DBMS_STATS sin problemas. Pero algunas veces tienes tablas especiales que necesitan tratamiento especial, como tablas "temporales" (tablas normales que son llenadas y sus datos modificados muy rápido) que pueden cambiar mucho el plan de ejecución de una sentencia SQL y volverte loco tratando de averiguar la causa raíz de esos cambios del plan de ejecución.

Como demostración de este comportamiento crearemos dos tablas, una que cambiará mucho (test1) y otra que es más o menos estática (test2):
SQL> create table test1 (id number primary key, id_char varchar2(15));

Table created.

SQL> declare
mynum number := 1;
begin
while mynum < 10 loop
  insert into test1 values(mynum,to_char(mynum));
  mynum := mynum + 1;
  end loop;
end;
/

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

SQL> create table test2 (id number primary key, id_char varchar2(15));

Table created.

SQL> declare
mynum number := 1;
begin
while mynum < 100000 loop
  insert into test2 values(mynum,to_char(mynum));
  mynum := mynum + 1;
  end loop;
end;
/

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

Al principio la tabla test1 tiene 9 registros, y queremos unir las tablas test1 y test2 para obtener algunos registros, pero primero recopilaremos estadísticas de test1:
SQL> BEGIN DBMS_STATS.GATHER_TABLE_STATS( OWNNAME => 'MYUSER', TABNAME => 'TEST1'); END;
  2  /

PL/SQL procedure successfully completed.

SQL> select a.ID, b.ID_CHAR from test1 a join test2 b on (a.ID=b.ID) where a.ID between 4 and 7;

 ID         ID_CHAR
---------- ---------------
 4          4
 5          5
 6          6
 7          7

A continuación obtendremos el plan de ejecución actual para esta pequeña sentencia:
SQL> EXPLAIN PLAN FOR
select a.ID, b.ID_CHAR from test1 a join test2 b on (a.ID=b.ID) where a.ID between 4 and 7;

Explained.

SQL> @$ORACLE_HOME/rdbms/admin/utlxpls.sql

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3677731951

--------------------------------------------------------------------------------------------
| Id  | Operation                    | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |             |  4    | 56    |  4   (0)   | 00:00:01 |
|   1 |  NESTED LOOPS                |             |  4    | 56    |  4   (0)   | 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| TEST2       |  5    | 55    |  4   (0)   | 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | SYS_C004858 |  5    |       |  3   (0)   | 00:00:01 |
|*  4 |   INDEX UNIQUE SCAN          | SYS_C004857 |  1    |  3    |  0   (0)   | 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("B"."ID">=4 AND "B"."ID"<=7)
   4 - access("A"."ID"="B"."ID")
       filter("A"."ID">=4 AND "A"."ID"<=7)

18 rows selected.

Este plan de ejecución se ve bien y realmente no importa mucho ya que obtuvimos sólo unos cuantos registros, pero espera! Nueve registros no es el tamaño típico de la tabla test1; ahora crearemos más registros:
SQL> delete from test1;

9 rows deleted.

SQL> declare
mynum number := 1;
begin
while mynum < 10000 loop
  insert into test1 values(mynum,to_char(mynum));
  mynum := mynum + 1;
  end loop;
end;
/

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

Podrías pensar que no hay mucha diferencia teniendo diez o diez mil registros en test1, pero el optimizador de Oracle no piensa lo mismo y esta vez el plan de ejecución es diferente:
SQL> EXPLAIN PLAN FOR
  2  select a.ID, b.ID_CHAR from test1 a join test2 b on (a.ID=b.ID) where a.ID between 4000 and 7000;

Explained.

SQL> @$ORACLE_HOME/rdbms/admin/utlxpls.sql

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2418654178

--------------------------------------------------------------------------------------------
| Id  | Operation                    | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |             |  1    | 14    |  3   (0)   | 00:00:01 |
|   1 |  NESTED LOOPS                |             |       |       |            |          |
|   2 |   NESTED LOOPS               |             |  1    | 14    |  3   (0)   | 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | SYS_C004857 |  1    |  3    |  1   (0)   | 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN         | SYS_C004858 |  1    |       |  1   (0)   | 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| TEST2       |  1    | 11    |  2   (0)   | 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("A"."ID">=4000 AND "A"."ID"<=7000)
   4 - access("A"."ID"="B"."ID")
       filter("B"."ID"<=7000 AND "B"."ID">=4000)

19 rows selected.

Podrías pensar que recuperar cuatro registros es diferente de recuperar cuatro mil y es correcto, pero el optimizador escogió el plan de ejecución basado en datos incorrectos; ve la cantidad de registros con los que Oracle piensa que va a trabajar.

Lo verás claramente actualizando las estadísticas de la tabla test1 y obteniendo la nueva trayectoria de ejecución:
SQL> BEGIN DBMS_STATS.GATHER_TABLE_STATS( OWNNAME => 'SYS', TABNAME => 'TEST1'); END;
  2  /

PL/SQL procedure successfully completed.

SQL> EXPLAIN PLAN FOR
select a.ID, b.ID_CHAR from test1 a join test2 b on (a.ID=b.ID) where a.ID between 4000 and 7000;

Explained.

SQL> @$ORACLE_HOME/rdbms/admin/utlxpls.sql

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3677731951

--------------------------------------------------------------------------------------------
| Id  | Operation                    | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |             |  3001 | 45015 | 17  (12)   | 00:00:01 |
|   1 |  NESTED LOOPS                |             |  3001 | 45015 | 17  (12)   | 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| TEST2       |  3002 | 33022 | 15   (0)   | 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | SYS_C004858 |  3002 |       |  8   (0)   | 00:00:01 |
|*  4 |   INDEX UNIQUE SCAN          | SYS_C004857 |  1    |  4    |  0   (0)   | 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("B"."ID">=4000 AND "B"."ID"<=7000)
   4 - access("A"."ID"="B"."ID")
       filter("A"."ID">=4000 AND "A"."ID"<=7000)

18 rows selected.

Puedes ver el problema? Teniendo que tratar con este tipo de tablas puedes: usar hints para forzar la ejecución óptima de la sentencia SQL, refrescar estadísticas cada vez que cambies una tabla como esta, o bloquear las estadísticas cuando tengas un tamaño típico de tabla o un plan de ejecución óptimo.

Consideraremos que en este punto la tabla test1 tiene un tamaño típico y sus estadísticas ayudan al optimizador a escoger un plan de ejecución óptimo, por lo tanto bloquearemos las estadísticas de esta forma:
SQL> exec DBMS_STATS.LOCK_TABLE_STATS ('MYUSER', 'TEST1');

PL/SQL procedure successfully completed.

Ahora volveremos a poner información en la tabla test1 con 9 registros:
SQL> delete from TEST1;

9999 rows deleted.

SQL> declare
mynum number := 1;
begin
while mynum < 10 loop
  insert into test1 values(mynum,to_char(mynum));
  mynum := mynum + 1;
  end loop;
end;
/

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

SQL> BEGIN DBMS_STATS.GATHER_TABLE_STATS( OWNNAME => 'MYUSER', TABNAME => 'TEST1'); END;
  2  /
BEGIN DBMS_STATS.GATHER_TABLE_STATS( OWNNAME => 'MYUSER', TABNAME => 'TEST1'); END;
*
ERROR at line 1:
ORA-20005: object statistics are locked (stattype = ALL)
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1

Ya que las estadísticas de la tabla test1 fueron bloqueadas no puedes recopilar nuevas estadísticas a menos que desbloquees las estadísticas de la tabla. De cualquier forma, el punto de hacer esto es ayudar al optimizador a escoger un buen plan de ejecución:
SQL> EXPLAIN PLAN FOR
select a.ID, b.ID_CHAR from test1 a join test2 b on (a.ID=b.ID) where a.ID between 4 and 7;

Explained.

SQL> @$ORACLE_HOME/rdbms/admin/utlxpls.sql

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3677731951

--------------------------------------------------------------------------------------------
| Id  | Operation                    | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |             |  4    | 60    |  4   (0)   | 00:00:01 |
|   1 |  NESTED LOOPS                |             |  4    | 60    |  4   (0)   | 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| TEST2       |  5    | 55    |  4   (0)   | 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | SYS_C004858 |  5    |       |  3   (0)   | 00:00:01 |
|*  4 |   INDEX UNIQUE SCAN          | SYS_C004857 |  1    |  4    |  0   (0)   | 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("B"."ID">=4 AND "B"."ID"<=7)
   4 - access("A"."ID"="B"."ID")
       filter("A"."ID"<=7 AND "A"."ID">=4)

18 rows selected.

SQL> delete from TEST1;

9 rows deleted.

SQL> declare
mynum number := 1;
begin
while mynum < 10000 loop
  insert into test1 values(mynum,to_char(mynum));
  mynum := mynum + 1;
  end loop;
end;
/

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

SQL> EXPLAIN PLAN FOR
select a.ID, b.ID_CHAR from test1 a join test2 b on (a.ID=b.ID) where a.ID between 4000 and 7000;

Explained.

SQL> @$ORACLE_HOME/rdbms/admin/utlxpls.sql

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3677731951

--------------------------------------------------------------------------------------------
| Id  | Operation                    | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |             |  3001 | 45015 | 17  (12)   | 00:00:01 |
|   1 |  NESTED LOOPS                |             |  3001 | 45015 | 17  (12)   | 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| TEST2       |  3002 | 33022 | 15   (0)   | 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | SYS_C004858 |  3002 |       |  8   (0)   | 00:00:01 |
|*  4 |   INDEX UNIQUE SCAN          | SYS_C004857 |  1    |  4    |  0   (0)   | 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("B"."ID">=4000 AND "B"."ID"<=7000)
   4 - access("A"."ID"="B"."ID")
       filter("A"."ID">=4000 AND "A"."ID"<=7000)

18 rows selected.

Como puedes ver, esta vez el plan de ejecución fue el mismo para diez o diez mil registros en la tabla test1 y el numero de registros manejados en los planes de ejecución fue más o menos preciso.

Por cierto, si quieres desbloquear las estadísticas de una tabla puedes hacerlo de esta forma:
SQL> exec DBMS_STATS.UNLOCK_TABLE_STATS ('MYUSER', 'TEST1');

PL/SQL procedure successfully completed.

SQL> BEGIN DBMS_STATS.GATHER_TABLE_STATS( OWNNAME => 'MYUSER', TABNAME => 'TEST1'); END;
  2  /

PL/SQL procedure successfully completed.


Más información:

Locking or Unlocking Statistics
Preserving Statistics using DBMS_STATS.LOCK_TABLE_STATS

jueves, 5 de abril de 2012

Oracle y OCFS2 sin liberar espacio usado

Algunas veces tienes que eliminar un tablespace para recuperar espacio de almacenamiento y usarlo para crear otro tablespace, como cuando tienes que redimensionar un tablespace de undo. Y usualmente no es gran problema a menos que tengas sistemas de archivos OCFS2:
oracle@myserver$ df -m|egrep '(Filesystem)|(/mydb/undo)'
Filesystem           1M-blocks      Used Available Use% Mounted on
                         24576     24572         4 100% /mydb/undo

oracle@myserver$ sqlplus '/ as sysdba'

SQL*Plus: Release 10.2.0.4.0 - Production on Thu Jan 26 22:57:35 2012

Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, OLAP, Data Mining
and Real Application Testing options

SQL> drop tablespace myundo including contents and datafiles;

Tablespace dropped.

SQL> create undo tablespace mynewundo datafile '/mydb/undo/undo.dbf' size 10g;
create undo tablespace mynewundo datafile '/mydb/undo/undo.dbf' size 10g
*
ERROR at line 1:
ORA-01119: error in creating database file '/mydb/undo/undo.dbf'
ORA-27044: unable to write the header block of file
Linux-ia64 Error: 28: No space left on device
Additional information: 4

SQL> quit
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, OLAP, Data Mining
and Real Application Testing options

oracle@myserver$ df -m|egrep '(Filesystem)|(/mydb/undo)'
Filesystem           1M-blocks      Used Available Use% Mounted on
                         24576     24572         4 100% /mydb/undo

oracle@myserver$ ls -la /mydb/undo
total 16
drwxr-xr-x  4 root   root 4096 Jan 20  2010 .
drwxr-xr-x  4 root   root 4096 Dec 17  2009 ..
drwxr-xr-x  2 root   root 4096 Dec 17  2009 lost+found

Como podrás haberte dado cuenta no hay espacio recuperado por eliminar ese tablespace de undo, y ya que necesitamos crear un nuevo tablespace de undo en el mismo sistema de archivos tenemos que hacer algo.

Yo no tengo muchas bases de datos ubicadas en sistemas de archivos OCFS2 por lo que no me preocupé por encontrar la causa raíz de este problema, y ya que tenía esta base de datos sin usuarios conectados sólo creé un pequeño tablespace de undo en otro lugar, di de baja la instancia, y después de hacerlo recuperé el espacio:
oracle@myserver$ df -m|egrep '(Filesystem)|(/mydb/undo)'
Filesystem           1M-blocks      Used Available Use% Mounted on
                         24576       359     24217   2% /mydb/undo

oracle@myserver$ ls -la /mydb/undo
total 16
drwxr-xr-x  4 root   root 4096 Jan 20  2010 .
drwxr-xr-x  4 root   root 4096 Dec 17  2009 ..
drwxr-xr-x  2 root   root 4096 Dec 17  2009 lost+found

oracle@myserver$ mount|grep /mydb/undo
/dev/mapper/myvg-undo on /mydb/undo type ocfs2 (rw,_netdev,datavolume,nointr,heartbeat=local)

oracle@myserver$ uname -a
Linux myserver.localdomain 2.6.9-67.EL #1 SMP Wed Nov 7 13:43:35 EST 2007 ia64 ia64 ia64 GNU/Linux

oracle@myserver$ /sbin/lsmod|grep ocf
ocfs2                 744096  26 
debugfs                27340  2 ocfs2
ocfs2_dlmfs            53416  1 
ocfs2_dlm             424192  2 ocfs2,ocfs2_dlmfs
ocfs2_nodemanager     316791  32 ocfs2,ocfs2_dlmfs,ocfs2_dlm
configfs               65468  2 ocfs2_nodemanager
jbd                   149272  2 ocfs2,ext3

oracle@myserver$ /sbin/modinfo ocfs2
filename:       /lib/modules/2.6.9-67.EL/kernel/fs/ocfs2/ocfs2.ko
license:        GPL
author:         Oracle
version:        1.2.9 1B43458FD47258934A48F1C
description:    OCFS2 1.2.9 Mon May 19 14:04:34 PDT 2008 (build a693806cb619dd7f225004092b675ede)
depends:        ocfs2_nodemanager,ocfs2_dlm,jbd,debugfs
vermagic:       2.6.9-67.EL SMP ia64gcc-3.4

Sé que este es sólo un arreglo temporal y no uno bueno, pero funciona bien y puedes continuar con lo que sigue.

miércoles, 4 de abril de 2012

Como configurar una conexión VPN PPTP

Si necesitas abrir una conexión VPN PPTP desde una computadora Linux, puedes usar el cliente pptp para configurar una conexión PPP y lanzar el demonio pppd para hacer la conexión. Necesitas conocer al menos el servidor VPN, un usuario autorizado para hacer la conexión y su contraseña, y trabajaremos con la cuenta de root por cuestiones prácticas pero no es indispensable.

Antes que nada, si no tienes el cliente pptp tienes que instalarlo o configurar la conexión PPP a mano o de otra forma, en Debian Squeeze puedes instalar el paquete pptp-linux.

A continuación, esta es la manera fácil para configurar tu conexión VPN PPTP:
pptpsetup --create MYVPN --server myvpnserver --username myvpnuser --password mypassword

Tienes que sustituir myvpnserver, myvpnuser y mypassword por el servidor VPN, el usuario y contraseña respectivamente; MYVPN es el nombre de la configuración PPP que crearás y es escogido por ti.

Después de esto tendrás un nuevo archivo de configuración en /etc/ppp/peers y también una línea extra en /etc/ppp/chap-secrets:
olimpo:~# cat /etc/ppp/peers/MYVPN
# written by pptpsetup
pty "pptp myvpnserver --nolaunchpppd"
lock
noauth
nobsdcomp
nodeflate
name myvpnuser
remotename MYVPN
ipparam MYVPN

olimpo:~# cat /etc/ppp/chap-secrets
# Secrets for authentication using CHAP
# client server secret   IP addresses

# added by pptpsetup for MYVPN
myvpnuser MYVPN "mypassword" *

Si quieres configurar el demonio pppd a mano, tienes que crear un archivo en /etc/ppp/peers y agregar la información de autenticación en /etc/ppp/chap-secrets y debería ser lo mismo.

Ahora puedes abrir la conexión PPP lanzando el demonio pppd:
olimpo:~# pppd call MYVPN updetach
Using interface ppp0
Connect: ppp0 <--> /dev/pts/3
CHAP authentication succeeded
local  IP address 10.5.15.222
remote IP address 10.5.15.127

olimpo:~# tail --lines=16 /var/log/syslog
Jan 25 13:04:12 olimpo pppd[29036]: pppd 2.4.5 started by root, uid 0
Jan 25 13:04:12 olimpo pppd[29036]: Using interface ppp0
Jan 25 13:04:12 olimpo pppd[29036]: Connect: ppp0 <--> /dev/pts/3
Jan 25 13:04:13 olimpo pptp[29037]: anon log[main:pptp.c:314]: The synchronous pptp option is NOT activated 
Jan 25 13:04:13 olimpo pptp[29041]: anon log[ctrlp_rep:pptp_ctrl.c:251]: Sent control packet type is 1 'Start-Control-Connection-Request' 
Jan 25 13:04:13 olimpo pptp[29041]: anon log[ctrlp_disp:pptp_ctrl.c:739]: Received Start Control Connection Reply
Jan 25 13:04:13 olimpo pptp[29041]: anon log[ctrlp_disp:pptp_ctrl.c:773]: Client connection established.
Jan 25 13:04:14 olimpo NetworkManager[1919]:    SCPlugin-Ifupdown: devices added (path: /sys/devices/virtual/net/ppp0, iface: ppp0)
Jan 25 13:04:14 olimpo NetworkManager[1919]:    SCPlugin-Ifupdown: device added (path: /sys/devices/virtual/net/ppp0, iface: ppp0): no ifupdown configuration found.
Jan 25 13:04:14 olimpo pptp[29041]: anon log[ctrlp_rep:pptp_ctrl.c:251]: Sent control packet type is 7 'Outgoing-Call-Request' 
Jan 25 13:04:14 olimpo pptp[29041]: anon log[ctrlp_disp:pptp_ctrl.c:858]: Received Outgoing Call Reply.
Jan 25 13:04:14 olimpo pptp[29041]: anon log[ctrlp_disp:pptp_ctrl.c:897]: Outgoing call established (call ID 0, peer's call ID 8918). 
Jan 25 13:04:14 olimpo modem-manager: (net/ppp0): could not get port's parent device
Jan 25 13:04:17 olimpo pppd[29036]: CHAP authentication succeeded
Jan 25 13:04:17 olimpo pppd[29036]: local  IP address 10.5.15.222
Jan 25 13:04:17 olimpo pppd[29036]: remote IP address 10.5.15.127

olimpo:~# ifconfig ppp0
ppp0      Link encap:Point-to-Point Protocol  
          inet addr:10.5.15.222  P-t-P:10.5.15.127  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:7 errors:0 dropped:0 overruns:0 frame:0
          TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3 
          RX bytes:99 (99.0 B)  TX bytes:93 (93.0 B)

olimpo:~# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.5.15.127     *               255.255.255.255 UH    0      0        0 ppp0
static.customer 10.123.46.1     255.255.255.255 UGH   0      0        0 br0
10.123.46.0     *               255.255.255.0   U     0      0        0 br0
default         10.123.46.1     0.0.0.0         UG    0      0        0 br0

Eso es, tienes una conexión VPN abierta a tu servidor VPN! O está casi terminado, ya que tenemos un problema de ruteo.
olimpo:~# ping 10.5.15.123
PING 10.5.15.123 (10.5.15.123) 56(84) bytes of data.
^C
--- 10.5.15.123 ping statistics ---
7 packets transmitted, 0 received, 100% packet loss, time 6047ms

olimpo:~# traceroute 10.5.15.123
traceroute to 10.5.15.123 (10.5.15.123), 30 hops max, 60 byte packets
 1  10.123.81.4 (10.123.81.4)  0.407 ms  0.429 ms  0.492 ms
 2  10.123.148.1 (10.123.148.1)  0.313 ms  0.352 ms  0.345 ms
 3  * * *
 4  * * *
 5  * * *
 6  *^C

Como podrás observar en la información de ruteo, no hay ruta a la red VPN desde tu computadora Linux. Puedes agregarle defaultroute al comando pppd, pero de esta forma tendrás conectividad SOLO a la red VPN; para ponerlo simple: es como si estuvieras directamente conectado a la otra red. Si tenías acceso a internet y no hay forma de accesar internet en la red VPN entonces no tendrás acceso a internet, o si tenías acceso a otras redes después de usar pppd con defaultroute no tendrás más acceso a ellas.

Y de hecho esa es la forma en que una VPN supuestamente debe trabajar. Por ejemplo, si tu computadora tiene un troyano y ese programa está diseñado específicamente para tu organización, y conectas tu computadora a la red VPN y aún tienes acceso a internet, entonces ese troyano puede robar información y enviarla a alguien más fácilmente. O podrías pensar que dejar tu conexión VPN abierta y tu computadora accesible desde internet podría ser útil para compartir tu conexión VPN; de esta manera estás creando un hoyo de seguridad en la red VPN por lo que sé cuidadoso y hazlo sólo si realmente necesitas hacerlo.

Por lo tanto, si necesitas mantener conectividad a otras redes y no quieres agregar defaultroute al comando pppd, tienes que crear la ruta a mano. En este caso, queremos accesar la red VPN 10.5.15.0/24, por lo tanto escribiremos este comando route:
olimpo:~# route add -net 10.5.15.0 netmask 255.255.255.0 dev ppp0

Como puedes ver en la información de route y traceroute, esta vez puedes alcanzar el servidor de ejemplo (10.5.15.123) a través del gateway PPP (10.5.15.127):
olimpo:~# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.5.15.127     *               255.255.255.255 UH    0      0        0 ppp0
static.customer 10.123.46.1     255.255.255.255 UGH   0      0        0 br0
10.123.46.0     *               255.255.255.0   U     0      0        0 br0
10.5.15.0       *               255.255.255.0   U     0      0        0 ppp0
default         10.123.46.1     0.0.0.0         UG    0      0        0 br0

olimpo:~# traceroute 10.5.15.123
traceroute to 10.5.15.123 (10.5.15.123), 30 hops max, 60 byte packets
 1  10.5.15.127 (10.5.15.127)  163.061 ms  163.027 ms  163.010 ms
 2  10.5.15.123 (10.5.15.123)  174.981 ms  174.967 ms  174.954 ms

Finalmente, para terminar tu conexión PPP puedes finalizar el demonio pppd (apropiadamente); puedes hacer esto obteniendo el PID del demonio pppd y lanzando un comando kill:
olimpo:~# ps ax|grep pppd
10094 pts/1    S+     0:00 grep pppd
29037 pts/1    S      0:00 pptp myvpnserver --nolaunchpppd
29041 pts/1    S      0:00 pptp myvpnserver --nolaunchpppd
29044 pts/1    S      0:00 pppd call MYVPN updetach

olimpo:~# cat /var/run/ppp0.pid 
29044

olimpo:~/bin# kill -TERM 29044

olimpo:~/bin# tail --lines=11 /var/log/syslog
Jan 25 15:06:43 olimpo pppd[29044]: Terminating on signal 15
Jan 25 15:06:43 olimpo pppd[29044]: Modem hangup
Jan 25 15:06:43 olimpo pppd[29044]: Connect time 122.5 minutes.
Jan 25 15:06:43 olimpo pppd[29044]: Sent 1140 bytes, received 1056 bytes.
Jan 25 15:06:43 olimpo pptp[29041]: anon log[callmgr_main:pptp_callmgr.c:258]: Closing connection (shutdown)
Jan 25 15:06:43 olimpo pptp[29041]: anon log[ctrlp_rep:pptp_ctrl.c:251]: Sent control packet type is 12 'Call-Clear-Request' 
Jan 25 15:06:43 olimpo pptp[29041]: anon log[call_callback:pptp_callmgr.c:79]: Closing connection (call state)
Jan 25 15:06:44 olimpo pppd[29044]: Connection terminated.
Jan 25 15:06:44 olimpo avahi-daemon[1488]: Withdrawing workstation service for ppp0.
Jan 25 15:06:44 olimpo NetworkManager[1919]:    SCPlugin-Ifupdown: devices removed (path: /sys/devices/virtual/net/ppp0, iface: ppp0)
Jan 25 15:06:49 olimpo pppd[29044]: Exit.

olimpo:~# ps ax|grep pppd
11587 pts/1    S+     0:00 grep pppd

Más información:

The Point-to-Point Protocol