Cómo aprobar la certificación Databricks Data Analyst Associate

Esta ha sido hasta ahora mi última certificación aprobada. Es una certificación bonita, he de decir que aprendí bastante pero eran características que desconocía por completo del entorno Databricks, no obstante, creo que aunque es un buen reto para aprender este ha de ser enfocado principalmente a perfiles Data Analyst como tal, que trabajen con visualización o a Data Engineer versátiles que se mueven bien entre BI y Data Engineering o sencillamente un Data engineer con vocación al análisis.

El examen

Consta de 45 preguntas de selección multiple a resolver en 90 minutos

El mínimo para aprobar es un 70% lo cual se corresponde con 32 preguntas respondidas correctamente

Objetivos a cubrir en el examen

  • Databricks SQL – 22% (10/45)
  • Data Management – 20% (9/45)
  • SQL – 29% (13/45)
  • Data Visualization and Dashboards – 18% (8/45)
  • Analytics Applications – 11% (5/45)

Siendo más específicos, la cualificación minima del candidato para aprobar debería ser la siguiente:

  • Describe Databricks SQL and its capabilities, including:
    • Databricks SQL (users, benefits, queries, dashboards, compute)
    • Integrations (Partner Connect, data ingestion, other BI tools)
    • Lakehouse (medallion architecture, streaming data)
  • Manage data with Databricks tools and best practices, including:
    • Delta Lake (basics, benefits)
    • Storage and Management (tables, databases, views, Data Explorer)
    • Security (table ownership, PII data)
  • Use Structured Query Language (SQL) to complete tasks in the Lakehouse, including:
    • Basic SQL (basic query structure, combining data, aggregations)
    • Complex Data (nested data objects, roll-ups, windows, cubes)
    • SQL in the Lakehouse (ANSI SQL, working with silver-level data, query history, higher-order functions, user-defined functions)
  • Create production-grade data visualizations and dashboards, including:
    • Visualization (Databricks SQL capabilities, types of visualizations, storytelling with data)
    • Dashboarding (Databricks SQL capabilities, parameterized dashboards and queries, sharing)
    • Production (refresh schedules, query alerts)
  • Develop analytics applications to solve common data analytics problems, including:
    • Descriptive Statistics (discrete statistics, summary statistics)
    • Common Applications (data enhancement, data blending, last-mile ETL)

Mi opinión acerca del examen

Para empezar al examen le daría una ponderación de 5-5,5 sobre 10, es decir, me pareció un poco más sencillo que el Data Engineer Associate, eso si he de decir que tener conocimientos en el anteriormente mencionado me ha ayudado mucho a afrontar las preguntas de este examen. Dentro de las preguntas claves y conceptos a dominar de cara al examen recomiendo:

  • Tener muy claro Lakehouse, características y ventajas. Arquitectura multihop/Medallion
  • Cual es el Rol a llevar a cabo por un Data Analyst
  • Qué es partner Connect y para que sirve? me sirve para conectar una herramienta de Bi?
  • Repasar muy bien el entorno grafico y conocer las características, por ejemplo: Saber que el query editor, nos permite guardar y ejecutar consultas así como programar (schedule) un refresh de la consulta.
  • Estudiar a fondo:
    • Query
    • Alert
    • Visualization (tener claro todas las posibles, es el bar diagram los mismo que un histogram? cuáles son los pasos para crear una vista counter)
    • Dashboard
  • Tener muy en cuenta Refresh (Schedule) en: Quey, Alerts, Dashboards
  • Gestión de permisos desde SQL y el Data explorer
  • Habrán ejercicios de SQL que contemplen Join (left, right, inner, self, left anti) con incluso con operaciones de ventana (rank, dense_rank, row_number), repasar como tratar datos en SQL de tipo: Array, Json y operaciones como explode, coalesce, etc

Material de estudio:

  • Hacer el curso de Data Lakehouse proporcionado por la misma página de Databricks academy (puedes darte de alta gratis como customer)
  • Hacer el learning plan de Data Analyst Associate de la Databricks academy
  • Revisar el GitHub de Databricks academy y hacer todos y cada uno de los ejercicios, allí también encontrarás las presentaciones.
  • Documentación de Databricks
  • Canal de Youtube de Databricks
  • En este caso la versión community no nos servirá para nada ya que el SQL workspace está presente únicamente en la versión de pago por lo cual para mitigar esta carencia intentando gastar lo mínimo hacer lo siguiente:
    • Estar atento a los workshop que ofrece continuamente Databricks tanto en Azure como AWS y así podrás contar con un cluster a todo dar durante 4-5 horas.
    • Aprovecha los free trial de 14 días tanto en AWS (marketplace) como en Azure.

Espero que este resumen les pueda ser de ayuda para aprobar el examen. Ahora es tu turno y cuéntanos cuál de todas las certificaciones de Databricks te llama la atención y una vez aprobada la Data Analyst Associate cuál será tu siguiente reto? La Data Analyst Professional, La Data Engineer Associate o la ML Practitioner.

Cómo aprobar la certificación Databricks Data Engineer Associate

Este año en cuanto ha certificaciones ha sido productivo me he logrado certificar en Terraform, Azure y algunas de Databricks que es de las que les quiero hablar hoy, mi receta para aprobar la Databricks Data Engineer Associate.

El examen

Consta de 45 preguntas de selección multiple a resolver en 90 minutos

El mínimo para aprobar es un 70% lo cual se corresponde con 32 preguntas respondidas correctamente

Objetivos a cubrir en el examen

Se deben dominar los siguientes tópicos:

  • Delta Lake
  • Relational entities on Databricks
  • ETL with Spark SQL
  • Just enough Python for Spark SQL
  • Incremental data processing with Structured Streaming and Auto Loader
  • Medallion architecture in the data lakehouse
  • Delta Live Tables
  • Task orchestration with Databricks Jobs
  • Databricks SQL
  • Databricks Repos
  • Managing permissions in the lakehouse(Unity Catalog)
  • Productionizing dashboards and queries on Databricks SQL

Siendo más específicos como detalla la página, la cualificación minima del candidato debería ser la siguiente

  • Understand how to use and the benefits of using the Databricks platform and its tools, including:
    • Platform (notebooks, clusters, Jobs, Databricks SQL, relational entities, Repos)
    • Apache Spark (PySpark, DataFrame API, basic architecture)
    • Delta Lake (SQL-based Delta APIs, basic architecture, core functions)
    • Databricks CLI (deploying notebook-based workflows)
    • Databricks REST API (configure and trigger production pipelines)
  • Build data processing pipelines using the Spark and Delta Lake APIs, including:
    • Building batch-processed ETL pipelines
    • Building incrementally processed ETL pipelines
    • Optimizing workloads
    • Deduplicating data
    • Using Change Data Capture (CDC) to propagate changes
  • Model data management solutions, including:
    • Lakehouse (bronze/silver/gold architecture, databases, tables, views, and the physical layout)
    • General data modeling concepts (keys, constraints, lookup tables, slowly changing dimensions)
  • Build production pipelines using best practices around security and governance, including:
    • Managing notebook and jobs permissions with ACLs
    • Creating row- and column-oriented dynamic views to control user/group access
    • Securely storing personally identifiable information (PII)
    • Securely delete data as requested according to GDPR & CCPA
  • Configure alerting and storage to monitor and log production jobs, including:
    • Setting up notifications
    • Configuring SparkListener
    • Recording logged metrics
    • Navigating and interpreting the Spark UI
    • Debugging errors
  • Follow best practices for managing, testing and deploying code, including:
    • Managing dependencies
    • Creating unit tests
    • Creating integration tests
    • Scheduling Jobs
    • Versioning code/notebooks
    • Orchestration Jobs

Mi opinión acerca del examen

El examen es de dificultad media y requiere conocimientos de:
Spark (batch & streaming), Delta tables, Delta Live Tables, Permisos, Jobs, pipelines y dominar el concepto de Data Lakehouse.

Las preguntas que me aparecieron en el examen fueron del tipo:

  • Donde puedo ver si tengo permisos para acceder a una tabla o donde y/o como puedo ver quien es el owner de la tabla para poder solicitarle permisos
  • Como conceder permisos en SQL a un grupo para acceder a una tabla
  • Ejemplo de Autoloader (Streaming de ficheros en cloud utilizado por Databricks)
  • Conocer bien arquitectura multi-hop para así saber responder en que forma están almacenados los datos en una tabla (bronze, silver o gold) así como también cual es el objetivo de cada una de estas
  • Para qué es el depends on de una tarea (task) en un pipeline
  • Cuando se utiliza el STREAM(live table) en SQL para leer de una tabla
  • Ejemplo de Left/Right join y cual sería el resultado final (Ten en cuenta que te pondrán 2 tablas y te preguntaran por el resultado del Join)
  • Que significa que se hayan borrado los datos y métodos de una tabla al hacer un DROP (que era una tabla managed), por ende conocer bien las diferencia y como se declaran las tablas managed y externas
  • Declaración de tablas externas a partir de datos CSV y/o JSON (por ejemplo CREATE TABLE .. USING CSV)
  • Entender cuando crear una Vista o una TEMP VIEW. Por ejemplo -> Si la «tabla temporal»(aunque todos sabemos que aquí no existe tal cosa de tabla temporal) no ha de ser utilizada por ningún otro usuario excepto tu, ya puedes intuir que una vista temporal (temporal view) será la opción y por ende descartas la vista (view)

Por último mencionar que en mi caso salieron varias preguntas acerca del SQL EndPoint y de la definición de pipelines.

Material de estudio:

  • Hacer el curso de Data Lakehouse proporcionado por la misma página de Databricks academy (puedes darte de alta gratis como customer)
  • Asumiendo que ya conoces Spark, Hacer la formación de Data Engineer Associate de la Databricks academy
  • Clonar o cargar los los notebooks desde el GitHub de Databricks academy y hacer todos y cada uno de los ejercicios, allí también encontrarás las presentaciones.
  • Documentación de Databricks
  • Si no posees una Cuenta Databricks community creala. No obstante, la versión community no te permitirá probar todos los notebooks y hacer todos los ejercicios ya que está capado, por ende no podrás probar AutoLoader, SQL Endpoint, Pipelines ni Delta Live Tables, no obstante como alternativa puedes hacer lo siguiente:
    • Estar atento a los workshop que ofrece continuamente Databricks tanto en Azure como AWS y así podrás contar con un cluster a todo dar durante 4-5 horas.
    • Aprovecha los free trial de 14 días tanto en AWS (marketplace) como en Azure

Espero que este resumen les pueda ser de ayuda para aprobar el examen. Ahora es tu turno y cuéntanos cuál de todas las certificaciones de Databricks te llama la atención y una vez aprobada la Data Engineer Associate cuál será tu siguiente reto? La Data Engineer Professional, La Data Analyst Associate o la ML Practitioner.

Cómo aprobar la certificación Databricks Spark 3.0

Finalmente ha llegado a mi correo mi certificación de Databricks Spark 3.0 tras haber aprobado dicho examen. El examen lo presenté el 14 de abril del 2021 y les voy a contar brevemente como fue el examen, de qué trata y qué hice para aprobarla.

El examen

Las características del examen son las siguientes:

  • Duración: 120 minutos.
  • Consta de 60 preguntas y apruebas con al menos 42 aprobadas (es decir, el 70%). Las cuales se distribuyen aproximadamente de la siguiente manera:
    • Spark Architecture: Conceptual understanding (~17%)
    • Spark Architecture: Applied understanding (~11%)
    • Spark DataFrame API Applications (~72%)
  • El examen es online y presentas desde por ejemplo tu casa, eso si, te estarán viendo por la cámara de tu ordenador, por lo cual no puedes ni debes abandonar tu sitio durante el examen, por lo cual asegúrate de hacer tus necesidades antes del examen.
  • No podrás tener accesorios a la mano como móviles, relojes, etc. Sencillamente cuentas con tu ordenador y en el examen te darán distintos PDF que abarcan todo el API.
  • Al comprar el examen seleccionas si presentarás el examen enfocada en la API Python o Scala de Spark.

OJO: No olvides días previos al examen entrar a la Web del examen y culminar el alta, que consiste en un software que reconoce tu rostro. NO LO DEJES PARA ÚLTIMO MOMENTO.

El examen consta de únicamente preguntas de selección múltiple, aunque eso si, no todas van de conceptos, las preguntas dada mi experiencia se componen de:

  • Evaluación de conceptos, por ejemplo: Cuales de las siguientes características no pertenece a las narrow transformations. NOTA: Cuidado con esas negaciones.
  • Dado un trozo de código seleccionar la opción que indique la razón del fallo.
  • Dado un trozo de código seleccionar la opción que representa el resultado final.
  • Dado un trozo de código incompleto seleccionar la opción que contiene esas partes de código que se usarían para completar el trozo de código para poder llevar a cabo una tarea de forma exitosa.

Las preguntas

Tal cual como lo indica la página dedicada a la certificación el contenido a evaluar es:

  • Arquitectura de Spark y el nuevo AQE (Adaptive Query Execution)
  • API Dataset:
    • Seleccionar, renombrar y manipular columnas.
    • filtrado, ordenamiento, eliminación y agregación de registros (filas).
    • Joins entre tablas, lectura, escritura y particionamiento en DataFrames.
    • UDFs y funciones Spark SQL.
  • Hay preguntas no solo del API Dataset sino también propiedades de configuración como por ejemplo spark.sql.shuffle.partitions
  • Las preguntas de completado de código eran como el siguiente ejemplo:

Donde las opciones a escoger serían:

  1. readDF, text, load
  2. readDF, ‘csv’, csv
  3. read, ‘csv’, load => Opción correcta
  4. read, csv, load

La plataforma te permite marcar preguntas para ser revisadas más adelante, cosa que yo aconsejo utilizar, hacer una pasada rápida por las preguntas, selecciona una respuesta y sobre aquellas que se tenga una mínima duda sencillamente se marca y le das una revisión, de esta forma tendrás oportunidad de responder todas las preguntas y revisar en 2 o hasta 3 ocasiones todas las preguntas que hayas dejado marcadas.

Finalizado el examen

Tan pronto finalizas el examen podrás observar por pantalla una valoración no definitiva del resultado de la prueba y minutos después recibirás un correo con la nota, donde ya te indican si aprobaste (o no, pero si sigues mi receta aprobarás) y luego pasados unos 7 días hábiles o quizás un poco menos recibes tu certificado por correo.

Receta para aprobar el examen

Ahora es tu turno, te animas a afrontar este reto, tienes alguna otra certificación en mente, estás indeciso en cual certificación de Spark tomar o si por el contrario ya presentaste está o alguna otra certificación cuéntanos qué tal te fue.

Primeros pasos con PySpark y PyCharm

Quienes me conocen saben que soy fan de IntelliJ, ya llevo unos cuantos años desde que dejé de usar eclipse y la verdad es que estoy encantado con la decisión que tomé, para mí es la mejor herramienta para desarrollo Java, Scala (y supongo que Kotlin).

Actualmente Spark está en mi día a día ya sea a modo desarrollo programando en Scala, razón por la cual uso continuamente IntelliJ sino también en la formación tanto en Scala como en Python, hasta hace poco para las formaciones de PySpark (entiéndase Spark con el API de Python) utilizaba los Jupyter notebooks (e incluso la plataforma de Databricks pero eso da para otra entrada en el blog) pero estaba la curiosidad que poco a poco se ha convertido en una necesidad de contar con una herramienta más potente que permitiese hacer debug, que integrase Git, hacer markdown, autocompletado de código, permita estandarizar el código, etc. Si lo meditan un poco casi todo de una forma u otra se puede alcanzar con los Jupyter notebooks, pero lo que cambia es la forma de programación, ya que con un IDE sería programación al uso (sea esta funcional o no), mientras que con los notebooks sería programación literaria, es decir, un entorno más enfocado a la explicación y documentación del código y hoy en día ampliamente utilizado por científicos de datos (data scientists), mientras que el primero (IDE) más utilizado por los ingenieros de datos (data engineer).

Finalmente nos hemos puesto manos a la obra en probar varios IDEs en el equipo y yo no quise desperdiciar la oportunidad de trastear con PyCharm y hacer mis primeras pruebas de programación de PySpark y es lo que comparto ahora con ustedes.

Antes que nada hay ciertos requisitos previos:

  1. Tener instalado Spark
  2. Tener instalado Python

Está vez no les voy a mostrar como instalar Spark, ya que hay muchísimas fuentes que nos explican como hacerlo pero en cualquier caso hay que tener claro que es necesario Java 8 (al menos) y declarar las variables de entorno SPARK_HOME y HADOOP_HOME, la primera apuntando a la ruta de la instalación de Spark y la segunda a la ruta base de donde se instalará el winutils.

El otro requisito es Python, hay distintas formas de instalar python que tampoco explicaré aquí, pero resumiendo puede ser instalando el lenguaje (y luego pip si se desea o es necesario instalar otra dependencia) o mediante anaconda, yo he elegido este último ya que es un entorno que trae consigo ya instalado jupyter notebooks además de otras herramientas, en cualquier caso si alguien tiene cierta curiosidad de cuando instalar pip o anaconda les dejo este articulo de stackoverflow que no tiene desperdicio.

Anaconda y sus aplicaciones

Ya entrando en materia, el primer paso es descargar e instalar PyCharm (este podría incluso llegar a instalarse desde Anaconda, yo preferí descargarla para contar con la versión mas reciente). Yo lo hice con la versión community

Una vez instalado y ejecutado por primera vez, crearemos un nuevo proyecto y deberemos especificar nuestro entorno (environment), yo soy de los que prefiere crear un entorno por proyecto debido a que cada proyecto python (en general) puede ser diferente en cuanto a dependencias características, etc. llegando incluso a diferir la versión de python entre 2 y 3. A su vez dejo marcada la opción de que genere un main.py tal cual como aparece en las imágenes.

El IDE nos genera un main.py que hace de esqueleto de la aplicación, lo editamos con un trozo de código que genera un DataFrame y mostrará parte de su contenido mediante la invocación del método show quedando de esta manera


# This is a sample Python script.
# Press ⌃R to execute it or replace it with your code.
# Press Double ⇧ to search everywhere for classes, files, tool windows, actions, and settings.
def print_hi(name):
# Use a breakpoint in the code line below to debug your script.
print(f'Hi, {name}')  # Press ⌘F8 to toggle the breakpoint.
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
print_hi('PyCharm')
try:
#import findspark;
#findspark.init()
from pyspark import SparkContext, SparkConf
except ImportError:
raise ImportError("Unable to find pyspark -- are you sure SPARK_HOME is set?")
import random
from pyspark.sql import SparkSession
spark = SparkSession \
.builder \
.appName("PyCharm Example") \
.getOrCreate()
mi_rango = spark.range(1000).toDF("numero")
mi_rango.show()
# See PyCharm help at https://www.jetbrains.com/help/pycharm/
Si este código tratamos de ejecutarlo (Menu Run->Run main), este falla. Esto se debe a que es necesario incluir las dependencias propias de Spark, por lo cual es necesario incluir la carpeta python de la instalación de Spark (sería $SPARK_HOME/python) y además la carpeta py4j ubicada en $SPARK_HOME/python/lib/py4j-xxx-.zip

El añadir py4j es una de las alternativas que existen para poder hacer ejecuciones en local del código. Otra alternativa es instalar py4j como dependencia del entorno (environment) y de hecho la misma herramienta te ofrece la alternativa como se refleja en la imagen a continuación

Ahora podemos darnos cuenta que ya no se resaltan (en rojo) parte del código. Procedemos de nuevo a ejecutar el código y nos damos cuenta que este se ejecuta exitosamente.

Ya estamos a punto de terminar pero falta un pequeño detalle, el IDE todavía nos marca unos warnings, por ejemplo el código comentado y esto se debe a que por defecto PyCharm ya aplica PEP8 como estándar al código y por ende todo aquello que no cumpla con el estándar definido será resaltado para su corrección como se muestra en la imagen.

Incluso si hacemos clic en el símbolo de warning (cuidado) ubicado en la parte superior derecha, nos listará las cosas a mejorar que cumplan con el estándar. Una vez listado los warnings procedo mejorar el código quedando de la siguiente manera

Finalmente el código (aunque es muy simple) ya cumple con el estándar PEP8 y nos ha resultado relativamente sencillo poder ejecutar nuestro código desde el mismo IDE, además la herramienta nos permite sin salir de ella acceder a linea de comandos, hacer control total de Git (pull, push, commit, comparación entre ramas y más), poner breakpoints y realizar debug del código. Otra cosa a comentar es que aun cuando no hayamos definido el SPARK_HOME (cosa que no recomiendo) y el HADOOP_HOME, estas variables podemos definirlas antes de ejecutar el código mediante Edit Configurations.

Ha sido un ejemplo muy simple pero creo que refleja parte del potencial de la herramienta, aunque no todos son buenas noticias, por ejemplo la versión community no permite abrir y ejecutar jupyter notebooks cosa que si permite la versión de pago, llegando incluso a permitir la ejecución celda a celda y esta es una característica muy deseada que algunas herramientas si lo permiten como es el caso de VSCode, sin embargo esto no empaña para nada las capacidades que tiene y puede aportarnos de cara a la productividad.

Uso del SHOW PARTITIONS en Spark

Seguro que en alguna ocasión nos ha tocado hacer un SHOW PARTITIONS de una tabla HIVE particionada, con la finalidad (para quien no lo sepa) de obtener/visualizar las particiones existentes de los datos. Hasta aquí nada nuevo, pero lo que quiero mostrarles en esta oportunidad es un método (nada espectacular) que me ha servido mucho donde obtengo las particiones de una tabla (Hive) y los usos que le he dado al método, entre otras con grandes ventajas en performance.

Como seguramente muchos de ustedes saben si invocamos un SHOW PARTITIONS en spark por ejemplo en la spark-shell, esta nos devuelve un DataFrame con una única columna, como el siguiente ejemplo:

Con mi método lo que hago es transformar el DataFrame original en uno formateado donde cada variable de particionado es una columna. A continuación el método y el que sería el DataFrame resultante

Ahora ustedes se preguntarán y que hay de fascinante o interesante en este método. La verdad que el método en sí aporta poco es sencillamente una simple transformación pero para mí la magia reside en para que lo utilizo y es lo que les quiero contar.

Imaginen que la tabla que posee las 5 variables de partición (ni discutamos si es acertado poseer 5 variables de particionamiento) posee un sin fin de particiones y que a su vez para una misma ciudad en una misma fecha hay varias particiones por hora (como aparece en el ejemplo para la ciudad de Valencia) y cada partición a su vez tiene muchos registros. Con este supuesto si quisiéramos hacer una consulta para obtener la máxima partición (la más reciente) para una fecha, ciudad, estado y país en especifico podríamos llegar a tener problemas de TimeOut o SocketTimeOut ya que:
* El cluster se vería exigido intentando trabajar sobre las particiones pertinentes (debido al gran número de particiones).
* Una vez obtenidas las particiones cargar los datos desde HDFS y recorrer de forma innecesaria un conjunto de datos requiriendo mucho más memoria de la necesaria.

¿Recorrer de forma innecesaria?
Si, ya que recorreríamos un conjunto de registros donde muchos de esto compartirán el valor de la columna «hora» (partición) y apegándonoslos al ejemplo de arriba (la ciudad de Valencia) realmente los valores posibles serían 2:
* 1700
* 1750

Solución: Pues al obtener el DataFrame de particiones, si posteriormente filtramos por país, estado, ciudad y día solo nos quedarían 2 registros para el campo hora y sencillamente tendría que hallar el máximo valor de 2 registros en vez de tener que cargar datos de HDFS e iterar sobre todos estos.

¿Mucha más memoria de la necesaria?
Si, ya que al hacer un SHOW PARTITIONS, nosotros interactuamos con el metatstore y los metadatos en vez de trabajar con todos los datos de HDFS con todo lo que eso implica en cuanto a latencia, debido a la necesidad de ir a disco, etc.

¿Existe alguna otra ventaja de trabajar con el metastore?
Si, por ejemplo para hallar la máxima partición, trabajando únicamente con el DataFrame de particiones y una vez hallada la partición idónea, digamos que la más reciente, entonces construyo la consulta (muy especifica) indicando los valores de la partición deseada evitando esos errores de TimeOut haciendo uso eficiente de los recursos. De hecho yo lo que hecho es construir un WHERE dinámico (quizás lo comparto en la próxima entrada) a partir del DataFrame de particiones filtrado.

¿Se te ocurre otra ventaja de utilizar un método como este e interactuar con el metastore? ¿Habías hecho algo similar para tener mejoras de rendimiento y uso eficiente de recursos?

Espero que les sea de utilidad.

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

ACEPTAR
Aviso de cookies