A la hora de diagnosticar y recopilar información sobre un incidente o simplemente realizar un healh check, tanto de Cluster Ready Services, Oracle Grid Infrastructure, Oracle RAC o Single Instance Oracle nos lo pone muy fácil mediante la herramienta TFA, soportando GI y Base de Datos a partir de la versión 10.2, ya que recopila, empaqueta y centraliza datos de diagnóstico.

En el siguiente artículo vamos a describir y analizar tanto el despliegue como algunas de sus funcionalidades, ya que hoy en día sigue siendo la gran desconocida, teniendo en cuenta que es una de las herramientas más interesantes actualmente.

TFA nos aporta los siguientes beneficios:

  • Centralización y encapsulación de la información de diagnóstico tanto de CRS, GI y/o RAC de todos los nodos a través de un comando ejecutado desde uno de ellos.
  • Aislar solo la información de interés para reducir la información a facilitar a soporte, tanto para un periodo de tiempo específico, un componente particular o error específico.
  • Revisión de todos los logs y ficheros de traza para condiciones específicas que nos indican un problema, tanto en tiempo real como bajo demanda

Y las siguientes funcionalidades:

  • Ver el estado general tanto del cluster, sus componentes, la base de datos y del sistema. Con un solo comando!
  • Investigar logs y analizarlos
  • Recolectar diagnósticos específicos para soporte, incluyendo filtrados (incluyendo Service Request Data Collection )
  • Gestionar logs de base de datos. Configuración de autopurge!
  • Notificaciones por email
  • Incident Packaging Service
  • Enmascaramiento de datos sensibles

Consideraciones previas a la instalación de TFA

Una de las dudas que se nos plantea en primera instancia es cuánto podría penalizar en nuestro servidor. El consumo de recursos es muy bajo, pudiendo aumentar de forma considerable en los siguientes casos:

  • Cuando está inventariando los ficheros de diagnóstico
  • Cuando está realizando un diagnóstico.

Tenemos dos posibles opciones de instalación:

Modo daemon

  • Configurada como root (recomendada sobre todo para RAC)
  • Instalada en todos los nodos
  • Descubre de forma automática el software de Oracle y Storage Servers de Exadata
  • Comienza a monitorizar de forma automática

Modo no daemon

  • Solo corre en el servidor sobre el que se instala
  • No recopila información automáticamente
  • No recopila información sobre el resto de los servidores
  • No recopila información sobre ficheros que no sean propiedad del usuario que lo instala (especial atención si tenemos separación de roles)

Oracle TFA está configurado por defecto como parte de la GI cuando ejecutamos root.sh o rootupgrade.sh.

Los directorios donde reside cuando forma parte de la GI son:

  • GRID_HOME/tfa: ejecutables y ficheros de configuración.
  • ORACLE_BASE/tfa: logs y ficheros de metadata.

Como buena práctica Oracle recomienda actualizarlo cada 6 meses. Es muy interesante tenerlo ubicado en un directorio que tengamos controlado, desvinculado del home de la grid, y hayamos definido siguiendo los estándares de nuestra plataforma (homogeneización de entornos), por lo que aprovechando la actualización inicial propongo eliminar la instalación de TFA y hacer una instalación limpia.

Hacemos ésto para tener únicamente una instancia de TFA desplegada y controlada en el directorio que hayamos definido.

Si por el contrario la que se quiere mantener es la original que viene definida en el home de la GI el script de instalación realizará un upgrade de ésta. A gusto del consumidor.

Si ya os he convencido para sacar partido de esta herramienta pongámonos manos a la obra, si aún no lo he conseguido recomiendo releer el párrafo anterior relativo a los beneficios que nos aporta 🙂

Manos a la obra!

Instalación de TFA

Describo el entorno sobre el que trabajaré:

  • Oracle RAC 2 nodos: nodo01, nodo02
  • Versión de Base de datos 18.6
  • Versión de GI 19.3

Como primer paso procedemos a la desinstalación de TFA de la GI original. El comando es:

$GRID_HOME/bin/tfactl uninstall

En mi caso estoy realizando la desinstalación en un RAC de 2 nodos, por lo que realizaremos los pasos en ambos nodos al ser ésta local del nodo en el que se trabaja. Nota: solo la desinstalación, la instalación la vemos en el siguiente paso siendo lanzada desde un solo nodo y confirmando que queremos que siga con el resto de nodos 🙂

[root@nodo01 ~]# /u01/app/19.3.0/grid/tfa/bin/tfactl uninstall
TFA will be uninstalled on nodo01 :

Removing TFA from nodo01 only
Please remove TFA locally on any other configured nodes

Notifying Other Nodes about TFA Uninstall...
Sleeping for 10 seconds...

Stopping TFA Support Tools...

Stopping TFA in nodo01...

Shutting down TFA
Removed symlink /etc/systemd/system/multi-user.target.wants/oracle-tfa.service.
Removed symlink /etc/systemd/system/graphical.target.wants/oracle-tfa.service.
. . . . .
. . .
Successfully shutdown TFA..

TFA-00002 Oracle Trace File Analyzer (TFA) is not running
Deleting TFA support files on nodo01:
Removing /u01/app/oracle/tfa/nodo01/database...
Removing /u01/app/oracle/tfa/nodo01/log...
Removing /u01/app/oracle/tfa/nodo01/output...
Removing /u01/app/oracle/tfa/nodo01...
Removing /u01/app/oracle/tfa...
Removing /etc/rc.d/rc0.d/K17init.tfa
Removing /etc/rc.d/rc1.d/K17init.tfa
Removing /etc/rc.d/rc2.d/K17init.tfa
Removing /etc/rc.d/rc4.d/K17init.tfa
Removing /etc/rc.d/rc6.d/K17init.tfa
Removing /etc/init.d/init.tfa...
Removing /u01/app/19.3.0/grid/bin/tfactl...
Removing /u01/app/19.3.0/grid/tfa/bin...
Removing /u01/app/19.3.0/grid/tfa/nodo01...
Removing /u01/app/19.3.0/grid/tfa...

 

 [root@nodo02 ~]# /u01/app/19.3.0/grid/tfa/bin/tfactl uninstall
TFA will be uninstalled on nodo02 :

Removing TFA from nodo02...

Stopping TFA Support Tools...

Stopping TFA in nodo02...

Shutting down TFA
Removed symlink /etc/systemd/system/multi-user.target.wants/oracle-tfa.service.
Removed symlink /etc/systemd/system/graphical.target.wants/oracle-tfa.service.
. . . . .
. . .
Successfully shutdown TFA..

TFA-00002 Oracle Trace File Analyzer (TFA) is not running
Deleting TFA support files on nodo02:
Removing /u01/app/oracle/tfa/nodo02/database...
Removing /u01/app/oracle/tfa/nodo02/log...
Removing /u01/app/oracle/tfa/nodo02/output...
Removing /u01/app/oracle/tfa/nodo02...
Removing /u01/app/oracle/tfa...
Removing /etc/rc.d/rc0.d/K17init.tfa
Removing /etc/rc.d/rc1.d/K17init.tfa
Removing /etc/rc.d/rc2.d/K17init.tfa
Removing /etc/rc.d/rc4.d/K17init.tfa
Removing /etc/rc.d/rc6.d/K17init.tfa
Removing /etc/init.d/init.tfa...
Removing /u01/app/19.3.0/grid/bin/tfactl...
Removing /u01/app/19.3.0/grid/tfa/bin...
Removing /u01/app/19.3.0/grid/tfa/nodo02...
Removing /u01/app/19.3.0/grid/tfa...

Para proceder con la instalación descargaremos la última versión de TFA (TFA Collector – TFA with Database Support Tools Bundle (Doc ID 1513912.1) ), en el caso que aplica versión 19.2.1.0.0

Una vez la hemos descargado crearemos el directorio en el que queramos centralizar la herramienta en ambos nodos:

[root@nodo01 ~]# mkdir /u01/tools/tfa

Descomprimimos la versión que descargamos en el paso anterior:

[root@nodo01 tfa]# unzip TFA-LINUX_v19.2.1.zip
Archive: TFA-LINUX_v19.2.1.zip
inflating: README.txt
inflating: installTFA-LINUX

Y procedemos a la instalación:

[root@nodo01 tfa]# ./installTFA-LINUX

TFA Installation Log will be written to File : /tmp/tfa_install_16340_2019_07_15-11_50_12.log

Starting TFA installation

TFA Version: 192100 Build Date: 201904251105

Enter a location for installing TFA (/tfa will be appended if not supplied) [/u01/tools/tfa]:
/u01/tools/tfa

Running Auto Setup for TFA as user root...

Would you like to do a [L]ocal only or [C]lusterwide installation ? [L|l|C|c] [C] : C

The following installation requires temporary use of SSH.
If SSH is not configured already then we will remove SSH
when complete.
Do you wish to Continue ? [Y|y|N|n] [Y] Y
Installing TFA now...

Discovering Nodes and Oracle resources

Starting Discovery...

 

Getting list of nodes in cluster . . . . .

List of nodes in cluster:
nodo01
nodo02

 

Checking ssh user equivalency settings on all nodes in cluster

Node nodo02 is configured for ssh user equivalency for root user

CRS_HOME=/u01/app/19.3.0/grid

Searching for running databases...
1. cdbpro

 

Searching out ORACLE_HOME for selected databases...

 

Getting Oracle Inventory...

ORACLE INVENTORY: /u01/app/oraInventory

 

Discovery Complete...

 

TFA Will be Installed on the Following Nodes:
++++++++++++++++++++++++++++++++++++++++++++

Install Nodes
=============
nodo01
nodo02

Do you wish to make changes to the Node List ? [Y/y/N/n] [N] N

TFA will scan the following Directories
++++++++++++++++++++++++++++++++++++++++++++

.--------------------------------------------------------------------------.
| nodo01 |
+--------------------------------------------------------------+-----------+
| Trace Directory | Resource |
+--------------------------------------------------------------+-----------+
| /u01/app/19.3.0/grid/cfgtoollogs | CFGTOOLS |
| /u01/app/19.3.0/grid/crs/log | CRS |
| /u01/app/19.3.0/grid/css/log | CRS |
| /u01/app/19.3.0/grid/evm/admin/logger | CRS |
| /u01/app/19.3.0/grid/evm/log | CRS |
| /u01/app/19.3.0/grid/install | INSTALL |
| /u01/app/19.3.0/grid/inventory/ContentsXML | INSTALL |
| /u01/app/19.3.0/grid/log | CRS |
| /u01/app/19.3.0/grid/network/log | CRS |
| /u01/app/19.3.0/grid/opmn/logs | CRS |
| /u01/app/19.3.0/grid/rdbms/log | ASM |
| /u01/app/19.3.0/grid/srvm/log | CRS |
| /u01/app/oraInventory/ContentsXML | INSTALL |
| /u01/app/oraInventory/logs | INSTALL |
| /u01/app/oracle/cfgtoollogs | CFGTOOLS |
| /u01/app/oracle/crsdata/nodo01/acfs | ACFS |
| /u01/app/oracle/crsdata/nodo01/afd | ASM |
| /u01/app/oracle/crsdata/nodo01/chad | CRS |
| /u01/app/oracle/crsdata/nodo01/core | CRS |
| /u01/app/oracle/crsdata/nodo01/crsconfig | CRS |
| /u01/app/oracle/crsdata/nodo01/crsdiag | CRS |
| /u01/app/oracle/crsdata/nodo01/cvu | CRS |
| /u01/app/oracle/crsdata/nodo01/evm | CRS |
| /u01/app/oracle/crsdata/nodo01/output | CRS |
| /u01/app/oracle/crsdata/nodo01/trace | CRS |
| /u01/app/oracle/diag/apx/+apx | ASMPROXY |
| /u01/app/oracle/diag/apx/+apx/+APX1/cdump | ASMPROXY |
| /u01/app/oracle/diag/apx/+apx/+APX1/trace | ASMPROXY |
| /u01/app/oracle/diag/asm/+asm/+ASM1/cdump | ASM |
| /u01/app/oracle/diag/asm/+asm/+ASM1/trace | ASM |
| /u01/app/oracle/diag/asm/user_oracle/host_2945644752_110/cdu | ASMCLIENT |
| /u01/app/oracle/diag/asm/user_oracle/host_2945644752_110/tra | ASM |
| /u01/app/oracle/diag/asm/user_root/host_2945644752_110/cdump | ASMCLIENT |
| /u01/app/oracle/diag/asm/user_root/host_2945644752_110/trace | ASM |
| /u01/app/oracle/diag/asmtool/user_oracle/host_2945644752_110 | ASMTOOL |
| /u01/app/oracle/diag/asmtool/user_oracle/host_2945644752_110 | ASM |
| /u01/app/oracle/diag/asmtool/user_root/host_2945644752_110/c | ASMTOOL |
| /u01/app/oracle/diag/asmtool/user_root/host_2945644752_110/t | ASM |
| /u01/app/oracle/diag/clients/user_oracle/host_2945644752_110 | DBCLIENT |
| /u01/app/oracle/diag/clients/user_oracle/host_2945644752_110 | DBCLIENT |
| /u01/app/oracle/diag/clients/user_root/host_2945644752_110/c | DBCLIENT |
| /u01/app/oracle/diag/clients/user_root/host_2945644752_110/t | DBCLIENT |
| /u01/app/oracle/diag/crs/nodo01/crs/cdump | CRS |
| /u01/app/oracle/diag/crs/nodo01/crs/trace | CRS |
| /u01/app/oracle/diag/rdbms/cdbpro/cdbpro1/cdump | RDBMS |
| /u01/app/oracle/diag/rdbms/cdbpro/cdbpro1/trace | RDBMS |
| /u01/app/oracle/diag/tnslsnr | TNS |
| /u01/app/oracle/diag/tnslsnr/nodo01/listener/cdump | TNS |
| /u01/app/oracle/diag/tnslsnr/nodo01/listener/trace | TNS |
| /u01/app/oracle/diag/tnslsnr/nodo01/listener_scan1/c | TNS |
| /u01/app/oracle/diag/tnslsnr/nodo01/listener_scan1/t | TNS |
| /u01/app/oracle/product/18.6.0/dbhome_pro/cfgtoollogs | CFGTOOLS |
| /u01/app/oracle/product/18.6.0/dbhome_pro/install | INSTALL |
| xxxxx | ASM |
| xxxxx | ASM |
'--------------------------------------------------------------+-----------'

Installing TFA on nodo01:
HOST: nodo01 TFA_HOME: /u01/tools/tfa/nodo01/tfa_home

Installing TFA on nodo02:
HOST: nodo02 TFA_HOME: /u01/tools/tfa/nodo02/tfa_home

.-----------------------------------------------------------------------------------.
| Host | Status of TFA | PID | Port | Version | Build ID |
+----------------+---------------+-------+------+------------+----------------------+
| nodo01 | RUNNING | 17620 | 5000 | 19.2.1.0.0 | 19210020190425110550 |
| nodo02 | RUNNING | 14817 | 5000 | 19.2.1.0.0 | 19210020190425110550 |
'----------------+---------------+-------+------+------------+----------------------'

Running Inventory in All Nodes...

Enabling Access for Non-root Users on nodo01...

Summary of TFA Installation:
.--------------------------------------------------------------.
| nodo01 |
+---------------------+----------------------------------------+
| Parameter | Value |
+---------------------+----------------------------------------+
| Install location | /u01/tools/tfa/nodo01/tfa_home |
| Repository location | /u01/tools/tfa/repository |
| Repository usage | 0 MB out of 10240 MB |
'---------------------+----------------------------------------'

.--------------------------------------------------------------.
| nodo02 |
+---------------------+----------------------------------------+
| Parameter | Value |
+---------------------+----------------------------------------+
| Install location | /u01/tools/tfa/nodo02/tfa_home |
| Repository location | /u01/tools/tfa/repository |
| Repository usage | 0 MB out of 10240 MB |
'---------------------+----------------------------------------'

TFA is successfully installed...

Usage : /u01/app/19.3.0/grid/bin/tfactl <command> [options]
commands:diagcollect|collection|analyze|ips|run|start|stop|enable|disable|status|print|access|purge|directory|host|receiver|set|toolstatus|uninstall|diagnosetfa|syncnodes|setupmos|upload|availability|rest|events|search|changes|isa|blackout|rediscover
For detailed help on each command use:
/u01/app/19.3.0/grid/bin/tfactl <command> -help

Por defecto TFA arranca automáticamente una vez instalado.

Y ahora sí… a jugar!

Una vez hemos instalado TFA he hecho una recopilación de funcionalidades más interesantes

Sacando partido a TFA

Podremos trabajar con tfactl de las siguientes maneras:

  • Command line interface
  • Shell interface
  • Menu interface

Os animo a probar cada una de ellas, sobretodo la interfaz de menú. Nos centraremos en la interfaz de línea de comandos en éste artículo.

Estado del sistema y cluster en tiempo real

Podremos ver un resumen del estado de todos los componentes de la base de datos, GI y CRS, pudiendo desgranar la información por un componente específico.

[root@nodo02 ~]# tfactl summary

  Executing Summary in Parallel on Following Nodes:
    Node : nodo01
    Node : nodo02

LOGFILE LOCATION : /u01/tools/tfa/repository/suptools/nodo02/summary/root/20190715162308/log/summary_command_20190715162308_nodo02_26066.log

  Component Specific Summary collection :
    - Collecting CRS details ... Done.
    - Collecting ASM details ... Done.
    - Collecting ACFS details ... Done.
    - Collecting DATABASE details ... Done.
    - Collecting PATCH details ... Done.
    - Collecting LISTENER details ... Done.
    - Collecting NETWORK details ... Done.
    - Collecting OS details ... Done.
    - Collecting TFA details ... Done.
    - Collecting SUMMARY details ... Done.

  Remote Summary Data Collection : In-Progress - Please wait ...
  - Data Collection From Node - nodo01 .. Done.

  Prepare Clusterwide Summary Overview ... Done
      cluster_status_summary

  DETAILS                                                                                             COMPONENT   STATUS
+---------------------------------------------------------------------------------------------------+-----------+---------+
  .-----------------------------------------------.                                                   CRS         PROBLEM
  | CRS_SERVER_STATUS   : ONLINE                  |
  | CRS_STATE           : ONLINE                  |
  | CRS_INTEGRITY_CHECK : FAIL                    |
  | CRS_RESOURCE_STATUS : OFFLINE Resources Found |
  '-----------------------------------------------'
  .-------------------------------------------------------.                                           ASM         PROBLEM
  | ASM_DISK_SIZE_STATUS : WARNING - Available Size < 20% |
  | ASM_BLOCK_STATUS     : PASS                           |
  | ASM_CHAIN_STATUS     : PASS                           |
  | ASM_INCIDENTS        : FAIL                           |
  | ASM_PROBLEMS         : FAIL                           |
  '-------------------------------------------------------'
  .-----------------------.                                                                           ACFS        OFFLINE
  | ACFS_STATUS : OFFLINE |
  '-----------------------'
  .-----------------------------------------------------------------------------------------------.   DATABASE    PROBLEM
  | ORACLE_HOME_NAME | ORACLE_HOME_DETAILS                                                        |
  +------------------+----------------------------------------------------------------------------+
  | OraDB18Home1     | .------------------------------------------------------------------------. |
  |                  | | DB_BLOCKS | PROBLEMS | STATUS  | INCIDENTS | DB_CHAINS | DATABASE_NAME | |
  |                  | +-----------+----------+---------+-----------+-----------+---------------+ |
  |                  | | PASS      | PROBLEM  | PROBLEM | PROBLEM   | PROBLEM   | cdbpro        | |
  |                  | '-----------+----------+---------+-----------+-----------+---------------' |
  '------------------+----------------------------------------------------------------------------'
  .----------------------------------------------.                                                    PATCH       OK
  | CRS_PATCH_CONSISTENCY_ACROSS_NODES      : OK |
  | DATABASE_PATCH_CONSISTENCY_ACROSS_NODES : OK |
  '----------------------------------------------'
  .-----------------------.                                                                           LISTENER    OK
  | LISTNER_STATUS   : OK |
  '-----------------------'
  .---------------------------.                                                                       NETWORK     OK
  | CLUSTER_NETWORK_STATUS :  |
  '---------------------------'
  .-----------------------.                                                                           OS          OK
  | MEM_USAGE_STATUS : OK |
  '-----------------------'
  .----------------------.                                                                            TFA         OK
  | TFA_STATUS : RUNNING |
  '----------------------'
  .------------------------------------.                                                              SUMMARY     OK
  | SUMMARY_EXECUTION_TIME : 0H:12M:6S |
  '------------------------------------'
+---------------------------------------------------------------------------------------------------+-----------+---------+


        ### Entering in to SUMMARY Command-Line Interface ###

tfactl_summary>list

  Components : Select Component - select [component_number|component_name]
        1 => overview
        2 => crs_overview
        3 => asm_overview
        4 => acfs_overview
        5 => database_overview
        6 => patch_overview
        7 => listener_overview
        8 => network_overview
        9 => os_overview
        10 => tfa_overview
        11 => summary_overview

Por defecto las recoleccioones automáticas de diagnóstico están habilitadas. Si queremos deshabilitarlas para ejecutarlas solo bajo demanda:

[root@nodo01 ~]# tfactl set autodiagcollect=OFF

Notificaciones por email

Se puede notificar alternativamente para ORACLE_HOME específicos incluyendo el propietario del software.

[root@nodo01 ~]# tfactl set notificationAddress=direccion@email.com

Investigar y analizar logs

Podemos centrar el análisis de logs en un periodo de tiempo o incluso en un incidente concreto

[root@nodo01 ~]# tfactl analyze –last 3d
[root@nodo01 ~]# tfactl analyze -search “ORA-27468" -last 1h

Purgado automático de logs

Por defecto viene deshabilitado. Podremos habilitarlo y establecer las políticas de borrado en rangos de días u horas.

Los siguiente comandos habilitarían la ejecución del purgado cada 120 minutos, borrando logs con antiguedad superior a 1 día:

[root@nodo01 ~]# tfactl set manageLogsAutoPurge=ON
[root@nodo01 ~]# tfactl set manageLogsAutoPurgePolicyAge=1d
[root@nodo01 ~]# tfactl set manageLogsAutoPurgeInterval=120

Purgado manual de logs

Es ejecutado con el owner del home de ADR, por lo que solo borrará aquellos ficheros sobre los que tenga permisos.

En la siguiente secuencia de comandos mostraríamos la ocupación de los ficheros de logs, cuanto ha variado en los ultimos 7 días, que va a realizar el comando de purgado y finalmente borrariamos.

tfactl managelogs –show usage
tfactl managelogs –show variation –older 7d
tfactl managelogs –purge –older 7d -dryrun 
tfactl managelogs –purge –older 7d

Enmascaramiento de datos sensibles

TFA nos permite enmascarar datos sensibles como hostnames o direcciones IP. Para ello deberiamos crear un fichero en tfa_home/resources/mask_strings.xml en cada nodo con información como ésta:

Service Request Data Collection (SRDC)

Recopilación de información para enviar a Orace Support 🙂

tfactl diagcollect –srdc ORA-04031 -sr 1-11111111111
tfactl diagcollect –srdc dbperf
tfactl diagcollect –srdc ORA-04031 -database cdbpro -for 7d

Y la subimos a soporte

tfactl upload -user <user_id> -sr <sr_number> <file1 file2>

Incident Packaging Service

Empaquetado simple IPS

tfactl ips show incidents
tfactl ips show problems
tfactl ips show package
tfactl diagcollect -ips

Y las tools… !!

Las tools sólo están disponibles cuando se trata de una descarga de TFA, no con el tfa que vendría por defecto en la GI.

Las tools solo se actualizan cuando actualizamos el TFA, no a través de un PSU

Dejo un listado de aquellas que suelo utilizar y/o veo más interesantes:

  • oratop. Qué decir. Una pasada para un diagnóstico rápido relacionado con indidencias de rendimiento. Desde la misma pantalla podremos ver el top de consultas, tanto por rac o por instancia, top de esperas, ver el plan de ejecución del sqlid que nos interese con un solo click, volumetrías tanto de tbs como de diskgroup, agregar por SQLID o por proceso…
  • orachk or exachk. Necesario! diagnóstico del estado de salud de nuestra base de datos o nuestro Exadata. Recomiendo pasar chequeos de forma rutinaria cada cierto periodo de tiempo.
  • triage. Resumen de los datos de oswatcher/exawatcher
  • oswatcher. Muy útil para problemas de rendimiento y estudiar la causa de node evictions. Recolecta métricas de SO
  • procwatcher. Muy útil para diagnosticar por qué una sesión se ha quedado colgada
  • sqlt. Trazas a nivel de sql para diagnosticar problemas de rendimiento
  • alertsummary. Sumariza los eventos de interés de una BBDD o ASM a través de todos los nodos del RAC
  • dbglevel. Habilita distintos niveles de traza para el CRS
  • calog. informa de eventos de interés a nivel de Cluster.
  • …..

Con este resumen de las tools termino el artículo y espero haber dejado dientes largos 🙂

Un saludo!

A %d blogueros les gusta esto: