jueves, 21 de octubre de 2021

Configurar Visual Studio Code para trabajar con C++ y G++


Introducción 

El objetivo de este post es de mantener una memoria práctica para compilar y depuerar en VSCode. De esta manera se eliminará la dependencia de un XCode o Visual Studio y apegarse a los estandares de g++, clang u otro compilador, sin importar el sistema operativo, siempre y cuando este pueda ejecutar el Visual Studio Code.


Entornos

Los entonrnos que se configurarón para la realización de este documentos son:

 Linux: 

  • OS: Manjaro 5.13.19-2
  • Visual Studio Code V 1.60.2
  • Compilador g++ (GCC) 11.1.0
  • Debuger: gdb

OSX:

  • OS: macOS Big Sur 11.5.2
  • Visual Studio Code V1.61.1
  • Compilador: Apple clang version 11.0.0 (clang-1100.0.33.17
  • Debuger: lldb-1100.0.30.12


Visual Studio Code:

  • C++ Extension Pack V1.0


Información Previa

Se requiere tener en cuenta que VSCode, para los poryecto de C/C++, se pueden utilizar varios extensiones que complementar la funcionalidad del VSCode. en mi caso he decidido trabajar con la extensión de microsoft C++ Extension Pack V1.0, la cual proporciona todos los elementos para codificar y depurar. 



Compilar on GNU g++

Antes de configurar el VSCode, es necesario tener en cuenta los comandos que requiere en este caso el g++. 

En mi caso, ocupo este paso para compilar la solición a modo de poder depurarlo "Debuging". Para construir una solucuión completa prefiero utilizar un archivo Makefile


$g++ -g [Flags] [Files] -o [Out File]


En este caso, las bandera más importante son:

  • -g Lo cual agrega la información requerida para realizar un depurado del compilado.
  • -o Nombre y ruta dónde se depositará el compilado.
Las secciones relacionadas a las banderas ([Flags]) son las banderas y sus parámentros que requiere la bandera, como puede ser la inclusión de una ruta en include. La sección de archivos ([Files]) corresponde a los archivos que se utilizarán para compilar, por ejemplo:

$g++ -g -L[Libs Path] -l[Lib Name] -I [Include Paths] main.cpp file2.cpp -o [Out file]

Con éste previo, se procederá a configurar las tareas que se utilizaran en VSCode para compilar y ejecutar


Tareas en VSCode

En cada proyecto de VSCode, se pueden configurar tareas de compilación, las cuales se verán reflejadas en el archivo .vscode/task.json.



Este archivo contendrá todas las tareas, tareas que serán ejecutadas en modo de consola de comandos. Para generar este archivo, siga estos pasos: 


Oprima F1.

Ecriba Tasks, y seleccione la opción Configure Task: 


Oprima enter y siga selecciones Crear archivo task.json desde plantilla:


Selecciones la opción Others, en este caso es mi preferido:


El archivo tasks.json se generará:



Parámetros

En este caso, compilación en C++, siga la siguiente plantilla:

{
"version": "2.0.0",
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++ compilar archivo activo",
"command": "g++",
"args": [],
"group": "build",
"detail": "compilador: /usr/bin/g++"
        }
}


  • type: Tipo de tarea que se llevará a cabo. Para este caso se especificará el tipo de cppbuild.
  • label: Etiqueta con el que se identificará esta tarea.
  • command. Es el comando que se ejecutará. Ubiquelo en una consola de comandos. Si no esta dado de alta la ruta en el path, se deberá especifiar la ruta del commando, p.e: /usr/bin/g++.
  • args. Lista dearguments que requiere el comando. Cada elemento de los argumentos estan delimitados por un espacio en blanco, es decir, si se tiene el comando:

$g++ -g -I ./myPath -o  ./myPath/bin/out

"args": [
        "-g",
        "-I",
        "./myPath",
        "-o",        
        "./myPath/bin/out"
],


Para concer más detalles de los parámetros de configuración del archivo task.json, vea el link Integrate with external tools via Tasks:




Caso Básico

Compilación / Construcción

Este caso implica conocer los conceptos básicos de compilación.



Crear una tarea de compilación Shift + Ctrl + B, le solicitará crear una el archivio task.json:

{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++ compilar archivo activo",
"command": "/usr/bin/g++",
"args": [
"-g",
"-std=c++17",
"${file}",
"-o",
"${fileDirname}/../bin/sample.out"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$g++"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Tarea generada por el depurador."
}
],
"version": "2.0.0"
}

  • command indica el compilador con el que se realizará la tarea, en este caso la de construir el programa.
  • args. contiene los parámetros de compilación que, en este caso, g++ utilizará, dónde
    • -std=c++17 Soporte para estándar C++17, puede indicarse el estándar 11 o 14.
    • -o indica la salida del compilador con un nombre especifico, establecido en el siguiente argumento, en este caso se refiere a sample.out.


Nota: ${fileDirname} es la macro para especificar el directorio sobre el archivo main.cpp, sobre el cual se debe ejecutar el comando de construcción Shift + Ctrl + B y seleccionando la tarea de compilación C/C++ g++ compiler archivo activo.





Iniciar la Depuración

{
// Use IntelliSense para saber los atributos posibles.
// Mantenga el puntero para ver las descripciones de los existentes atributos.
// Para más información, visite: https://go.microsoft.com/fwlink/?linkid=830387
//https://code.visualstudio.com/docs/cpp/launch-json-reference
"version": "0.2.0",
"configurations": [
{
"name": "(DEBUG) Iniciar",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/../bin/sample.out",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "lldb"
}
]
}


Dónde los parámetros más importantes son:

  • type. El cual indica que una tarea de depuración de c++ gdb.
  • programm. Indica el compilado a ejecutar.
  • args: indica los argumentos con los que se ejecutará el programa a depurar.
  • externalConsole. Indica si se utilizará la consola de VSCode o la consola ecterna.
  • MIMode. Indica el depurador que utilizará el VSCode, hay dos tipos: gdb o lldb, este último para mac. 


Depuración con multiples archivos


Construcción

A diferencia del caso básico, aqui se debe definir en los argumentos de la compilación los archivos que se deben considerar para la compilación, como se haría en una el caso de compilarlo en línea de comando:

{
"version": "2.0.0",
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++ compilar de Proyecto",
"command": "/usr/bin/g++",
"args": [
"-g",
"-std=c++17",
"${fileDirname}/main.cpp",
"${fileDirname}/myclass.cpp",
"-o",
"${fileDirname}/../bin/sample.out"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": "build",
"detail": "compilador: /usr/bin/g++"
}
]
}


Conclusión

En resume, la compilación del programa se debe especificar como una nueva tarea y tener en cuenta que se debe especificar el compilador y sus argumentos de compilación, como se haría en línea de comandos.

En lo que respecta a la depuración, el archivo launch.json contiene la configuración requerida para iniciar un progrma, utilizando el depurador gdb en linux o lldb en OSX.


Compilación en g++

Post dirigido a mantener una memoria sobre cómo compilar programas con el g++; información que será retomada en VSCode para construir los programas hechos en C++.

Básico


$g++ -g -std=c++17 <cpp files> -o <target>

Dónde:

  • -g Indica que se estará generando la información para depuración (debugging) dentro del compilado. Esta bandera se recomienda utilizar para generar los compilados de Debug, pero se debería eliminar cuando se tenga el compilado de Release.
  • -std=c++17 Es la opción del compilador g++ para indicar que se trabajará con el estandar de C++17. En mi caso trabajo a partir del estanar 11 (c++11) y se puede utilizar el estandar 14 (C++14).
  • <cpp files> Corresponde a los archivos cpp que estarán compilando para construir el ejecutable. Si son varios archivos estos deben separados por espacio, p.e.:
  • -o <target> Indica la ruta y nombre del archivo ejectuable, resultado de la compilación.

Ejemplo:

$g++ -g -std=c++17 main.cpp myclass.cpp mylogs.cpp -o ../bin/sample.out

Se generará el archivo sample.out subro la carpeta bin, ubicada un nivel arriba de dónde se ejecuta la línea de comando, utilizando estadar C++17, utilizando tres archivos cpp y agregando la informción de depuración en el compilado final.

Importante: Este modo de construcción Compila y Enlaza los archivos, dejando el resultado en la ruta u archivo final especificado en la opción -o.


En OSX utiliza clang, compatible con g++, por lo que si se revisa la versión de g++ en OSX este aparecerá como clang:

$ g++ --version

Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1

Apple clang version 11.0.0 (clang-1100.0.33.17)

Target: x86_64-apple-darwin20.6.0

Thread model: posix

InstalledDir: /Library/Developer/CommandLineTools/usr/bin

Mientras que en Linux si se implementa el compilador g++:

$ g++ --version

g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

Copyright (C) 2019 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.  There is NO

warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Ambos compiladores, clang u gcc, son compatibles. Para mas información sobre CLang y GCC, vease aquí




 

jueves, 7 de mayo de 2020

WebSocket Server API C#

En ocasiones se llega a presentar la necesidad, como programador, de enlazar un sitio web con algún servicio ubicado en la computadora (localhost), incluso poder mandar mensajes de manera bidireccional entre ambas partes, y sin contar que debe correr en cualquier sistema operativo que soporte Windows .Net 4.5, incluyendo Windows 7.

Debido a que me he topado que no puedo implementar la implementación de WwebSocket Server, que viene incluido en Microsoft 4.5, debido a que el sistema operativo es Windows 7, por que solo corre a partir de Windows 10, me he dato a la tarea de crear una libreria que permita implementar una conexión Web Socket insegura (ws://) o segura (wss://), la cual comparto por medio del link a su repositorio de GitHub.

Esta libreria puede correr  en cualquier sistema operativo que soporte:


  • Microsoft  Framework .Net 4.5 (Para Windows)
  • Microsfot Net Core 2.1 (Para Mac).


La librería fue creada con Visual Studio 2017 Community For Mac.


https://github.com/jorgemedra/WebSocketAPI

viernes, 14 de abril de 2017

Creación de Cluster de Linux – CentOS 7

Introducción

Este post tiene la finalidad de mostrar como crear un Cluster con dos servidores de Linux, utilzando el sistema operativo CentOS y Pacemaker.
  • El Cluster trabajará de la siguiente manera:
  • Cada servidor de Linux representa un Nodo en el cluster.
  • Los nodos del Cluster compartirán una IPVirtual, la cual es el primer recurso que se configura en el Cluster.
  • El nodo Activo será el primer servidor que se logre levantar.
  • Si el Nodo que tiene asignada la IPVirtual deja de responder entonces el otro Nodo tomara posesión de la IPVitual.
  • Solo se pude intercambiar la IPVirtual si el Nodo que tiene la posesión deja de responder, ya sea haya sido desactivando, apagando o por medio de una falla de red en el Nodo.

Instalación y Configuración de los Servidores

Este ejemplo utiliza es ejemplificado por medio de maquinas virtuales, utilizando en VMWare Fusion. Las características de los servidores y datos de las cuentas que serán utilizadas son:

  • Versión de Fedora: Centos 7 (x86_64 Bits)
  • Maquina virtual con:
  1. 2 cores
  2. 10 GB de DD
  3. 1.2 GB de RAM

Usuario de administración root
  • pwd: *********

Usuario de Middleware, con privilegios de administrador.
  • user: srvmdlw
  • pwd: **********

En este caso de nombra a la entidad Middleware, pensando que el Cluster será una capa intermedia para acceder a diversos servicios.
Nodo1
  • Hostname: mddlwr.n1
  • IP PUB: 192.168.13.231
  • MAC ADDRESS1: 00:50:56:31:5B:AA

Nodo 2
  • Hostname: mddlwr.n2
  • IP PUB: 192.168.13.232
  • MAC ADDRESS1: 00:50:56:3E:FA:54

Nodo Middleware, quien representa al Cluster.
  • IP Virtual: 192.168.13.230
  • User: hacluster
  • PWD: *********
  • ClusterName: mddlwr.cluste

Instalación y Configuración de Linux CENTOS 7

Los paquetes que se deben instalar en cada uno e las instalaciones de Linux.

Asignación de IP’s

Utilizar los datos de acuerdo al nodo del Middleware que se esté instalando (Nodo 1 o Nodo 2), descritos en la sección Datos de Instalación.
IP Pública
IP Privada

Instalación del Cluster

El comando para instalar los paquetes de High Availability debe ser realizado como usuario ROOT, ademas de tener los servidores conectados a Internet.
Instalar el paquete de Corosync
# yum -y install corosync pacemaker pcs
Si ya se tiene instalada la última actualización, se obtendrá el siguiente resultado:

Abrir puertos de Firewall

Abrir puertos UDP 5404 y 5405, para Corosync:
# iptables -I INPUT -m state --state NEW -p udp -m multiport --dports 5404,5405 -j ACCEPT
Abrir TCP 2224, para PCS:
# firewall-cmd --permanent --add-port=2224/tcp
Permitir el tráfico IGMP:
# iptables -I INPUT -p igmp -j ACCEPT
Permitir el tráfico MULTICAST:
# iptables -I INPUT -m addrtype --dst-type MULTICAST -j ACCEPT
Antes de guardar los cambios, asegúrese de actualizar el servicio de iptable.
# yum install iptables-services
Guardar los cambios hechos:
# service iptables save
iptables: Saving firewall rules to /etc/sysconfig/iptables:[ OK ]
Para revisar las reglas de Firewall, utilice el siguiente comando:
# iptables -L -n

Preparando tabla de Hostnames

Para todos los nodos, se encontrarán por medio del host name y la IP privada, por lo que la edición debe ser en el archivo de Hosts, en ambos nodos.

Cambiar password del usuario hacluster

Ejecutar el comando en ambos nodos.
El usuario ya debería estar creado, el uso de este comando es para confirmar que está creado:
# useradd hacluster
Asignación de password
#passwd hacluster
Los datos del password están descritos en la sección Datos de Instalación.

Iniciar servicio PCS

Ejecutar el comando en ambos nodos.
# systemctl enable pcsd.service
# systemctl start pcsd.service

# systemctl enable corosync.service
# systemctl start corosync.service

# systemctl enable pacemaker.service
# systemctl start pacemaker.service

Autorizando los servidores

Ejecutar el comando en ambos nodos.
# pcs cluster auth mddlwr.n1 mddlwr.n2
El comando le solicitará identificarse como el usuario hacluster.  El resultado debe indicarse que se logró identificar en ambos nodos.

Creación de cluster con sus nodos

Ejecutar este comando en el nodo 1.
# pcs cluster setup --name mddlwr.cluster mddlwr.n1 mddlwr.n2
Este comando creará el archivo /etc/corosync.conf, el cual contendrá la configuración de clúster.
Después de la creación se puede iniciar el clúster, aunque todavía falte configurarlo. Para iniciarlo se utiliza el comando:
# pcs cluster start --all
Este comando iniciará los nodos del cluster:

Revisando el estado del Clúster

Para revisar el estado del Clúster se debe utilizar el siguiente comando:
# pcs status cluster
En este caso todavía no se tiene configurado algún recurso:
2 nodes and 0 resources configured
Online: [ mddlwr.n1 mddlwr.n2 ]
Para revisar el estado de los nodos se utiliza el siguiente comando:
# pcs status nodes
Para revisar el estado de los miembros del cluster:
# corosync-cmapctl | grep members
Para obtener información del grupo:
# pcs status corosync
Para obtener información completa del cluster y sus servicios:
# pcs status

Configuración del Cluster

Se debe utilizar el usuario root para ejecutar lo comandos de esta sección y deben ser ejecutados en ambos nodos, caso de este ejemplo.
Antes de iniciar se revisa si se tiene errores configuración en el cluster, por lo que se utiliza el comando:
# crm_verify -L -V
El cual nos muestra una lista de errores, en donde se indica que no tiene configurado la propiedad STONITH, el cual es un mecanismos que asegura que no se tengan dos nodos activos, lo que provocaría que se estaría compitiendo por la IP compartida.
En este caso se tiene una configuración sencilla de cluster, por lo que deshabilitamos esta propiedad con el siguiente comando:
# pcs property set stonith-enabled=false
Sobre la configuración del cluster, se tiene una propiedad que indica el quórum mínimo requerido para que el Cluster esté operando. Esta propiedad es útil en el caso de que se tengan varios nodos; pero en el caso de nuestro cluster solo se cuenta con dos nodos, por lo que es recomendable que se desactive esta propiedad, pues la cantidad mínima requerida de nodos levantados es de un (1) nodo.
El comando para ignorar el umbral de nodos mínimos es:
# pcs property set no-quorum-policy=ignore
Para revisar los cambios hechos, STONITH y mínimo quórum, utilice el siguiente comando:
# pcs property

Asignación de IP Virtual

Una vez configurado los aspectos previos del Cluster, se debe asignar l IP que compartirán en común los nodos.
En este caso, del cluster, se considera que la creación de la IP Virtual como un nuevo recurso, por lo que se crea un nuevo recurso con un tiempo de 15 segundos de poleo. El tiempo de poleo se ajusta de acuerdo a sus necesidades.
# pcs resource create virtual_ip ocf:heartbeat:IPaddr2 ip=192.168.13.230 cidr_netmask=32 op monitor interval=15s
En este ejemplo se ejecuto el comando desde el nodo 1, creando el recurso en ambos nodos.
Para revisar la configuración de los recursos se utiliza el siguiente comando:
# pcs status resources
Ahora la IP Virtual ya puede ser alcanzada por otro dispositivo dentro de la red:

Activando / Suspendiendo un Nodo del Cluster

Como procedimiento opcional, se puede desactivar o activar nodos con los siguientes comandos:
Desactivar un Nodo, en este ejemplo es el nodo 2.
# pcs cluster unstandby mddlwr.n2
Activar un Nodo, en este ejemplo es el nodo 2.
# pcs cluster standby mddlwr.n2
De esta manera se puede desactivar un nodo para que el otro se quede activo con la IP Virtual, dato que se puede consultar al consultar el estatus del cluster:

Implementando HTTP Server de Apache como un recurso del Cluster

Firmado  con super usuario ROOT
En este caso implementaremos el servicio de Apache HTTP para ser parte de los recursos del cluster, de tal manera que si se llegase a detener el servicio httpd, se active el siguiente nodo.
Instalación del software de Apache:
# yum install httpd
Abriendo en el Firiwall el Puerto 80:
# firewall-cmd --permanent --add-port=80/tcp
# firewall-cmd --reload
Habilitar servicio
# systemctl enable httpd
Detener el servicio, si se encuentra ejecutándose
# systemctl stop httpd
Creamos el mecanismo que permitirá al cluster monitorear el http server. Este mecanismo consiste en un archivo de configuración, llamada serverstatus.conf, ubicado en la ruta /etc/httpd/conf.d, con el siguiente contenido:
Listen 127.0.0.1:80

SetHandler server-status
Order deny,allow
Deny from all
Allow from 127.0.0.1

Este archivo debe estar contenido en ambos nodos, o los que formen parte del cluster.
Ahora se debe comentar, del archivo de configuración del http server, la configuración correspondiente al Listener, para evitar que se trate de escuchas en múltiple ocasiones sobre el mismo puerto. Esta paso se lleva a cabo con el sigueinte comando:
# sed -i ‘s/Listen/#Listen/’ /etc/httpd/conf/httpd.conf
Antes del comando:
Después del comando:
Iniciamos el servidor http, en ambos nodos:
# systemctl start httpd
Debido a que se tiene desactivado el Listener, no será posible realizar peticiones al servidor http. La manera de ver que este trabajando el http server es po comando, realizando una petición al localhost, solicitando el estado del servidor, mecanismo que se implemento con el archivo serverstatus.conf:
# wget http://127.0.0.1/server-status
Esta validación se debe llevar a cabo en ambos nodos.
El siguiente paso es el de crear en el http server un Listener hacia el puerto 80 y utilizando la IP Virtual. Para esto se modificará el archivo de configuración del http server, en ambos nodos, utilizando los siguientes comandos, en ambos nodos:
# echo "Listen 192.168.13.230:80"|tee --append /etc/httpd/conf/httpd.conf
Antes del comando:
Después del comando:
Reiniciar el servicio de http server. Una vez reiniciado el servicio será posible realizar peticiones desde equipos externos.
Una vez que se tiene este mecanismo/interfaz, el cluster tendrá la capacidad de monitorear y controlar el servidor http. Por lo que el siguiente paso es incluir el servidor http como un recurso más del cluster, utilizando el siguiente comando en cualquiera de los nodos disponibles del cluster:
# pcs resource create webserver ocf:heartbeat:apache configfile=/etc/httpd/conf/httpd.conf 
statusurl="http://localhost/server-status" op monitor interval=25s
Antes de ejecutar el comando se puede consultar el estado del cluster y apreciar que se encuentra configurado un solo recurso:
Después de la ejecución se pueden apreciar el nuevo recurso:
En este punto el cluster solo puede asignarle la IP a un solo nodo, por lo que al tratar de levantar el http server al nodo no activo se tendrá el error de que no se pudo asociar el puerto a la IP establecida en la configuración. Esta situación se debe arreglar configurando una restricción en el cluster, utilizando el siguiente comando:
# pcs constraint colocation add webserver virtual_ip INFINITY
Este comando indica que los recursos dependen de la IP Virtual, pero ademas se requiere evitar que el webserver inicie antes que se asigne una IP Virtual. Para esto se debe crear otra restricción, la cual indica cuál debe ser el orden de arranque de los recursos:
# pcs constraint order virtual_ip then webserver
Para revisar los recursos configurados hasta el momento, utilice el comando:
# pcs resource
Para revisar las restricciones configuradas hasta el momento, utilice el comando:
# pcs constraint
Pare que inicie por completo el cluster:
Revise el estado del cluster en ambos nodos:
Nodo 1:
Nodo 2:
En ambos nodos se aprecia que el Cluster está operando exitosamente, así como también se aprecia que los recursos están trabajando en el nodo 1:

Instalando Tomcat

Versión de Tomcat: 9.1
Ruta de instalación de Tomcat:
/home/srvmdlw/tomcat
Ruta de instalación de JRE:
/lib/jvm/jre
Abrir puertos 8080:
 firewall-cmd --permanent --add-port=8080/tcp
 firewall-cmd --reload
Crear recurso de Tomcat en Pacemaker:
# pcs resource create tomcat ocf:heartbeat:tomcat params java_home=/lib/jvm/jre catalina_home=/home/srvmdlw/tomcat op monitor interval=10s
La restricción para que arranque después de la IP Virtual:
# pcs constraint colocation add webserver virtual_ip INFINITY
# pcs constraint order virtual_ip then webserver

# pcs constraint colocation add tomcat virtual_ip INFINITY
# pcs constraint order webserver then tomcat
Para conocer el Id de los Constrains:
# pcs constraint list --full
Para remover la restricción anterior:
# pcs constraint order remove virtual_ip webserver
Asignar el nuevo orden de arranque:
# pcs constraint order virtual_ip then webserver
# pcs constraint order webserver then tomcat

Procesos para evaluar el funcionamiento del Cluster

Recomiendo utilizar los siguientes comandos para revisar el funcionamiento del Cluster:
Estado General:
#pcs status
Estado del Cluster:
#pcs status cluster
Estado de los Recursos:
#pcs status resource
Los logs del CoroSync se encuentran en la siguiente ruta:
/var/log/cluster/corosync.log

Referencias

Guía para crear un Cluster sencillo:
Monitoreo de Apache:
Manual de Pacemaker:
Configuración de Tomcat para PCS